Compare commits
450 Commits
v2.0.0
...
bug/withDe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
407dad8c7f | ||
|
|
9f944506e0 | ||
|
|
114c37645a | ||
|
|
8f9bb621b8 | ||
|
|
48a6c87d9d | ||
|
|
f28b55dd4d | ||
|
|
8a8b4b0245 | ||
|
|
0afbc57012 | ||
|
|
c651f2440f | ||
|
|
8c05084994 | ||
|
|
6813be47f0 | ||
|
|
30327e8d27 | ||
|
|
8096ed300d | ||
|
|
4a8efbf426 | ||
|
|
7a6d117a76 | ||
|
|
73568043f1 | ||
|
|
76a3b35e9e | ||
|
|
feb1991da3 | ||
|
|
2bee4342b8 | ||
|
|
7e9fc486f2 | ||
|
|
1d99b79926 | ||
|
|
eb8347f942 | ||
|
|
d383b48916 | ||
|
|
e88c40db0a | ||
|
|
f228f37bb8 | ||
|
|
503a54fc5e | ||
|
|
48b21aa0bf | ||
|
|
ca40cc5271 | ||
|
|
1c641c6d11 | ||
|
|
32b362f9cc | ||
|
|
103ef8ee0d | ||
|
|
4a6239e017 | ||
|
|
1f637edd36 | ||
|
|
25878b9bb1 | ||
|
|
521a96bffb | ||
|
|
ead1f3954f | ||
|
|
0ac84b58e3 | ||
|
|
a2f1e37ad2 | ||
|
|
373343fea1 | ||
|
|
2ef99026e5 | ||
|
|
29aff9accc | ||
|
|
7d7f628f6e | ||
|
|
8f6cf07e82 | ||
|
|
245b8a6e3c | ||
|
|
a967100be8 | ||
|
|
a9bca8e1f8 | ||
|
|
7de8e6be5e | ||
|
|
6b70a39f02 | ||
|
|
d538d722d7 | ||
|
|
13bd831c5f | ||
|
|
9b297ba882 | ||
|
|
be6c802745 | ||
|
|
564cce2462 | ||
|
|
8f166b8b3f | ||
|
|
fe7192ae61 | ||
|
|
6d54f21c1e | ||
|
|
87f8f61163 | ||
|
|
510ba376e5 | ||
|
|
40c88b3e35 | ||
|
|
7c65da4cf3 | ||
|
|
0f07c47e9f | ||
|
|
17c45fee11 | ||
|
|
a63c0817cc | ||
|
|
68aa54bdb7 | ||
|
|
41cb6eb190 | ||
|
|
61e5a48b02 | ||
|
|
9e4d7df7d0 | ||
|
|
e83dbc2e5c | ||
|
|
03ab6a208d | ||
|
|
dbd39ba0d8 | ||
|
|
136b1ff63b | ||
|
|
69a6207a4d | ||
|
|
9e74a8c2e7 | ||
|
|
ad76d100ee | ||
|
|
235968073a | ||
|
|
ad7b8da37e | ||
|
|
7d3e1a700f | ||
|
|
d3a1898dad | ||
|
|
45e508fc36 | ||
|
|
8edad7ded7 | ||
|
|
75e1adb7b3 | ||
|
|
22ac13f2f0 | ||
|
|
7f0246eb47 | ||
|
|
5f0800760f | ||
|
|
6db99c9e37 | ||
|
|
3b2cabd3f3 | ||
|
|
b0dd6b0bd6 | ||
|
|
874b846e60 | ||
|
|
dbe2525c6f | ||
|
|
afd414fa3f | ||
|
|
94763dcb31 | ||
|
|
6314740f46 | ||
|
|
d7332120e3 | ||
|
|
8c74fe9925 | ||
|
|
c74ddeb530 | ||
|
|
e31c0a9d02 | ||
|
|
d9e5d4aec5 | ||
|
|
c1ee8f5dd0 | ||
|
|
dd59de3de0 | ||
|
|
511a3c55f3 | ||
|
|
c9021ab3ca | ||
|
|
a765c4a7cc | ||
|
|
ea99732474 | ||
|
|
6c64ffe833 | ||
|
|
5fa6c6cdb3 | ||
|
|
d94759870e | ||
|
|
f0a6fc641a | ||
|
|
7ba00bee0b | ||
|
|
dc2bdf81b9 | ||
|
|
187a30abac | ||
|
|
5b824ccb17 | ||
|
|
3bdf2baf97 | ||
|
|
9af8a24a89 | ||
|
|
57c4759bdb | ||
|
|
d9d7261bc5 | ||
|
|
a12315d81a | ||
|
|
9f0956556f | ||
|
|
748318d44e | ||
|
|
ff3062cdfc | ||
|
|
48d67fe7e1 | ||
|
|
2c9918f9a7 | ||
|
|
ee03952201 | ||
|
|
43dcd3c443 | ||
|
|
8f8c42a92a | ||
|
|
6496aded25 | ||
|
|
eacf8113af | ||
|
|
c4a1527153 | ||
|
|
4a89a6aafc | ||
|
|
52539b084d | ||
|
|
8d5bd051a1 | ||
|
|
3809e9853e | ||
|
|
5f795acd61 | ||
|
|
17c550404f | ||
|
|
a840079119 | ||
|
|
2761894164 | ||
|
|
6b8bc618dc | ||
|
|
258f79604f | ||
|
|
81ae70ee04 | ||
|
|
6b02d290a5 | ||
|
|
7ab1bbaf62 | ||
|
|
079083d0f2 | ||
|
|
0504707aab | ||
|
|
fb4aab875d | ||
|
|
7bb32ecf7e | ||
|
|
e129a5c179 | ||
|
|
8045f26c19 | ||
|
|
86516421b5 | ||
|
|
bce88ccd44 | ||
|
|
66d408b7db | ||
|
|
297bf3205f | ||
|
|
7366b32349 | ||
|
|
b7e0169c9b | ||
|
|
6b6f85cc7e | ||
|
|
2c014a2f4b | ||
|
|
d6df675821 | ||
|
|
427baf4c79 | ||
|
|
4f2b682341 | ||
|
|
e03f888cb2 | ||
|
|
513396d498 | ||
|
|
8f04f0758b | ||
|
|
7a77bfc248 | ||
|
|
ddd29374ea | ||
|
|
4b0d7a6c3d | ||
|
|
20bfc02a4e | ||
|
|
2511724b73 | ||
|
|
a851ee3fab | ||
|
|
40f6e6f8a8 | ||
|
|
e2fd104c2d | ||
|
|
a3eafa54fa | ||
|
|
b90b4a1910 | ||
|
|
247df4d5b9 | ||
|
|
4a3889a76e | ||
|
|
224a6e069c | ||
|
|
2c3097eeb7 | ||
|
|
3289ede0e8 | ||
|
|
ddf21c17d2 | ||
|
|
c9a24a0d28 | ||
|
|
fa4b130b18 | ||
|
|
8561a7547f | ||
|
|
e85f7b8232 | ||
|
|
2f91d25ed4 | ||
|
|
ae304b5af7 | ||
|
|
363d34b5e5 | ||
|
|
1c51f8b32e | ||
|
|
aaff07bba2 | ||
|
|
4bc38d5e0f | ||
|
|
fdfca00886 | ||
|
|
d872e393f8 | ||
|
|
686d8e5be7 | ||
|
|
4e30efd737 | ||
|
|
aa4935c505 | ||
|
|
0e381ab850 | ||
|
|
4a03ee4518 | ||
|
|
be414d8279 | ||
|
|
c47b1f2413 | ||
|
|
ba9ee052a6 | ||
|
|
b1c6708762 | ||
|
|
0ba31b6c79 | ||
|
|
14f402f186 | ||
|
|
b811e97ea5 | ||
|
|
6d167ce1d6 | ||
|
|
9ac1d23fd9 | ||
|
|
dea3a34e3d | ||
|
|
39de34f083 | ||
|
|
02d5f0fdf3 | ||
|
|
0bb7cbe8d9 | ||
|
|
3e3b88b8c2 | ||
|
|
1438beb93b | ||
|
|
c1ec5dc60d | ||
|
|
3f513f2f4d | ||
|
|
d1b573f6f9 | ||
|
|
0e08abc46f | ||
|
|
49bdf9f203 | ||
|
|
b3e9df4f3d | ||
|
|
3b8cf4a60a | ||
|
|
b7ccb9a34c | ||
|
|
b8ffa872c7 | ||
|
|
ef866f7851 | ||
|
|
f27515bf1d | ||
|
|
ddf74c5d7c | ||
|
|
8ea12695b3 | ||
|
|
d6324e6ba6 | ||
|
|
2325982801 | ||
|
|
b103c45e65 | ||
|
|
409989eddb | ||
|
|
3f5fcae280 | ||
|
|
d5123c793a | ||
|
|
e6707c1e4a | ||
|
|
41be5cc4a8 | ||
|
|
e82a4a1d23 | ||
|
|
e30e4edfce | ||
|
|
bd72ef7950 | ||
|
|
539034df2a | ||
|
|
6da6afc5a1 | ||
|
|
fea523972d | ||
|
|
5cfc6c2949 | ||
|
|
6dd0c25d49 | ||
|
|
ab9b3e47b9 | ||
|
|
33ebdf2831 | ||
|
|
ef95939763 | ||
|
|
0394deaeef | ||
|
|
337a60c8a4 | ||
|
|
47b341d50e | ||
|
|
a5fd39adf8 | ||
|
|
5772117dc8 | ||
|
|
f73c8a45d9 | ||
|
|
109d4190ae | ||
|
|
317de82be6 | ||
|
|
3604d69463 | ||
|
|
96cf774652 | ||
|
|
d2b39976ba | ||
|
|
06161bc963 | ||
|
|
2ab1d3dbfa | ||
|
|
ecdc7919ae | ||
|
|
a24541ac2b | ||
|
|
d832690548 | ||
|
|
2844710ea8 | ||
|
|
84ad4071ad | ||
|
|
7f501241f0 | ||
|
|
5dcfa66c5d | ||
|
|
f428a21279 | ||
|
|
ccdd4963cd | ||
|
|
2092a3729c | ||
|
|
a628420adb | ||
|
|
23de147ca1 | ||
|
|
93f55f5619 | ||
|
|
b10933898f | ||
|
|
1727b754d4 | ||
|
|
ffb0c12c08 | ||
|
|
c332808fe4 | ||
|
|
8f0538c886 | ||
|
|
e6bb7e2ca9 | ||
|
|
2a012520d0 | ||
|
|
c71333d9cb | ||
|
|
728515c225 | ||
|
|
e0f88e01f9 | ||
|
|
1aa94a12c0 | ||
|
|
de2d3361a7 | ||
|
|
680937e50b | ||
|
|
6751c50514 | ||
|
|
0c389701fe | ||
|
|
9454d8c100 | ||
|
|
166f9e817b | ||
|
|
d2865c637c | ||
|
|
9698932bde | ||
|
|
44026fcd41 | ||
|
|
d938af0c2c | ||
|
|
fd658400a6 | ||
|
|
dcbb17b164 | ||
|
|
49741875bd | ||
|
|
0fcd9733ff | ||
|
|
62d50169d7 | ||
|
|
1d3d5a1e6a | ||
|
|
d309fa745e | ||
|
|
f7031992d5 | ||
|
|
fcdf68ea15 | ||
|
|
b0a6692179 | ||
|
|
e1e763575d | ||
|
|
4236d1179c | ||
|
|
09365bcabe | ||
|
|
be29ddcbd6 | ||
|
|
522194ca8d | ||
|
|
5af8f584f6 | ||
|
|
adc08f8865 | ||
|
|
0f39d54c3c | ||
|
|
9e6659e842 | ||
|
|
46a0f6e3f8 | ||
|
|
e90b26ebed | ||
|
|
4407f260ae | ||
|
|
d4392416c8 | ||
|
|
2d3cbd26b8 | ||
|
|
98b9660956 | ||
|
|
4e8a4e8914 | ||
|
|
96bcbc80f8 | ||
|
|
1dfc8e2973 | ||
|
|
311886f6c9 | ||
|
|
4a332f40e5 | ||
|
|
93a97a2f4c | ||
|
|
1dee098ca2 | ||
|
|
a07cc7e560 | ||
|
|
c26f7f5ebc | ||
|
|
5d801cf566 | ||
|
|
631b2d869e | ||
|
|
c02f54cc18 | ||
|
|
827a95515d | ||
|
|
9082152f1a | ||
|
|
0efbddeda4 | ||
|
|
b2e186957c | ||
|
|
d855e5cffb | ||
|
|
f3747edaa3 | ||
|
|
752932ef3d | ||
|
|
948cf9dae3 | ||
|
|
b2f93aa549 | ||
|
|
108f228edf | ||
|
|
fe6030140f | ||
|
|
003400cfa8 | ||
|
|
41a02f059d | ||
|
|
b4ed6fd107 | ||
|
|
36246da9e1 | ||
|
|
457b6b982c | ||
|
|
05a07dc4a1 | ||
|
|
85889c2cb9 | ||
|
|
be6ceaab04 | ||
|
|
f1b18688bb | ||
|
|
80c7decb81 | ||
|
|
3ef5a1e21a | ||
|
|
2eb0a4c754 | ||
|
|
10f5af5dda | ||
|
|
8b27ebb96b | ||
|
|
b28f82a881 | ||
|
|
c921606f3f | ||
|
|
c6c08f6c60 | ||
|
|
02cf620090 | ||
|
|
917550ff4d | ||
|
|
4a12cc76fa | ||
|
|
f4f74e223f | ||
|
|
8b4535c131 | ||
|
|
b15fd6c75a | ||
|
|
e1a25fa894 | ||
|
|
2bb3b71a70 | ||
|
|
4c55b9c304 | ||
|
|
639a629809 | ||
|
|
d6e3bd09b4 | ||
|
|
8d67a0d95f | ||
|
|
b9fc0175e7 | ||
|
|
dc5f52cc0d | ||
|
|
602aabdeb8 | ||
|
|
2f8aa79ec1 | ||
|
|
8af90432cf | ||
|
|
61da0733c2 | ||
|
|
33951482d5 | ||
|
|
4e8484ee7c | ||
|
|
071761a61e | ||
|
|
10a11d6725 | ||
|
|
c81178ae26 | ||
|
|
2bafae5397 | ||
|
|
6a1d201e0e | ||
|
|
8de544696d | ||
|
|
66c489da8f | ||
|
|
26c8f35688 | ||
|
|
c3e881ed77 | ||
|
|
2cf55cbb96 | ||
|
|
73f22abf56 | ||
|
|
dbae90a193 | ||
|
|
28aeac4533 | ||
|
|
162b3d6192 | ||
|
|
b016d3fd9d | ||
|
|
f64ff58dbc | ||
|
|
d4d3d96bbb | ||
|
|
a5197ee544 | ||
|
|
8a5fd4f745 | ||
|
|
12cd7940c6 | ||
|
|
0c2cec46a7 | ||
|
|
84457ddc86 | ||
|
|
d6d20e5d49 | ||
|
|
9acdca1059 | ||
|
|
66c4fd4d2f | ||
|
|
f2defb3a31 | ||
|
|
aa66c10608 | ||
|
|
28d20a9c61 | ||
|
|
94fcc0a6a9 | ||
|
|
91abdd5415 | ||
|
|
58e940d193 | ||
|
|
6991dd48f3 | ||
|
|
270a077539 | ||
|
|
ff326acfc8 | ||
|
|
af1446233c | ||
|
|
67d66e2b2f | ||
|
|
753ce5bd6e | ||
|
|
e7d71ef301 | ||
|
|
8430921e4e | ||
|
|
c938abf606 | ||
|
|
3addfe8d4b | ||
|
|
5276556837 | ||
|
|
e47ad94666 | ||
|
|
7065763c7c | ||
|
|
86489d95c2 | ||
|
|
f357c4f171 | ||
|
|
36e34fe667 | ||
|
|
dcc59f42fa | ||
|
|
f6fbff2b42 | ||
|
|
67ce20ef62 | ||
|
|
788e0dc851 | ||
|
|
e2b1c83698 | ||
|
|
15373be63e | ||
|
|
8c9cd079b7 | ||
|
|
6f67a97ade | ||
|
|
ada568cb75 | ||
|
|
b9fa254ab5 | ||
|
|
174ba90fb5 | ||
|
|
de8c7c1ca3 | ||
|
|
407a125533 | ||
|
|
d8881ba6a3 | ||
|
|
755540fb81 | ||
|
|
add358c752 | ||
|
|
91352ade20 | ||
|
|
39fbf4ef33 | ||
|
|
d9547c6654 | ||
|
|
04f9428267 | ||
|
|
ebd8f43219 | ||
|
|
f75b2e26a3 | ||
|
|
2e654c143f | ||
|
|
5bcee265a6 | ||
|
|
647599e5aa | ||
|
|
16b9a2b06e | ||
|
|
7da427c669 | ||
|
|
405e6c1e4e | ||
|
|
efbc21826b | ||
|
|
7a1f1c9df7 | ||
|
|
476bfbaef0 |
2
.github/FUNDING.yml
vendored
@@ -1,4 +1,2 @@
|
||||
github: hoppscotch
|
||||
open_collective: hoppscotch
|
||||
patreon: liyasthomas
|
||||
custom: https://www.paypal.me/liyascthomas
|
||||
|
||||
83
.github/workflows/codeql-analysis.yml
vendored
@@ -1,62 +1,61 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
name: "CodeQL"
|
||||
name: "Code Scanning - Action"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [main]
|
||||
schedule:
|
||||
# ┌───────────── minute (0 - 59)
|
||||
# │ ┌───────────── hour (0 - 23)
|
||||
# │ │ ┌───────────── day of the month (1 - 31)
|
||||
# │ │ │ ┌───────────── month (1 - 12 or JAN-DEC)
|
||||
# │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT)
|
||||
# │ │ │ │ │
|
||||
# │ │ │ │ │
|
||||
# │ │ │ │ │
|
||||
# * * * * *
|
||||
- cron: '0 0 * * 6'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
CodeQL-Build:
|
||||
# CodeQL runs on ubuntu-latest, windows-latest, and macos-latest
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
# Override automatic language detection by changing the below list
|
||||
# Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
|
||||
language: ['javascript']
|
||||
# Learn more...
|
||||
# https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection
|
||||
permissions:
|
||||
# required for all workflows
|
||||
security-events: write
|
||||
|
||||
# only required for workflows in private repositories
|
||||
actions: read
|
||||
contents: read
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
# Override language selection by uncommenting this and choosing your languages
|
||||
# with:
|
||||
# languages: go, javascript, csharp, python, cpp, java
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below).
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following
|
||||
# three lines and modify them (or add more) to build your code if your
|
||||
# project uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
# This file was auto-generated by the Firebase CLI
|
||||
# https://github.com/firebase/firebase-tools
|
||||
name: Deploy to Live Channel
|
||||
|
||||
name: Deploy to Firebase Hosting on merge
|
||||
'on':
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build_and_deploy:
|
||||
deploy_live_website:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
39
.github/workflows/publish-docker.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: Publish Docker image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
jobs:
|
||||
push_to_registry:
|
||||
name: Push Docker image to Docker Hub
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
|
||||
with:
|
||||
images: hoppscotch/hoppscotch
|
||||
flavor: |
|
||||
latest=true
|
||||
prefix=
|
||||
suffix=
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@ad44023a93711e3deb337508980b4b5e9bcdc5dc
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
15
.github/workflows/tests.yml
vendored
@@ -2,13 +2,12 @@ name: Node.js CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ main ]
|
||||
branches: [main]
|
||||
pull_request:
|
||||
branches: [ main ]
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
@@ -17,9 +16,13 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
- name: Install pnpm
|
||||
run: curl -f https://get.pnpm.io/v6.14.js | node - add --global pnpm@6
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: npm ci
|
||||
- run: npm test
|
||||
cache: pnpm
|
||||
- name: Run tests
|
||||
run: pnpm i && pnpm -r test
|
||||
|
||||
6
.gitignore
vendored
@@ -104,3 +104,9 @@ tests/*/screenshots
|
||||
|
||||
# Tests videos
|
||||
tests/*/videos
|
||||
|
||||
# Local Netlify folder
|
||||
.netlify
|
||||
|
||||
# Andrew's crazy Volar shim generator
|
||||
shims-volar.d.ts
|
||||
1
.husky/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
_
|
||||
@@ -6,7 +6,7 @@ We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, caste, color, religion, or sexual identity
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
@@ -17,23 +17,23 @@ diverse, inclusive, and healthy community.
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
- Demonstrating empathy and kindness toward other people
|
||||
- Being respectful of differing opinions, viewpoints, and experiences
|
||||
- Giving and gracefully accepting constructive feedback
|
||||
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
* Demonstrating empathy and kindness toward other people
|
||||
* Being respectful of differing opinions, viewpoints, and experiences
|
||||
* Giving and gracefully accepting constructive feedback
|
||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
- Focusing on what is best not just for us as individuals, but for the
|
||||
* Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
- The use of sexualized language or imagery, and sexual attention or
|
||||
* The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or email
|
||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
@@ -60,7 +60,7 @@ representative at an online or offline event.
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement at
|
||||
[INSERT CONTACT METHOD].
|
||||
support@hoppscotch.io.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
@@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban.
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
@@ -116,17 +116,13 @@ the community.
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.0, available at
|
||||
[https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0].
|
||||
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||
|
||||
Community Impact Guidelines were inspired by
|
||||
[Mozilla's code of conduct enforcement ladder][mozilla coc].
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
[https://www.contributor-covenant.org/faq][faq]. Translations are available
|
||||
at [https://www.contributor-covenant.org/translations][translations].
|
||||
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||
enforcement ladder](https://github.com/mozilla/diversity).
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
[v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html
|
||||
[mozilla coc]: https://github.com/mozilla/diversity
|
||||
[faq]: https://www.contributor-covenant.org/faq
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
https://www.contributor-covenant.org/faq. Translations are available at
|
||||
https://www.contributor-covenant.org/translations.
|
||||
|
||||
16
Dockerfile
@@ -1,4 +1,4 @@
|
||||
FROM node:12-alpine
|
||||
FROM node:lts-alpine
|
||||
|
||||
LABEL maintainer="Hoppscotch (support@hoppscotch.io)"
|
||||
|
||||
@@ -9,17 +9,19 @@ RUN apk add --update --no-cache \
|
||||
# Create app directory
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
|
||||
RUN npm install
|
||||
|
||||
ADD . /app/
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN npm install -g pnpm
|
||||
|
||||
RUN pnpm i --unsafe-perm=true
|
||||
|
||||
ENV HOST 0.0.0.0
|
||||
EXPOSE 3000
|
||||
|
||||
RUN mv .env.example .env
|
||||
RUN mv packages/hoppscotch-app/.env.example packages/hoppscotch-app/.env
|
||||
|
||||
CMD ["npm", "run", "dev"]
|
||||
RUN pnpm run generate
|
||||
|
||||
CMD ["pnpm", "run", "start"]
|
||||
|
||||
39
README.md
@@ -1,7 +1,7 @@
|
||||
<div align="center">
|
||||
<a href="https://hoppscotch.io">
|
||||
<img
|
||||
src="https://raw.githubusercontent.com/hoppscotch/hoppscotch/main/static/logo.png"
|
||||
src="https://avatars.githubusercontent.com/u/56705483"
|
||||
alt="Hoppscotch Logo"
|
||||
height="64"
|
||||
/>
|
||||
@@ -36,7 +36,7 @@
|
||||
<p>
|
||||
<a href="https://hoppscotch.io">
|
||||
<img
|
||||
src="https://raw.githubusercontent.com/hoppscotch/hoppscotch/main/static/images/screenshots/light_rest.png"
|
||||
src="https://raw.githubusercontent.com/hoppscotch/hoppscotch/main/packages/hoppscotch-app/static/banner.png"
|
||||
alt="Screenshot"
|
||||
width="100%"
|
||||
/>
|
||||
@@ -89,7 +89,7 @@
|
||||
- `TRACE` - Performs a message loop-back test along the path to the target resource
|
||||
- `<custom>` - Some APIs use custom request methods such as `LIST`. Type in your custom methods.
|
||||
|
||||
🌈 **Make it yours:** Customizable combinations for background, foreground and accent colors — [customize now ✨](https://hoppscotch.io/settings).
|
||||
🌈 **Make it yours:** Customizable combinations for background, foreground and accent colors — [customize now](https://hoppscotch.io/settings).
|
||||
|
||||
**Theming**
|
||||
|
||||
@@ -173,7 +173,7 @@ _Collections are synced with cloud / local session storage_
|
||||
|
||||
- Hide your IP address
|
||||
- Fixes [`CORS`](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) (Cross Origin Resource Sharing) issues
|
||||
- Access APIs served in non-HTTPS `[http://]` endpoints
|
||||
- Access APIs served in non-HTTPS (`http://`) endpoints
|
||||
- Use your own Proxy URL
|
||||
|
||||
_Official proxy server is hosted by Hoppscotch - **[GitHub](https://github.com/hoppscotch/proxyscotch)** - **[Privacy Policy](https://docs.hoppscotch.io/privacy)**_
|
||||
@@ -197,6 +197,8 @@ _Official proxy server is hosted by Hoppscotch - **[GitHub](https://github.com/h
|
||||
|
||||
🌎 **i18n:** Experience the app in your own language.
|
||||
|
||||
Help us to translate Hoppscotch. Please read [`TRANSLATIONS`](TRANSLATIONS.md) for details on our [`CODE OF CONDUCT`](CODE_OF_CONDUCT.md), and the process for submitting pull requests to us.
|
||||
|
||||
📦 **Add-ons:** Official add-ons for hoppscotch.
|
||||
|
||||
- **[Proxy](https://github.com/hoppscotch/proxyscotch)** - A simple proxy server created for Hoppscotch
|
||||
@@ -254,9 +256,18 @@ _Add-ons are developed and maintained under **[Hoppscotch Organization](https://
|
||||
|
||||
👨👩👧👦 **Teams β:** Helps you collaborate across your team to design, develop, and test APIs faster.
|
||||
|
||||
- Unlimited team collections and shared requests
|
||||
- Unlimited teams
|
||||
- Unlimited shared collections
|
||||
- Unlimited team members
|
||||
- User roles
|
||||
- Role-based access control
|
||||
- Cloud sync
|
||||
- Multiple devices
|
||||
|
||||
🚚 **Bulk Edit:** Edit key-value pairs in bulk.
|
||||
|
||||
- Entries are separated by newline
|
||||
- Keys and values are separated by `:`
|
||||
- Prepend `//` to any row you want to add but keep disabled
|
||||
|
||||
**For more features, please read our [documentation](https://docs.hoppscotch.io).**
|
||||
|
||||
@@ -282,7 +293,7 @@ _Add-ons are developed and maintained under **[Hoppscotch Organization](https://
|
||||
|
||||
## **Developing**
|
||||
|
||||
0. Update [`.env.example`](https://github.com/hoppscotch/hoppscotch/blob/main/.env.example) file found in repository's root directory with your own keys and rename it to `.env`.
|
||||
0. Update [`.env.example`](https://github.com/hoppscotch/hoppscotch/blob/main/packages/hoppscotch-app/.env.example) file found in `packages/hoppscotch-app` with your own keys and rename it to `.env`.
|
||||
|
||||
_Sample keys only works with the [production build](https://hoppscotch.io)._
|
||||
|
||||
@@ -294,9 +305,10 @@ _Sample keys only works with the [production build](https://hoppscotch.io)._
|
||||
### Local development environment
|
||||
|
||||
1. [Clone this repo](https://help.github.com/en/articles/cloning-a-repository) with git.
|
||||
2. Install dependencies by running `npm install` within the directory that you cloned (probably `hoppscotch`).
|
||||
3. Start the development server with `npm run dev`.
|
||||
4. Open development site by going to [`http://localhost:3000`](http://localhost:3000) in your browser.
|
||||
2. Install pnpm using npm by running `npm install -g pnpm`.
|
||||
3. Install dependencies by running `pnpm install` within the directory that you cloned (probably `hoppscotch`).
|
||||
4. Start the development server with `pnpm run dev`.
|
||||
5. Open development site by going to [`http://localhost:3000`](http://localhost:3000) in your browser.
|
||||
|
||||
### Docker compose
|
||||
|
||||
@@ -315,9 +327,10 @@ docker run --rm --name hoppscotch -p 3000:3000 hoppscotch/hoppscotch:latest
|
||||
## **Releasing**
|
||||
|
||||
1. [Clone this repo](https://help.github.com/en/articles/cloning-a-repository) with git.
|
||||
2. Install dependencies by running `npm install` within the directory that you cloned (probably `hoppscotch`).
|
||||
3. Build the release files with `npm run generate`.
|
||||
4. Find the built project in `./dist`.
|
||||
2. Install pnpm using npm by running `npm install -g pnpm`.
|
||||
3. Install dependencies by running `pnpm install` within the directory that you cloned (probably `hoppscotch`).
|
||||
4. Build the release files with `pnpm run generate`.
|
||||
5. Find the built project in `packages/hoppscotch-app/dist`.
|
||||
|
||||
## **Contributing**
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@ if there is no existing translation, you can create a new one by following these
|
||||
|
||||
1. **[Fork the repository](https://github.com/hoppscotch/hoppscotch/fork).**
|
||||
2. **Create a new branch for your translation.**
|
||||
3. **Create target language file in the [`locales`](https://github.com/hoppscotch/hoppscotch/tree/main/locales) directory.**
|
||||
4. **Copy the contents of the source file [`locales/en.json`](https://github.com/hoppscotch/hoppscotch/blob/main/locales/en.json) to the target language file.**
|
||||
3. **Create target language file in the [`locales`](https://github.com/hoppscotch/hoppscotch/tree/main/packages/hoppscotch-app/locales) directory.**
|
||||
4. **Copy the contents of the source file [`locales/en.json`](https://github.com/hoppscotch/hoppscotch/blob/main/packages/hoppscotch-app/locales/en.json) to the target language file.**
|
||||
5. **Translate the strings in the target language file.**
|
||||
6. **Add your language entry to [`languages.json`](https://github.com/hoppscotch/hoppscotch/blob/main/languages.json).**
|
||||
6. **Add your language entry to [`languages.json`](https://github.com/hoppscotch/hoppscotch/blob/main/packages/hoppscotch-app/languages.json).**
|
||||
7. **Save & commit changes.**
|
||||
8. **Send a pull request.**
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient x1="49.998%" y1=".706%" x2="49.998%" y2="96.99%" id="IconifyId-17b2bfeca9d-d6badc-3320"><stop stop-color="#86BBE5" offset="0%"></stop><stop stop-color="#1072BA" offset="100%"></stop></linearGradient></defs><path d="M127.789.035s75.32-3.38 115.253 72.328H121.38s-22.96-.74-42.573 27.114c-5.634 11.691-11.69 23.734-4.894 47.468c-9.79-16.586-51.975-90.04-51.975-90.04S51.693 3.028 127.788.035z" fill="#EF3F36"></path><path d="M239.133 192.229s-34.756 66.94-120.253 63.63c10.564-18.276 60.848-105.358 60.848-105.358s12.149-19.508-2.183-50.425c-7.29-10.74-14.72-21.973-38.664-27.96c19.262-.175 103.95 0 103.95 0s31.726 52.715-3.698 120.113z" fill="#FCD900"></path><path d="M16.973 192.757s-40.601-63.56 5.035-135.958c10.529 18.276 60.813 105.358 60.813 105.358s10.846 20.283 44.756 23.31c12.924-.95 26.375-1.76 43.56-19.472C161.663 182.757 119.16 256 119.16 256s-61.552 1.127-102.188-63.243z" fill="#61BC5B"></path><path d="M118.845 256.493l17.113-71.412s18.804-1.48 34.58-18.769c-9.79 17.22-51.693 90.181-51.693 90.181z" fill="#5AB055"></path><path d="M70.462 129.056c0-31.48 25.53-57.01 57.01-57.01c31.48 0 57.01 25.53 57.01 57.01c0 31.481-25.53 57.01-57.01 57.01c-31.48-.035-57.01-25.529-57.01-57.01z" fill="#FFF"></path><path d="M80.004 129.056c0-26.198 21.234-47.467 47.468-47.467c26.198 0 47.467 21.234 47.467 47.467c0 26.199-21.233 47.468-47.467 47.468c-26.199 0-47.468-21.269-47.468-47.468z" fill="url(#IconifyId-17b2bfeca9d-d6badc-3320)"></path><path d="M242.795 72.152l-70.462 20.67s-10.634-15.6-33.487-20.67c19.825-.106 103.949 0 103.949 0z" fill="#EACA05"></path><path d="M72.54 144.339c-9.896-17.149-50.602-87.434-50.602-87.434l52.186 51.622s-5.353 11.022-3.345 26.797l1.76 9.015z" fill="#DF3A32"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.9 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="41.17" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 199"><path d="M216.856 16.597A208.502 208.502 0 0 0 164.042 0c-2.275 4.113-4.933 9.645-6.766 14.046c-19.692-2.961-39.203-2.961-58.533 0c-1.832-4.4-4.55-9.933-6.846-14.046a207.809 207.809 0 0 0-52.855 16.638C5.618 67.147-3.443 116.4 1.087 164.956c22.169 16.555 43.653 26.612 64.775 33.193A161.094 161.094 0 0 0 79.735 175.3a136.413 136.413 0 0 1-21.846-10.632a108.636 108.636 0 0 0 5.356-4.237c42.122 19.702 87.89 19.702 129.51 0a131.66 131.66 0 0 0 5.355 4.237a136.07 136.07 0 0 1-21.886 10.653c4.006 8.02 8.638 15.67 13.873 22.848c21.142-6.58 42.646-16.637 64.815-33.213c5.316-56.288-9.08-105.09-38.056-148.36zM85.474 135.095c-12.645 0-23.015-11.805-23.015-26.18s10.149-26.2 23.015-26.2c12.867 0 23.236 11.804 23.015 26.2c.02 14.375-10.148 26.18-23.015 26.18zm85.051 0c-12.645 0-23.014-11.805-23.014-26.18s10.148-26.2 23.014-26.2c12.867 0 23.236 11.804 23.015 26.2c0 14.375-10.148 26.18-23.015 26.18z" fill="#5865F2"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--simple-icons" width="32" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 24 24"><path d="M15.61 12c0 1.99-1.62 3.61-3.61 3.61c-1.99 0-3.61-1.62-3.61-3.61c0-1.99 1.62-3.61 3.61-3.61c1.99 0 3.61 1.62 3.61 3.61M12 0C5.383 0 0 5.383 0 12s5.383 12 12 12c2.424 0 4.761-.722 6.76-2.087l.034-.024l-1.617-1.879l-.027.017A9.494 9.494 0 0 1 12 21.54c-5.26 0-9.54-4.28-9.54-9.54c0-5.26 4.28-9.54 9.54-9.54c5.26 0 9.54 4.28 9.54 9.54a9.63 9.63 0 0 1-.225 2.05c-.301 1.239-1.169 1.618-1.82 1.568c-.654-.053-1.42-.52-1.426-1.661V12A6.076 6.076 0 0 0 12 5.93A6.076 6.076 0 0 0 5.93 12A6.076 6.076 0 0 0 12 18.07a6.02 6.02 0 0 0 4.3-1.792a3.9 3.9 0 0 0 3.32 1.805c.874 0 1.74-.292 2.437-.821c.719-.547 1.256-1.336 1.553-2.285c.047-.154.135-.504.135-.507l.002-.013c.175-.76.253-1.52.253-2.457c0-6.617-5.383-12-12-12" fill="currentColor"></path></svg>
|
||||
|
Before Width: | Height: | Size: 982 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="32" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M241.871 256.001c7.802 0 14.129-6.326 14.129-14.129V14.129C256 6.325 249.673 0 241.871 0H14.129C6.324 0 0 6.325 0 14.129v227.743c0 7.803 6.324 14.129 14.129 14.129h227.742" fill="#395185"></path><path d="M176.635 256.001v-99.137h33.277l4.982-38.635h-38.259V93.561c0-11.186 3.107-18.809 19.148-18.809l20.459-.009V40.188c-3.54-.471-15.684-1.523-29.812-1.523c-29.498 0-49.692 18.005-49.692 51.071v28.493h-33.362v38.635h33.362v99.137h39.897" fill="#FFF"></path></svg>
|
||||
|
Before Width: | Height: | Size: 697 B |
|
Before Width: | Height: | Size: 14 KiB |
@@ -1 +0,0 @@
|
||||
<svg width="2500" height="2432" viewBox="0 0 256 249" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet"><g fill="currentColor"><path d="M127.505 0C57.095 0 0 57.085 0 127.505c0 56.336 36.534 104.13 87.196 120.99 6.372 1.18 8.712-2.766 8.712-6.134 0-3.04-.119-13.085-.173-23.739-35.473 7.713-42.958-15.044-42.958-15.044-5.8-14.738-14.157-18.656-14.157-18.656-11.568-7.914.872-7.752.872-7.752 12.804.9 19.546 13.14 19.546 13.14 11.372 19.493 29.828 13.857 37.104 10.6 1.144-8.242 4.449-13.866 8.095-17.05-28.32-3.225-58.092-14.158-58.092-63.014 0-13.92 4.981-25.295 13.138-34.224-1.324-3.212-5.688-16.18 1.235-33.743 0 0 10.707-3.427 35.073 13.07 10.17-2.826 21.078-4.242 31.914-4.29 10.836.048 21.752 1.464 31.942 4.29 24.337-16.497 35.029-13.07 35.029-13.07 6.94 17.563 2.574 30.531 1.25 33.743 8.175 8.929 13.122 20.303 13.122 34.224 0 48.972-29.828 59.756-58.22 62.912 4.573 3.957 8.648 11.717 8.648 23.612 0 17.06-.148 30.791-.148 34.991 0 3.393 2.295 7.369 8.759 6.117 50.634-16.879 87.122-64.656 87.122-120.973C255.009 57.085 197.922 0 127.505 0"/><path d="M47.755 181.634c-.28.633-1.278.823-2.185.389-.925-.416-1.445-1.28-1.145-1.916.275-.652 1.273-.834 2.196-.396.927.415 1.455 1.287 1.134 1.923M54.027 187.23c-.608.564-1.797.302-2.604-.589-.834-.889-.99-2.077-.373-2.65.627-.563 1.78-.3 2.616.59.834.899.996 2.08.36 2.65M58.33 194.39c-.782.543-2.06.034-2.849-1.1-.781-1.133-.781-2.493.017-3.038.792-.545 2.05-.055 2.85 1.07.78 1.153.78 2.513-.019 3.069M65.606 202.683c-.699.77-2.187.564-3.277-.488-1.114-1.028-1.425-2.487-.724-3.258.707-.772 2.204-.555 3.302.488 1.107 1.026 1.445 2.496.7 3.258M75.01 205.483c-.307.998-1.741 1.452-3.185 1.028-1.442-.437-2.386-1.607-2.095-2.616.3-1.005 1.74-1.478 3.195-1.024 1.44.435 2.386 1.596 2.086 2.612M85.714 206.67c.036 1.052-1.189 1.924-2.705 1.943-1.525.033-2.758-.818-2.774-1.852 0-1.062 1.197-1.926 2.721-1.951 1.516-.03 2.758.815 2.758 1.86M96.228 206.267c.182 1.026-.872 2.08-2.377 2.36-1.48.27-2.85-.363-3.039-1.38-.184-1.052.89-2.105 2.367-2.378 1.508-.262 2.857.355 3.049 1.398"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 2.0 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M118.2 208.3c19.9-60.3 76.5-103.6 143.7-103.6 36.1 0 68.6 12.8 94.3 33.7L430.5 64C385.2 24.4 327 0 261.8 0 160.9 0 74 57.6 32.3 141.9l85.9 66.4z" fill="currentColor"/><path d="M348 384.3c-23.3 15-52.8 23-86.2 23-66.9 0-123.3-43-143.4-102.9l-86.2 65.4C73.9 454.3 160.8 512 261.8 512c62.6 0 122.4-22.2 167.1-64L348 384.3z" fill="currentColor"/><path d="M428.9 448c46.8-43.7 77.2-108.7 77.2-192 0-15.1-2.3-31.4-5.8-46.5H261.8v98.9h137.3c-6.8 33.3-25 59-51.1 75.9l80.9 63.7z" fill="currentColor"/><path d="M118.4 304.4c-5.1-15.2-7.9-31.4-7.9-48.4 0-16.7 2.7-32.7 7.6-47.7l-85.9-66.4C15.1 176.2 5.8 214.9 5.8 256c0 40.9 9.5 79.6 26.4 113.8l86.2-65.4z" fill="currentColor"/></svg>
|
||||
|
Before Width: | Height: | Size: 746 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="32" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><path d="M218.123 218.127h-37.931v-59.403c0-14.165-.253-32.4-19.728-32.4c-19.756 0-22.779 15.434-22.779 31.369v60.43h-37.93V95.967h36.413v16.694h.51a39.907 39.907 0 0 1 35.928-19.733c38.445 0 45.533 25.288 45.533 58.186l-.016 67.013zM56.955 79.27c-12.157.002-22.014-9.852-22.016-22.009c-.002-12.157 9.851-22.014 22.008-22.016c12.157-.003 22.014 9.851 22.016 22.008A22.013 22.013 0 0 1 56.955 79.27m18.966 138.858H37.95V95.967h37.97v122.16zM237.033.018H18.89C8.58-.098.125 8.161-.001 18.471v219.053c.122 10.315 8.576 18.582 18.89 18.474h218.144c10.336.128 18.823-8.139 18.966-18.474V18.454c-.147-10.33-8.635-18.588-18.966-18.453" fill="#0A66C2"></path></svg>
|
||||
|
Before Width: | Height: | Size: 882 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="32" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 256"><circle fill="#FF4500" cx="128" cy="128" r="128"></circle><path d="M213.15 129.22c0-10.376-8.391-18.617-18.617-18.617a18.74 18.74 0 0 0-12.97 5.189c-12.818-9.157-30.368-15.107-49.9-15.87l8.544-39.981l27.773 5.95c.307 7.02 6.104 12.667 13.278 12.667c7.324 0 13.275-5.95 13.275-13.278c0-7.324-5.95-13.275-13.275-13.275c-5.188 0-9.768 3.052-11.904 7.478l-30.976-6.562c-.916-.154-1.832 0-2.443.458c-.763.458-1.22 1.22-1.371 2.136l-9.464 44.558c-19.837.612-37.692 6.562-50.662 15.872a18.74 18.74 0 0 0-12.971-5.188c-10.377 0-18.617 8.391-18.617 18.617c0 7.629 4.577 14.037 10.988 16.939a33.598 33.598 0 0 0-.458 5.646c0 28.686 33.42 52.036 74.621 52.036c41.202 0 74.622-23.196 74.622-52.036a35.29 35.29 0 0 0-.458-5.646c6.408-2.902 10.985-9.464 10.985-17.093zM85.272 142.495c0-7.324 5.95-13.275 13.278-13.275c7.324 0 13.275 5.95 13.275 13.275s-5.95 13.278-13.275 13.278c-7.327.15-13.278-5.953-13.278-13.278zm74.317 35.251c-9.156 9.157-26.553 9.768-31.588 9.768c-5.188 0-22.584-.765-31.59-9.768c-1.371-1.373-1.371-3.51 0-4.883c1.374-1.371 3.51-1.371 4.884 0c5.8 5.8 18.008 7.782 26.706 7.782c8.699 0 21.058-1.983 26.704-7.782c1.374-1.371 3.51-1.371 4.884 0c1.22 1.373 1.22 3.51 0 4.883zm-2.443-21.822c-7.325 0-13.275-5.95-13.275-13.275s5.95-13.275 13.275-13.275c7.327 0 13.277 5.95 13.277 13.275c0 7.17-5.95 13.275-13.277 13.275z" fill="#FFF"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="39.2" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 209"><path d="M256 25.45c-9.42 4.177-19.542 7-30.166 8.27c10.845-6.5 19.172-16.793 23.093-29.057a105.183 105.183 0 0 1-33.351 12.745C205.995 7.201 192.346.822 177.239.822c-29.006 0-52.523 23.516-52.523 52.52c0 4.117.465 8.125 1.36 11.97c-43.65-2.191-82.35-23.1-108.255-54.876c-4.52 7.757-7.11 16.78-7.11 26.404c0 18.222 9.273 34.297 23.365 43.716a52.312 52.312 0 0 1-23.79-6.57c-.003.22-.003.44-.003.661c0 25.447 18.104 46.675 42.13 51.5a52.592 52.592 0 0 1-23.718.9c6.683 20.866 26.08 36.05 49.062 36.475c-17.975 14.086-40.622 22.483-65.228 22.483c-4.24 0-8.42-.249-12.529-.734c23.243 14.902 50.85 23.597 80.51 23.597c96.607 0 149.434-80.031 149.434-149.435c0-2.278-.05-4.543-.152-6.795A106.748 106.748 0 0 0 256 25.45" fill="#55acee"></path></svg>
|
||||
|
Before Width: | Height: | Size: 971 B |
@@ -1,164 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="flex justify-between">
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="LEFT_SIDEBAR ? $t('hide.sidebar') : $t('show.sidebar')"
|
||||
icon="menu_open"
|
||||
:class="{ 'transform -rotate-180': !LEFT_SIDEBAR }"
|
||||
@click.native="LEFT_SIDEBAR = !LEFT_SIDEBAR"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="`${
|
||||
ZEN_MODE ? $t('action.turn_off') : $t('action.turn_on')
|
||||
} ${$t('layout.zen_mode')}`"
|
||||
:icon="ZEN_MODE ? 'fullscreen_exit' : 'fullscreen'"
|
||||
:class="{
|
||||
'!text-accent !focus-visible:text-accentDark !hover:text-accentDark':
|
||||
ZEN_MODE,
|
||||
}"
|
||||
@click.native="ZEN_MODE = !ZEN_MODE"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<span>
|
||||
<tippy
|
||||
ref="options"
|
||||
interactive
|
||||
trigger="click"
|
||||
theme="popover"
|
||||
arrow
|
||||
>
|
||||
<template #trigger>
|
||||
<ButtonSecondary
|
||||
icon="help_center"
|
||||
class="rounded-none"
|
||||
:label="$t('app.help')"
|
||||
/>
|
||||
</template>
|
||||
<div class="flex flex-col">
|
||||
<SmartItem
|
||||
:label="$t('app.documentation')"
|
||||
to="https://docs.hoppscotch.io"
|
||||
blank
|
||||
@click.native="$refs.options.tippy().hide()"
|
||||
/>
|
||||
<SmartItem
|
||||
:label="$t('app.keyboard_shortcuts')"
|
||||
@click.native="
|
||||
showShortcuts = true
|
||||
$refs.options.tippy().hide()
|
||||
"
|
||||
/>
|
||||
<SmartItem
|
||||
:label="$t('app.whats_new')"
|
||||
to="https://docs.hoppscotch.io/changelog"
|
||||
blank
|
||||
@click.native="$refs.options.tippy().hide()"
|
||||
/>
|
||||
<SmartItem
|
||||
:label="$t('app.chat_with_us')"
|
||||
@click.native="
|
||||
chatWithUs()
|
||||
$refs.options.tippy().hide()
|
||||
"
|
||||
/>
|
||||
<hr />
|
||||
<SmartItem
|
||||
:label="$t('app.twitter')"
|
||||
to="https://twitter.com/hoppscotch_io"
|
||||
blank
|
||||
@click.native="$refs.options.tippy().hide()"
|
||||
/>
|
||||
<SmartItem
|
||||
:label="$t('app.terms_and_privacy')"
|
||||
to="https://docs.hoppscotch.io/privacy"
|
||||
blank
|
||||
@click.native="$refs.options.tippy().hide()"
|
||||
/>
|
||||
<!-- <SmartItem :label="$t('app.status')" /> -->
|
||||
<div class="flex opacity-50 py-2 px-4">
|
||||
{{ `${$t("app.name")} ${$t("app.version")}` }}
|
||||
</div>
|
||||
</div>
|
||||
</tippy>
|
||||
</span>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
icon="keyboard"
|
||||
:title="$t('app.shortcuts')"
|
||||
@click.native="showShortcuts = true"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-if="navigatorShare"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
icon="share"
|
||||
:title="$t('request.share')"
|
||||
@click.native="nativeShare()"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="RIGHT_SIDEBAR ? $t('hide.sidebar') : $t('show.sidebar')"
|
||||
icon="menu_open"
|
||||
class="transform rotate-180"
|
||||
:class="{ 'rotate-360': !RIGHT_SIDEBAR }"
|
||||
@click.native="RIGHT_SIDEBAR = !RIGHT_SIDEBAR"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<AppShortcuts :show="showShortcuts" @close="showShortcuts = false" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from "@nuxtjs/composition-api"
|
||||
import { defineActionHandler } from "~/helpers/actions"
|
||||
import { showChat } from "~/helpers/support"
|
||||
import { useSetting } from "~/newstore/settings"
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const showShortcuts = ref(false)
|
||||
|
||||
defineActionHandler("flyouts.keybinds.toggle", () => {
|
||||
showShortcuts.value = !showShortcuts.value
|
||||
})
|
||||
|
||||
return {
|
||||
LEFT_SIDEBAR: useSetting("LEFT_SIDEBAR"),
|
||||
RIGHT_SIDEBAR: useSetting("RIGHT_SIDEBAR"),
|
||||
ZEN_MODE: useSetting("ZEN_MODE"),
|
||||
|
||||
navigatorShare: !!navigator.share,
|
||||
|
||||
showShortcuts,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
ZEN_MODE() {
|
||||
this.LEFT_SIDEBAR = !this.ZEN_MODE
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
nativeShare() {
|
||||
if (navigator.share) {
|
||||
navigator
|
||||
.share({
|
||||
title: "Hoppscotch",
|
||||
text: "Hoppscotch • Open source API development ecosystem - Helps you create requests faster, saving precious time on development.",
|
||||
url: "https://hoppscotch.io",
|
||||
})
|
||||
.then(() => {})
|
||||
.catch(console.error)
|
||||
} else {
|
||||
// fallback
|
||||
}
|
||||
},
|
||||
chatWithUs() {
|
||||
showChat()
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -1,137 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<header
|
||||
class="flex space-x-2 flex-1 py-2 px-2 items-center justify-between"
|
||||
>
|
||||
<div class="space-x-2 inline-flex items-center">
|
||||
<ButtonSecondary
|
||||
class="tracking-wide !font-bold !text-secondaryDark"
|
||||
label="HOPPSCOTCH"
|
||||
to="/"
|
||||
/>
|
||||
<AppGitHubStarButton class="mt-1.5 transition" />
|
||||
</div>
|
||||
<div class="space-x-2 inline-flex items-center">
|
||||
<ButtonSecondary
|
||||
id="installPWA"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('header.install_pwa')"
|
||||
icon="download_for_offline"
|
||||
class="rounded"
|
||||
@click.native="showInstallPrompt()"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-if="currentUser === null"
|
||||
icon="filter_drama"
|
||||
:label="$t('header.save_workspace')"
|
||||
filled
|
||||
class="hidden !font-semibold md:flex"
|
||||
@click.native="showLogin = true"
|
||||
/>
|
||||
<ButtonPrimary
|
||||
v-if="currentUser === null"
|
||||
:label="$t('header.login')"
|
||||
@click.native="showLogin = true"
|
||||
/>
|
||||
<span v-else class="pr-2">
|
||||
<tippy ref="user" interactive trigger="click" theme="popover" arrow>
|
||||
<template #trigger>
|
||||
<ProfilePicture
|
||||
v-if="currentUser.photoURL"
|
||||
v-tippy="{
|
||||
theme: 'tooltip',
|
||||
}"
|
||||
:url="currentUser.photoURL"
|
||||
:alt="currentUser.displayName"
|
||||
:title="currentUser.displayName"
|
||||
indicator
|
||||
:indicator-styles="isOnLine ? 'bg-green-500' : 'bg-red-500'"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-else
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('header.account')"
|
||||
class="rounded"
|
||||
icon="account_circle"
|
||||
/>
|
||||
</template>
|
||||
<SmartItem
|
||||
to="/settings"
|
||||
icon="settings"
|
||||
:label="$t('navigation.settings')"
|
||||
@click.native="$refs.user.tippy().hide()"
|
||||
/>
|
||||
<FirebaseLogout @confirm-logout="$refs.user.tippy().hide()" />
|
||||
</tippy>
|
||||
</span>
|
||||
</div>
|
||||
</header>
|
||||
<AppAnnouncement v-if="!isOnLine" />
|
||||
<FirebaseLogin :show="showLogin" @hide-modal="showLogin = false" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import intializePwa from "~/helpers/pwa"
|
||||
import { currentUser$ } from "~/helpers/fb/auth"
|
||||
import { getLocalConfig, setLocalConfig } from "~/newstore/localpersistence"
|
||||
import { useReadonlyStream } from "~/helpers/utils/composables"
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
return {
|
||||
currentUser: useReadonlyStream(currentUser$, null),
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// Once the PWA code is initialized, this holds a method
|
||||
// that can be called to show the user the installation
|
||||
// prompt.
|
||||
showInstallPrompt: null,
|
||||
showLogin: false,
|
||||
isOnLine: navigator.onLine,
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
window.addEventListener("online", () => {
|
||||
this.isOnLine = true
|
||||
})
|
||||
window.addEventListener("offline", () => {
|
||||
this.isOnLine = false
|
||||
})
|
||||
|
||||
// Initializes the PWA code - checks if the app is installed,
|
||||
// etc.
|
||||
this.showInstallPrompt = await intializePwa()
|
||||
|
||||
const cookiesAllowed = getLocalConfig("cookiesAllowed") === "yes"
|
||||
if (!cookiesAllowed) {
|
||||
this.$toast.show(this.$t("app.we_use_cookies").toString(), {
|
||||
icon: "cookie",
|
||||
duration: 0,
|
||||
action: [
|
||||
{
|
||||
text: this.$t("action.learn_more").toString(),
|
||||
onClick: (_, toastObject) => {
|
||||
setLocalConfig("cookiesAllowed", "yes")
|
||||
toastObject.goAway(0)
|
||||
window
|
||||
.open("https://docs.hoppscotch.io/privacy", "_blank")
|
||||
.focus()
|
||||
},
|
||||
},
|
||||
{
|
||||
text: this.$t("action.dismiss").toString(),
|
||||
onClick: (_, toastObject) => {
|
||||
setLocalConfig("cookiesAllowed", "yes")
|
||||
toastObject.goAway(0)
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -1,123 +0,0 @@
|
||||
<template>
|
||||
<SmartModal v-if="show" @close="$emit('hide-modal')">
|
||||
<template #body>
|
||||
<input
|
||||
id="command"
|
||||
v-model="search"
|
||||
v-focus
|
||||
type="text"
|
||||
name="command"
|
||||
:placeholder="$t('app.type_a_command_search')"
|
||||
class="
|
||||
bg-transparent
|
||||
border-b border-dividerLight
|
||||
text-secondaryDark text-base
|
||||
leading-normal
|
||||
px-4
|
||||
pt-2
|
||||
pb-6
|
||||
"
|
||||
/>
|
||||
<div
|
||||
class="
|
||||
divide-y divide-dividerLight
|
||||
flex flex-col
|
||||
space-y-4
|
||||
flex-1
|
||||
overflow-auto
|
||||
hide-scrollbar
|
||||
"
|
||||
>
|
||||
<div
|
||||
v-for="(map, mapIndex) in filteredMappings"
|
||||
:key="`map-${mapIndex}`"
|
||||
>
|
||||
<h5 class="my-2 text-secondaryLight py-2 px-4">
|
||||
{{ $t(map.section) }}
|
||||
</h5>
|
||||
<div
|
||||
v-for="(shortcut, shortcutIndex) in map.shortcuts"
|
||||
:key="`map-${mapIndex}-shortcut-${shortcutIndex}`"
|
||||
class="
|
||||
rounded
|
||||
cursor-pointer
|
||||
flex
|
||||
py-2
|
||||
px-4
|
||||
transition
|
||||
items-center
|
||||
group
|
||||
hover:bg-primaryLight
|
||||
"
|
||||
@click="
|
||||
runAction(shortcut.action)
|
||||
hideModal()
|
||||
"
|
||||
>
|
||||
<i class="mr-4 opacity-75 material-icons group-hover:opacity-100">
|
||||
{{ shortcut.icon }}
|
||||
</i>
|
||||
<span class="flex flex-1 mr-4 group-hover:text-secondaryDark">
|
||||
{{ $t(shortcut.label) }}
|
||||
</span>
|
||||
<span
|
||||
v-for="(key, keyIndex) in shortcut.keys"
|
||||
:key="`map-${mapIndex}-shortcut-${shortcutIndex}-key-${keyIndex}`"
|
||||
class="shortcut-key"
|
||||
>
|
||||
{{ key }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</SmartModal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import { invokeAction } from "~/helpers/actions"
|
||||
import { spotlight } from "~/helpers/shortcuts"
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
show: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
search: "",
|
||||
mappings: spotlight,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
filteredMappings() {
|
||||
return this.mappings.filter((mapping) =>
|
||||
mapping.shortcuts.some((shortcut) =>
|
||||
shortcut.keywords.some((keyword) =>
|
||||
keyword.toLowerCase().includes(this.search.toLowerCase())
|
||||
)
|
||||
)
|
||||
)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
hideModal() {
|
||||
this.$emit("hide-modal")
|
||||
},
|
||||
runAction(command) {
|
||||
invokeAction(command, "path_from_invokeAction")
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.shortcut-key {
|
||||
@apply bg-dividerLight;
|
||||
@apply rounded;
|
||||
@apply ml-2;
|
||||
@apply py-1;
|
||||
@apply px-2;
|
||||
@apply inline-flex;
|
||||
}
|
||||
</style>
|
||||
@@ -1,18 +0,0 @@
|
||||
<template>
|
||||
<section :id="label.toLowerCase()" class="flex flex-col flex-1 relative">
|
||||
<slot></slot>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
default: "Section",
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -1,125 +0,0 @@
|
||||
<template>
|
||||
<SmartModal
|
||||
v-if="show"
|
||||
:title="$t('app.invite_your_friends')"
|
||||
@close="$emit('hide-modal')"
|
||||
>
|
||||
<template #body>
|
||||
<p class="text-secondaryLight mb-8 px-2">
|
||||
{{ $t("app.invite_description") }}
|
||||
</p>
|
||||
<div class="flex flex-col space-y-2 px-2">
|
||||
<div class="grid gap-4 grid-cols-3">
|
||||
<a
|
||||
v-for="(platform, index) in platforms"
|
||||
:key="`platform-${index}`"
|
||||
:href="platform.link"
|
||||
target="_blank"
|
||||
class="share-link"
|
||||
>
|
||||
<SmartIcon :name="platform.icon" class="h-6 w-6" />
|
||||
<span class="mt-3">
|
||||
{{ platform.name }}
|
||||
</span>
|
||||
</a>
|
||||
<button class="share-link" @click="copyAppLink">
|
||||
<span class="font-icon h-6 text-xl w-6">{{ copyIcon }}</span>
|
||||
<span class="mt-3">
|
||||
{{ $t("app.copy") }}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</SmartModal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
show: Boolean,
|
||||
},
|
||||
data() {
|
||||
const url = "https://hoppscotch.io"
|
||||
const text = "Hoppscotch - Open source API development ecosystem."
|
||||
const description =
|
||||
"Helps you create requests faster, saving precious time on development."
|
||||
const subject =
|
||||
"Checkout Hoppscotch - an open source API development ecosystem"
|
||||
const summary = `Hi there!%0D%0A%0D%0AI thought you’ll like this new platform that I joined called Hoppscotch - https://hoppscotch.io.%0D%0AIt is a simple and intuitive interface for creating and managing your APIs. You can build, test, document, and share your APIs.%0D%0A%0D%0AThe best part about Hoppscotch is that it is open source and free to get started.%0D%0A%0D%0A`
|
||||
const twitter = "hoppscotch_io"
|
||||
|
||||
return {
|
||||
url: "https://hoppscotch.io",
|
||||
copyIcon: "content_copy",
|
||||
platforms: [
|
||||
{
|
||||
name: "Email",
|
||||
icon: "email",
|
||||
link: `mailto:?subject=${subject}&body=${summary}`,
|
||||
},
|
||||
{
|
||||
name: "Twitter",
|
||||
icon: "twitter",
|
||||
link: `https://twitter.com/intent/tweet?text=${text} ${description}&url=${url}&via=${twitter}`,
|
||||
},
|
||||
{
|
||||
name: "Facebook",
|
||||
icon: "facebook",
|
||||
link: `https://www.facebook.com/sharer/sharer.php?u=${url}`,
|
||||
},
|
||||
{
|
||||
name: "Reddit",
|
||||
icon: "reddit",
|
||||
link: `https://www.reddit.com/submit?url=${url}&title=${text}`,
|
||||
},
|
||||
{
|
||||
name: "LinkedIn",
|
||||
icon: "linkedin",
|
||||
link: `https://www.linkedin.com/sharing/share-offsite/?url=${url}`,
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
copyAppLink() {
|
||||
copyToClipboard(this.url)
|
||||
this.copyIcon = "done"
|
||||
this.$toast.success(this.$t("state.copied_to_clipboard").toString(), {
|
||||
icon: "content_paste",
|
||||
})
|
||||
setTimeout(() => (this.copyIcon = "content_copy"), 1000)
|
||||
},
|
||||
hideModal() {
|
||||
this.$emit("hide-modal")
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.share-link {
|
||||
@apply border border-dividerLight;
|
||||
@apply rounded;
|
||||
@apply flex-col flex;
|
||||
@apply p-4;
|
||||
@apply items-center;
|
||||
@apply justify-center;
|
||||
@apply hover:(bg-primaryLight text-secondaryDark);
|
||||
@apply focus:outline-none;
|
||||
@apply focus-visible:border-divider;
|
||||
|
||||
svg {
|
||||
@apply opacity-80;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
svg {
|
||||
@apply opacity-100;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,120 +0,0 @@
|
||||
<template>
|
||||
<AppSlideOver :show="show" @close="close()">
|
||||
<template #content>
|
||||
<div
|
||||
class="
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex
|
||||
p-2
|
||||
top-0
|
||||
z-10
|
||||
items-center
|
||||
sticky
|
||||
justify-between
|
||||
"
|
||||
>
|
||||
<h3 class="ml-4 heading">{{ $t("app.shortcuts") }}</h3>
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
icon="close"
|
||||
class="rounded"
|
||||
@click.native="close()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bg-primary border-b border-dividerLight">
|
||||
<div class="flex flex-col my-4 mx-6 search-wrapper">
|
||||
<input
|
||||
v-model="filterText"
|
||||
type="search"
|
||||
class="
|
||||
bg-primaryLight
|
||||
border border-dividerLight
|
||||
rounded
|
||||
flex
|
||||
w-full
|
||||
py-2
|
||||
pr-2
|
||||
pl-8
|
||||
focus-visible:border-divider
|
||||
"
|
||||
:placeholder="$t('action.search')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="
|
||||
divide-y divide-dividerLight
|
||||
flex flex-col flex-1
|
||||
overflow-auto
|
||||
hide-scrollbar
|
||||
"
|
||||
>
|
||||
<div
|
||||
v-for="(map, mapIndex) in mappings"
|
||||
:key="`map-${mapIndex}`"
|
||||
class="space-y-4 py-4 px-6"
|
||||
>
|
||||
<h1 class="font-semibold text-secondaryDark">
|
||||
{{ $t(map.section) }}
|
||||
</h1>
|
||||
<div
|
||||
v-for="(shortcut, shortcutIndex) in map.shortcuts"
|
||||
:key="`map-${mapIndex}-shortcut-${shortcutIndex}`"
|
||||
class="flex items-center"
|
||||
>
|
||||
<span class="flex flex-1 mr-4">
|
||||
{{ $t(shortcut.label) }}
|
||||
</span>
|
||||
<span
|
||||
v-for="(key, keyIndex) in shortcut.keys"
|
||||
:key="`map-${mapIndex}-shortcut-${shortcutIndex}-key-${keyIndex}`"
|
||||
class="shortcut-key"
|
||||
>
|
||||
{{ key }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</AppSlideOver>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import shortcuts from "~/helpers/shortcuts"
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
show: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
filterText: "",
|
||||
mappings: shortcuts,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
$route() {
|
||||
this.$emit("close")
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.$emit("close")
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.shortcut-key {
|
||||
@apply bg-dividerLight;
|
||||
@apply rounded;
|
||||
@apply ml-2;
|
||||
@apply py-1;
|
||||
@apply px-2;
|
||||
@apply inline-flex;
|
||||
}
|
||||
</style>
|
||||
@@ -1,178 +0,0 @@
|
||||
<template>
|
||||
<aside class="flex h-full justify-between md:flex-col">
|
||||
<nav class="flex flex-nowrap md:flex-col">
|
||||
<NuxtLink
|
||||
v-for="(navigation, index) in primaryNavigation"
|
||||
:key="`navigation-${index}`"
|
||||
:to="localePath(navigation.target)"
|
||||
class="nav-link"
|
||||
tabindex="0"
|
||||
>
|
||||
<i v-if="navigation.icon" class="material-icons">
|
||||
{{ navigation.icon }}
|
||||
</i>
|
||||
<div v-if="navigation.svg" class="h-4 w-4">
|
||||
<SmartIcon :name="navigation.svg" class="svg-icons" />
|
||||
</div>
|
||||
<span v-if="LEFT_SIDEBAR">{{ navigation.title }}</span>
|
||||
</NuxtLink>
|
||||
</nav>
|
||||
<!-- <nav
|
||||
class="
|
||||
flex flex-nowrap
|
||||
p-4
|
||||
items-center
|
||||
justify-center
|
||||
md:(flex-col
|
||||
space-x-0 space-y-2)
|
||||
"
|
||||
>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip', placement: 'top' }"
|
||||
:title="`${$t('app.search')} <kbd>/</kbd>`"
|
||||
icon="search"
|
||||
class="rounded"
|
||||
@click.native="showSearch = true"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip', placement: 'top' }"
|
||||
:title="$t('app.invite')"
|
||||
icon="person_add_alt"
|
||||
class="rounded"
|
||||
@click.native="showShare = true"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip', placement: 'top' }"
|
||||
:title="`${$t('support.title')} <kbd>?</kbd>`"
|
||||
icon="support"
|
||||
class="rounded"
|
||||
@click.native="showSupport = true"
|
||||
/>
|
||||
</nav> -->
|
||||
<!-- <AppSearch :show="showSearch" @hide-modal="showSearch = false" />
|
||||
<AppSupport :show="showSupport" @hide-modal="showSupport = false" />
|
||||
<AppShare :show="showShare" @hide-modal="showShare = false" /> -->
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import { useSetting } from "~/newstore/settings"
|
||||
// import { defineActionHandler } from "~/helpers/actions"
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
// const showSearch = ref(false)
|
||||
// const showSupport = ref(false)
|
||||
// const showShare = ref(false)
|
||||
|
||||
// defineActionHandler("modals.search.toggle", () => {
|
||||
// showSearch.value = !showSearch.value
|
||||
// })
|
||||
|
||||
// defineActionHandler("modals.support.toggle", () => {
|
||||
// showSupport.value = !showSupport.value
|
||||
// })
|
||||
|
||||
// defineActionHandler("modals.share.toggle", () => {
|
||||
// showShare.value = !showShare.value
|
||||
// })
|
||||
|
||||
return {
|
||||
// showSearch,
|
||||
// showSupport,
|
||||
// showShare,
|
||||
LEFT_SIDEBAR: useSetting("LEFT_SIDEBAR"),
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
primaryNavigation: [
|
||||
{
|
||||
target: "index",
|
||||
icon: "settings_ethernet",
|
||||
title: this.$t("navigation.rest"),
|
||||
},
|
||||
{
|
||||
target: "graphql",
|
||||
svg: "graphql",
|
||||
title: this.$t("navigation.graphql"),
|
||||
},
|
||||
{
|
||||
target: "realtime",
|
||||
icon: "language",
|
||||
title: this.$t("navigation.realtime"),
|
||||
},
|
||||
{
|
||||
target: "documentation",
|
||||
icon: "book",
|
||||
title: this.$t("navigation.doc"),
|
||||
},
|
||||
{
|
||||
target: "settings",
|
||||
icon: "settings",
|
||||
title: this.$t("navigation.settings"),
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.nav-link {
|
||||
@apply relative;
|
||||
@apply p-4;
|
||||
@apply flex flex-col flex-1;
|
||||
@apply items-center;
|
||||
@apply justify-center;
|
||||
@apply hover:(bg-primaryDark text-secondaryDark);
|
||||
@apply focus-visible:text-secondaryDark;
|
||||
|
||||
&::after {
|
||||
@apply absolute;
|
||||
@apply inset-x-0;
|
||||
@apply md:inset-x-auto;
|
||||
@apply md:inset-y-0;
|
||||
@apply bottom-0;
|
||||
@apply md:bottom-auto;
|
||||
@apply md:left-0;
|
||||
@apply z-2;
|
||||
@apply h-0.5;
|
||||
@apply md:h-full;
|
||||
@apply w-full;
|
||||
@apply md:w-0.5;
|
||||
|
||||
content: "";
|
||||
}
|
||||
|
||||
&:focus::after {
|
||||
@apply bg-divider;
|
||||
}
|
||||
|
||||
.material-icons,
|
||||
.svg-icons {
|
||||
@apply opacity-75;
|
||||
}
|
||||
|
||||
span {
|
||||
@apply mt-2;
|
||||
@apply font-font-medium;
|
||||
}
|
||||
|
||||
&.exact-active-link {
|
||||
@apply text-secondaryDark;
|
||||
@apply bg-primaryLight;
|
||||
@apply hover:text-secondaryDark;
|
||||
|
||||
.material-icons,
|
||||
.svg-icons {
|
||||
@apply opacity-100;
|
||||
}
|
||||
|
||||
&::after {
|
||||
@apply bg-accent;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,69 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<transition v-if="show" name="fade" appear>
|
||||
<div class="inset-0 transition-opacity z-20 fixed" @keydown.esc="close()">
|
||||
<div
|
||||
class="bg-primaryDark opacity-90 inset-0 absolute"
|
||||
tabindex="0"
|
||||
@click="close()"
|
||||
></div>
|
||||
</div>
|
||||
</transition>
|
||||
<aside
|
||||
class="
|
||||
bg-primary
|
||||
flex flex-col
|
||||
h-full
|
||||
max-w-full
|
||||
transform
|
||||
transition
|
||||
top-0
|
||||
ease-in-out
|
||||
right-0
|
||||
w-96
|
||||
z-30
|
||||
duration-300
|
||||
fixed
|
||||
overflow-auto
|
||||
"
|
||||
:class="show ? 'shadow-xl translate-x-0' : 'translate-x-full'"
|
||||
>
|
||||
<slot name="content"></slot>
|
||||
</aside>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
show: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
show: {
|
||||
immediate: true,
|
||||
handler(show) {
|
||||
if (process.client) {
|
||||
if (show) document.body.style.setProperty("overflow", "hidden")
|
||||
else document.body.style.removeProperty("overflow")
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener("keydown", (e) => {
|
||||
if (e.keyCode === 27 && this.show) this.close()
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.$emit("close")
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -1,242 +0,0 @@
|
||||
<template>
|
||||
<SmartModal v-if="show" :title="$t('collection.save_as')" @close="hideModal">
|
||||
<template #body>
|
||||
<div class="flex flex-col px-2">
|
||||
<div class="flex relative">
|
||||
<input
|
||||
id="selectLabelSaveReq"
|
||||
v-model="requestName"
|
||||
v-focus
|
||||
class="input floating-input"
|
||||
placeholder=" "
|
||||
type="text"
|
||||
@keyup.enter="saveRequestAs"
|
||||
/>
|
||||
<label for="selectLabelSaveReq">
|
||||
{{ $t("request.name") }}
|
||||
</label>
|
||||
</div>
|
||||
<label class="px-4 pt-4 pb-4">
|
||||
{{ $t("collection.select_location") }}
|
||||
</label>
|
||||
<CollectionsGraphql
|
||||
v-if="mode === 'graphql'"
|
||||
:doc="false"
|
||||
:show-coll-actions="false"
|
||||
:picked="picked"
|
||||
:saving-mode="true"
|
||||
@select="onSelect"
|
||||
/>
|
||||
<Collections
|
||||
v-else
|
||||
:picked="picked"
|
||||
:save-request="true"
|
||||
@select="onSelect"
|
||||
@update-collection="collectionsType.type = $event"
|
||||
@update-coll-type="onUpdateCollType"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #footer>
|
||||
<span>
|
||||
<ButtonPrimary
|
||||
:label="$t('action.save')"
|
||||
@click.native="saveRequestAs"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
:label="$t('action.cancel')"
|
||||
@click.native="hideModal"
|
||||
/>
|
||||
</span>
|
||||
</template>
|
||||
</SmartModal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import * as teamUtils from "~/helpers/teams/utils"
|
||||
import {
|
||||
saveRESTRequestAs,
|
||||
editRESTRequest,
|
||||
editGraphqlRequest,
|
||||
saveGraphqlRequestAs,
|
||||
} from "~/newstore/collections"
|
||||
import { getGQLSession, useGQLRequestName } from "~/newstore/GQLSession"
|
||||
import {
|
||||
getRESTRequest,
|
||||
useRESTRequestName,
|
||||
setRESTSaveContext,
|
||||
} from "~/newstore/RESTSession"
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
// mode can be either "graphql" or "rest"
|
||||
mode: { type: String, default: "rest" },
|
||||
show: Boolean,
|
||||
},
|
||||
setup(props) {
|
||||
return {
|
||||
requestName:
|
||||
props.mode === "rest" ? useRESTRequestName() : useGQLRequestName(),
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
requestData: {
|
||||
name: this.requestName,
|
||||
collectionIndex: undefined,
|
||||
folderName: undefined,
|
||||
requestIndex: undefined,
|
||||
},
|
||||
collectionsType: {
|
||||
type: "my-collections",
|
||||
selectedTeam: undefined,
|
||||
},
|
||||
picked: null,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
"requestData.collectionIndex": function resetFolderAndRequestIndex() {
|
||||
// if user has chosen some folder, than selected other collection, which doesn't have any folders
|
||||
// than `requestUpdateData.folderName` won't be reseted
|
||||
this.$data.requestData.folderName = undefined
|
||||
this.$data.requestData.requestIndex = undefined
|
||||
},
|
||||
"requestData.folderName": function resetRequestIndex() {
|
||||
this.$data.requestData.requestIndex = undefined
|
||||
},
|
||||
editingRequest({ name }) {
|
||||
this.$data.requestData.name = name || this.$data.defaultRequestName
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onUpdateCollType(newCollType) {
|
||||
this.collectionsType = newCollType
|
||||
},
|
||||
onSelect({ picked }) {
|
||||
this.picked = picked
|
||||
},
|
||||
async saveRequestAs() {
|
||||
if (!this.requestName) {
|
||||
this.$toast.error(this.$t("error.empty_req_name"), {
|
||||
icon: "error_outline",
|
||||
})
|
||||
return
|
||||
}
|
||||
if (this.picked == null) {
|
||||
this.$toast.error(this.$t("collection.select"), {
|
||||
icon: "error_outline",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const requestUpdated =
|
||||
this.mode === "rest" ? getRESTRequest() : getGQLSession()
|
||||
|
||||
// Filter out all REST file inputs
|
||||
if (this.mode === "rest" && requestUpdated.bodyParams) {
|
||||
requestUpdated.bodyParams = requestUpdated.bodyParams.map((param) =>
|
||||
param?.value?.[0] instanceof File ? { ...param, value: "" } : param
|
||||
)
|
||||
}
|
||||
|
||||
if (this.picked.pickedType === "my-request") {
|
||||
editRESTRequest(
|
||||
this.picked.folderPath,
|
||||
this.picked.requestIndex,
|
||||
requestUpdated
|
||||
)
|
||||
setRESTSaveContext({
|
||||
originLocation: "user-collection",
|
||||
folderPath: this.picked.folderPath,
|
||||
requestIndex: this.picked.requestIndex,
|
||||
})
|
||||
} else if (this.picked.pickedType === "my-folder") {
|
||||
const insertionIndex = saveRESTRequestAs(
|
||||
this.picked.folderPath,
|
||||
requestUpdated
|
||||
)
|
||||
setRESTSaveContext({
|
||||
originLocation: "user-collection",
|
||||
folderPath: this.picked.folderPath,
|
||||
requestIndex: insertionIndex,
|
||||
})
|
||||
} else if (this.picked.pickedType === "my-collection") {
|
||||
const insertionIndex = saveRESTRequestAs(
|
||||
`${this.picked.collectionIndex}`,
|
||||
requestUpdated
|
||||
)
|
||||
setRESTSaveContext({
|
||||
originLocation: "user-collection",
|
||||
folderPath: `${this.picked.collectionIndex}`,
|
||||
requestIndex: insertionIndex,
|
||||
})
|
||||
} else if (this.picked.pickedType === "teams-request") {
|
||||
teamUtils.overwriteRequestTeams(
|
||||
this.$apollo,
|
||||
JSON.stringify(requestUpdated),
|
||||
requestUpdated.name,
|
||||
this.picked.requestID
|
||||
)
|
||||
setRESTSaveContext({
|
||||
originLocation: "team-collection",
|
||||
requestID: this.picked.requestID,
|
||||
})
|
||||
} else if (this.picked.pickedType === "teams-folder") {
|
||||
const req = await teamUtils.saveRequestAsTeams(
|
||||
this.$apollo,
|
||||
JSON.stringify(requestUpdated),
|
||||
requestUpdated.name,
|
||||
this.collectionsType.selectedTeam.id,
|
||||
this.picked.folderID
|
||||
)
|
||||
|
||||
if (req && req.id) {
|
||||
setRESTSaveContext({
|
||||
originLocation: "team-collection",
|
||||
requestID: req.id,
|
||||
teamID: this.collectionsType.selectedTeam.id,
|
||||
collectionID: this.picked.folderID,
|
||||
})
|
||||
}
|
||||
} else if (this.picked.pickedType === "teams-collection") {
|
||||
const req = await teamUtils.saveRequestAsTeams(
|
||||
this.$apollo,
|
||||
JSON.stringify(requestUpdated),
|
||||
requestUpdated.name,
|
||||
this.collectionsType.selectedTeam.id,
|
||||
this.picked.collectionID
|
||||
)
|
||||
|
||||
if (req && req.id) {
|
||||
setRESTSaveContext({
|
||||
originLocation: "team-collection",
|
||||
requestID: req.id,
|
||||
teamID: this.collectionsType.selectedTeam.id,
|
||||
collectionID: this.picked.collectionID,
|
||||
})
|
||||
}
|
||||
} else if (this.picked.pickedType === "gql-my-request") {
|
||||
editGraphqlRequest(
|
||||
this.picked.folderPath,
|
||||
this.picked.requestIndex,
|
||||
requestUpdated
|
||||
)
|
||||
} else if (this.picked.pickedType === "gql-my-folder") {
|
||||
saveGraphqlRequestAs(this.picked.folderPath, requestUpdated)
|
||||
} else if (this.picked.pickedType === "gql-my-collection") {
|
||||
saveGraphqlRequestAs(`${this.picked.collectionIndex}`, requestUpdated)
|
||||
}
|
||||
this.$toast.success(this.$t("request.added"), {
|
||||
icon: "post_add",
|
||||
})
|
||||
|
||||
this.hideModal()
|
||||
},
|
||||
hideModal() {
|
||||
this.picked = null
|
||||
this.$emit("hide-modal")
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -1,254 +0,0 @@
|
||||
<template>
|
||||
<div class="opacity-0 show-if-initialized" :class="{ initialized }">
|
||||
<pre ref="editor" :class="styles"></pre>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ace from "ace-builds"
|
||||
import "ace-builds/webpack-resolver"
|
||||
import "ace-builds/src-noconflict/ext-language_tools"
|
||||
import "ace-builds/src-noconflict/mode-graphqlschema"
|
||||
import * as gql from "graphql"
|
||||
import { getAutocompleteSuggestions } from "graphql-language-service-interface"
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import { defineGQLLanguageMode } from "~/helpers/syntax/gqlQueryLangMode"
|
||||
import debounce from "~/helpers/utils/debounce"
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
theme: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
onRunGQLQuery: {
|
||||
type: Function,
|
||||
default: () => {},
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
styles: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
initialized: false,
|
||||
editor: null,
|
||||
cacheValue: "",
|
||||
validationSchema: null,
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
appFontSize() {
|
||||
return getComputedStyle(document.documentElement).getPropertyValue(
|
||||
"--body-font-size"
|
||||
)
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
value(value) {
|
||||
if (value !== this.cacheValue) {
|
||||
this.editor.session.setValue(value, 1)
|
||||
this.cacheValue = value
|
||||
}
|
||||
},
|
||||
theme() {
|
||||
this.initialized = false
|
||||
this.editor.setTheme(`ace/theme/${this.defineTheme()}`, () => {
|
||||
this.$nextTick().then(() => {
|
||||
this.initialized = true
|
||||
})
|
||||
})
|
||||
},
|
||||
options(value) {
|
||||
this.editor.setOptions(value)
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
defineGQLLanguageMode(ace)
|
||||
|
||||
const langTools = ace.require("ace/ext/language_tools")
|
||||
|
||||
const editor = ace.edit(this.$refs.editor, {
|
||||
mode: `ace/mode/gql-query`,
|
||||
enableBasicAutocompletion: true,
|
||||
enableLiveAutocompletion: true,
|
||||
...this.options,
|
||||
})
|
||||
|
||||
// Set the theme and show the editor only after it's been set to prevent FOUC.
|
||||
editor.setTheme(`ace/theme/${this.defineTheme()}`, () => {
|
||||
this.$nextTick().then(() => {
|
||||
this.initialized = true
|
||||
})
|
||||
})
|
||||
|
||||
// Set the theme and show the editor only after it's been set to prevent FOUC.
|
||||
editor.setTheme(`ace/theme/${this.defineTheme()}`, () => {
|
||||
this.$nextTick().then(() => {
|
||||
this.initialized = true
|
||||
})
|
||||
})
|
||||
|
||||
editor.setFontSize(this.appFontSize)
|
||||
|
||||
const completer = {
|
||||
getCompletions: (
|
||||
editor,
|
||||
_session,
|
||||
{ row, column },
|
||||
_prefix,
|
||||
callback
|
||||
) => {
|
||||
if (this.validationSchema) {
|
||||
const completions = getAutocompleteSuggestions(
|
||||
this.validationSchema,
|
||||
editor.getValue(),
|
||||
{
|
||||
line: row,
|
||||
character: column,
|
||||
}
|
||||
)
|
||||
|
||||
callback(
|
||||
null,
|
||||
completions.map(({ label, detail }) => ({
|
||||
name: label,
|
||||
value: label,
|
||||
score: 1.0,
|
||||
meta: detail,
|
||||
}))
|
||||
)
|
||||
} else {
|
||||
callback(null, [])
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
langTools.setCompleters([completer])
|
||||
|
||||
if (this.value) editor.setValue(this.value, 1)
|
||||
|
||||
this.editor = editor
|
||||
this.cacheValue = this.value
|
||||
|
||||
editor.commands.addCommand({
|
||||
name: "runGQLQuery",
|
||||
exec: () => this.onRunGQLQuery(this.editor.getValue()),
|
||||
bindKey: {
|
||||
mac: "cmd-enter",
|
||||
win: "ctrl-enter",
|
||||
},
|
||||
})
|
||||
|
||||
editor.commands.addCommand({
|
||||
name: "prettifyGQLQuery",
|
||||
exec: () => this.prettifyQuery(),
|
||||
bindKey: {
|
||||
mac: "cmd-p",
|
||||
win: "ctrl-p",
|
||||
},
|
||||
})
|
||||
|
||||
editor.on("change", () => {
|
||||
const content = editor.getValue()
|
||||
this.$emit("input", content)
|
||||
this.parseContents(content)
|
||||
this.cacheValue = content
|
||||
})
|
||||
|
||||
this.parseContents(this.value)
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
this.editor.destroy()
|
||||
},
|
||||
|
||||
methods: {
|
||||
prettifyQuery() {
|
||||
try {
|
||||
this.$emit("update-query", gql.print(gql.parse(this.editor.getValue())))
|
||||
} catch (e) {
|
||||
this.$toast.error(this.$t("error.gql_prettify_invalid_query"), {
|
||||
icon: "error_outline",
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
defineTheme() {
|
||||
if (this.theme) {
|
||||
return this.theme
|
||||
}
|
||||
const strip = (str) =>
|
||||
str.replace(/#/g, "").replace(/ /g, "").replace(/"/g, "")
|
||||
return strip(
|
||||
window
|
||||
.getComputedStyle(document.documentElement)
|
||||
.getPropertyValue("--editor-theme")
|
||||
)
|
||||
},
|
||||
|
||||
setValidationSchema(schema) {
|
||||
this.validationSchema = schema
|
||||
this.parseContents(this.cacheValue)
|
||||
},
|
||||
|
||||
parseContents: debounce(function (content) {
|
||||
if (content !== "") {
|
||||
try {
|
||||
const doc = gql.parse(content)
|
||||
|
||||
if (this.validationSchema) {
|
||||
this.editor.session.setAnnotations(
|
||||
gql
|
||||
.validate(this.validationSchema, doc)
|
||||
.map(({ locations, message }) => ({
|
||||
row: locations[0].line - 1,
|
||||
column: locations[0].column - 1,
|
||||
text: message,
|
||||
type: "error",
|
||||
}))
|
||||
)
|
||||
}
|
||||
} catch (e) {
|
||||
this.editor.session.setAnnotations([
|
||||
{
|
||||
row: e.locations[0].line - 1,
|
||||
column: e.locations[0].column - 1,
|
||||
text: e.message,
|
||||
type: "error",
|
||||
},
|
||||
])
|
||||
}
|
||||
} else {
|
||||
this.editor.session.setAnnotations([])
|
||||
}
|
||||
}, 2000),
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.show-if-initialized {
|
||||
&.initialized {
|
||||
@apply opacity-100;
|
||||
}
|
||||
|
||||
& > * {
|
||||
@apply transition-none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,481 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<SmartTabs styles="sticky top-upperPrimaryStickyFold z-10">
|
||||
<SmartTab :id="'query'" :label="$t('tab.query')" :selected="true">
|
||||
<AppSection label="query">
|
||||
<div
|
||||
class="
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
top-upperSecondaryStickyFold
|
||||
pl-4
|
||||
z-10
|
||||
sticky
|
||||
items-center
|
||||
justify-between
|
||||
gqlRunQuery
|
||||
"
|
||||
>
|
||||
<label class="font-semibold text-secondaryLight">
|
||||
{{ $t("request.query") }}
|
||||
</label>
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
:label="$t('request.run')"
|
||||
icon="play_arrow"
|
||||
class="rounded-none !text-accent"
|
||||
@click.native="runQuery()"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('action.copy')"
|
||||
:icon="copyQueryIcon"
|
||||
@click.native="copyQuery"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="`${$t(
|
||||
'action.prettify'
|
||||
)} <kbd>${getSpecialKey()}</kbd><kbd>P</kbd>`"
|
||||
:icon="prettifyQueryIcon"
|
||||
@click.native="prettifyQuery"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
ref="saveRequest"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('request.save')"
|
||||
icon="create_new_folder"
|
||||
@click.native="saveRequest"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<GraphqlQueryEditor
|
||||
ref="queryEditor"
|
||||
v-model="gqlQueryString"
|
||||
:on-run-g-q-l-query="runQuery"
|
||||
:options="{
|
||||
maxLines: Infinity,
|
||||
minLines: 16,
|
||||
autoScrollEditorIntoView: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false,
|
||||
}"
|
||||
styles="border-b border-dividerLight"
|
||||
@update-query="updateQuery"
|
||||
/>
|
||||
</AppSection>
|
||||
</SmartTab>
|
||||
|
||||
<SmartTab :id="'variables'" :label="$t('tab.variables')">
|
||||
<AppSection label="variables">
|
||||
<div
|
||||
class="
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
top-upperSecondaryStickyFold
|
||||
pl-4
|
||||
z-10
|
||||
sticky
|
||||
items-center
|
||||
justify-between
|
||||
"
|
||||
>
|
||||
<label class="font-semibold text-secondaryLight">
|
||||
{{ $t("request.variables") }}
|
||||
</label>
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('action.copy')"
|
||||
:icon="copyVariablesIcon"
|
||||
@click.native="copyVariables"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<SmartAceEditor
|
||||
ref="variableEditor"
|
||||
v-model="variableString"
|
||||
:lang="'json'"
|
||||
:options="{
|
||||
maxLines: Infinity,
|
||||
minLines: 16,
|
||||
autoScrollEditorIntoView: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false,
|
||||
}"
|
||||
styles="border-b border-dividerLight"
|
||||
/>
|
||||
</AppSection>
|
||||
</SmartTab>
|
||||
|
||||
<SmartTab :id="'headers'" :label="$t('tab.headers')">
|
||||
<AppSection label="headers">
|
||||
<div
|
||||
class="
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
top-upperSecondaryStickyFold
|
||||
pl-4
|
||||
z-10
|
||||
sticky
|
||||
items-center
|
||||
justify-between
|
||||
"
|
||||
>
|
||||
<label class="font-semibold text-secondaryLight">
|
||||
{{ $t("tab.headers") }}
|
||||
</label>
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('action.clear_all')"
|
||||
icon="clear_all"
|
||||
@click.native="headers = []"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('add.new')"
|
||||
icon="add"
|
||||
@click.native="addRequestHeader"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="(header, index) in headers"
|
||||
:key="`header-${index}`"
|
||||
class="
|
||||
divide-x divide-dividerLight
|
||||
border-b border-dividerLight
|
||||
flex
|
||||
"
|
||||
>
|
||||
<SmartAutoComplete
|
||||
:placeholder="$t('count.header', { count: index + 1 })"
|
||||
:source="commonHeaders"
|
||||
:spellcheck="false"
|
||||
:value="header.key"
|
||||
autofocus
|
||||
styles="
|
||||
bg-transparent
|
||||
flex
|
||||
flex-1
|
||||
py-1
|
||||
px-4
|
||||
truncate
|
||||
focus:outline-none
|
||||
"
|
||||
@input="
|
||||
updateGQLHeader(index, {
|
||||
key: $event,
|
||||
value: header.value,
|
||||
active: header.active,
|
||||
})
|
||||
"
|
||||
/>
|
||||
<input
|
||||
class="bg-transparent flex flex-1 py-2 px-4"
|
||||
:placeholder="$t('count.value', { count: index + 1 })"
|
||||
:name="`value ${index}`"
|
||||
:value="header.value"
|
||||
autofocus
|
||||
@change="
|
||||
updateGQLHeader(index, {
|
||||
key: header.key,
|
||||
value: $event.target.value,
|
||||
active: header.active,
|
||||
})
|
||||
"
|
||||
/>
|
||||
<span>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="
|
||||
header.hasOwnProperty('active')
|
||||
? header.active
|
||||
? $t('action.turn_off')
|
||||
: $t('action.turn_on')
|
||||
: $t('action.turn_off')
|
||||
"
|
||||
:icon="
|
||||
header.hasOwnProperty('active')
|
||||
? header.active
|
||||
? 'check_circle_outline'
|
||||
: 'radio_button_unchecked'
|
||||
: 'check_circle_outline'
|
||||
"
|
||||
color="green"
|
||||
@click.native="
|
||||
updateGQLHeader(index, {
|
||||
key: header.key,
|
||||
value: header.value,
|
||||
active: !header.active,
|
||||
})
|
||||
"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('action.remove')"
|
||||
icon="remove_circle_outline"
|
||||
color="red"
|
||||
@click.native="removeRequestHeader(index)"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="headers.length === 0"
|
||||
class="
|
||||
flex flex-col
|
||||
text-secondaryLight
|
||||
p-4
|
||||
items-center
|
||||
justify-center
|
||||
"
|
||||
>
|
||||
<span class="text-center pb-4">
|
||||
{{ $t("empty.headers") }}
|
||||
</span>
|
||||
<ButtonSecondary
|
||||
:label="$t('add.new')"
|
||||
filled
|
||||
icon="add"
|
||||
@click.native="addRequestHeader"
|
||||
/>
|
||||
</div>
|
||||
</AppSection>
|
||||
</SmartTab>
|
||||
</SmartTabs>
|
||||
|
||||
<CollectionsSaveRequest
|
||||
mode="graphql"
|
||||
:show="showSaveRequestModal"
|
||||
@hide-modal="hideRequestModal"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {
|
||||
defineComponent,
|
||||
onMounted,
|
||||
PropType,
|
||||
ref,
|
||||
useContext,
|
||||
watch,
|
||||
} from "@nuxtjs/composition-api"
|
||||
import clone from "lodash/clone"
|
||||
import { getPlatformSpecialKey } from "~/helpers/platformutils"
|
||||
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
||||
import {
|
||||
useNuxt,
|
||||
useReadonlyStream,
|
||||
useStream,
|
||||
} from "~/helpers/utils/composables"
|
||||
import {
|
||||
addGQLHeader,
|
||||
gqlHeaders$,
|
||||
gqlQuery$,
|
||||
gqlResponse$,
|
||||
gqlURL$,
|
||||
gqlVariables$,
|
||||
removeGQLHeader,
|
||||
setGQLHeaders,
|
||||
setGQLQuery,
|
||||
setGQLResponse,
|
||||
setGQLVariables,
|
||||
updateGQLHeader,
|
||||
} from "~/newstore/GQLSession"
|
||||
import { commonHeaders } from "~/helpers/headers"
|
||||
import { GQLConnection } from "~/helpers/GQLConnection"
|
||||
import { makeGQLHistoryEntry, addGraphqlHistoryEntry } from "~/newstore/history"
|
||||
import { logHoppRequestRunToAnalytics } from "~/helpers/fb/analytics"
|
||||
import { getCurrentStrategyID } from "~/helpers/network"
|
||||
import { makeGQLRequest } from "~/helpers/types/HoppGQLRequest"
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
conn: {
|
||||
type: Object as PropType<GQLConnection>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const {
|
||||
$toast,
|
||||
app: { i18n },
|
||||
} = useContext()
|
||||
const t = i18n.t.bind(i18n)
|
||||
const nuxt = useNuxt()
|
||||
|
||||
const url = useReadonlyStream(gqlURL$, "")
|
||||
const gqlQueryString = useStream(gqlQuery$, "", setGQLQuery)
|
||||
const variableString = useStream(gqlVariables$, "", setGQLVariables)
|
||||
const headers = useStream(gqlHeaders$, [], setGQLHeaders)
|
||||
|
||||
const queryEditor = ref<any | null>(null)
|
||||
|
||||
const copyQueryIcon = ref("content_copy")
|
||||
const prettifyQueryIcon = ref("photo_filter")
|
||||
const copyVariablesIcon = ref("content_copy")
|
||||
|
||||
const showSaveRequestModal = ref(false)
|
||||
|
||||
const schema = useReadonlyStream(props.conn.schemaString$, "")
|
||||
|
||||
watch(
|
||||
headers,
|
||||
() => {
|
||||
if (
|
||||
(headers.value[headers.value.length - 1]?.key !== "" ||
|
||||
headers.value[headers.value.length - 1]?.value !== "") &&
|
||||
headers.value.length
|
||||
)
|
||||
addRequestHeader()
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
if (!headers.value?.length) {
|
||||
addRequestHeader()
|
||||
}
|
||||
})
|
||||
|
||||
const copyQuery = () => {
|
||||
copyToClipboard(gqlQueryString.value)
|
||||
copyQueryIcon.value = "done"
|
||||
setTimeout(() => (copyQueryIcon.value = "content_copy"), 1000)
|
||||
}
|
||||
|
||||
const response = useStream(gqlResponse$, "", setGQLResponse)
|
||||
|
||||
const runQuery = async () => {
|
||||
const startTime = Date.now()
|
||||
|
||||
nuxt.value.$loading.start()
|
||||
response.value = t("state.loading").toString()
|
||||
|
||||
try {
|
||||
const runURL = clone(url.value)
|
||||
const runHeaders = clone(headers.value)
|
||||
const runQuery = clone(gqlQueryString.value)
|
||||
const runVariables = clone(variableString.value)
|
||||
|
||||
const responseText = await props.conn.runQuery(
|
||||
runURL,
|
||||
runHeaders,
|
||||
runQuery,
|
||||
runVariables
|
||||
)
|
||||
const duration = Date.now() - startTime
|
||||
|
||||
nuxt.value.$loading.finish()
|
||||
|
||||
response.value = JSON.stringify(JSON.parse(responseText), null, 2)
|
||||
|
||||
addGraphqlHistoryEntry(
|
||||
makeGQLHistoryEntry({
|
||||
request: makeGQLRequest({
|
||||
name: "",
|
||||
url: runURL,
|
||||
query: runQuery,
|
||||
headers: runHeaders,
|
||||
variables: runVariables,
|
||||
}),
|
||||
response: response.value,
|
||||
star: false,
|
||||
})
|
||||
)
|
||||
|
||||
$toast.success(t("state.finished_in", { duration }).toString(), {
|
||||
icon: "done",
|
||||
})
|
||||
} catch (e: any) {
|
||||
response.value = `${e}. ${t("error.check_console_details")}`
|
||||
nuxt.value.$loading.finish()
|
||||
|
||||
$toast.error(`${e} ${t("error.f12_details").toString()}`, {
|
||||
icon: "error_outline",
|
||||
})
|
||||
console.error(e)
|
||||
}
|
||||
|
||||
logHoppRequestRunToAnalytics({
|
||||
platform: "graphql-query",
|
||||
strategy: getCurrentStrategyID(),
|
||||
})
|
||||
}
|
||||
|
||||
const hideRequestModal = () => {
|
||||
showSaveRequestModal.value = false
|
||||
}
|
||||
|
||||
const prettifyQuery = () => {
|
||||
queryEditor.value.prettifyQuery()
|
||||
prettifyQueryIcon.value = "done"
|
||||
setTimeout(() => (prettifyQueryIcon.value = "photo_filter"), 1000)
|
||||
}
|
||||
|
||||
const saveRequest = () => {
|
||||
showSaveRequestModal.value = true
|
||||
}
|
||||
|
||||
// Why ?
|
||||
const updateQuery = (updatedQuery: string) => {
|
||||
gqlQueryString.value = updatedQuery
|
||||
}
|
||||
|
||||
const copyVariables = () => {
|
||||
copyToClipboard(variableString.value)
|
||||
copyVariablesIcon.value = "done"
|
||||
setTimeout(() => (copyVariablesIcon.value = "content_copy"), 1000)
|
||||
}
|
||||
|
||||
const addRequestHeader = () => {
|
||||
addGQLHeader({
|
||||
key: "",
|
||||
value: "",
|
||||
active: true,
|
||||
})
|
||||
}
|
||||
|
||||
const removeRequestHeader = (index: number) => {
|
||||
removeGQLHeader(index)
|
||||
}
|
||||
|
||||
return {
|
||||
gqlQueryString,
|
||||
variableString,
|
||||
headers,
|
||||
copyQueryIcon,
|
||||
prettifyQueryIcon,
|
||||
copyVariablesIcon,
|
||||
|
||||
queryEditor,
|
||||
|
||||
showSaveRequestModal,
|
||||
hideRequestModal,
|
||||
|
||||
schema,
|
||||
|
||||
copyQuery,
|
||||
runQuery,
|
||||
prettifyQuery,
|
||||
saveRequest,
|
||||
updateQuery,
|
||||
copyVariables,
|
||||
addRequestHeader,
|
||||
removeRequestHeader,
|
||||
|
||||
getSpecialKey: getPlatformSpecialKey,
|
||||
|
||||
commonHeaders,
|
||||
updateGQLHeader,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -1,188 +0,0 @@
|
||||
<template>
|
||||
<AppSection ref="response" label="response">
|
||||
<div
|
||||
v-if="responseString"
|
||||
class="
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
pl-4
|
||||
top-0
|
||||
z-10
|
||||
sticky
|
||||
items-center
|
||||
justify-between
|
||||
"
|
||||
>
|
||||
<label class="font-semibold text-secondaryLight">
|
||||
{{ $t("response.title") }}
|
||||
</label>
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
ref="downloadResponse"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('action.download_file')"
|
||||
:icon="downloadResponseIcon"
|
||||
@click.native="downloadResponse"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
ref="copyResponseButton"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('action.copy')"
|
||||
:icon="copyResponseIcon"
|
||||
@click.native="copyResponse"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<SmartAceEditor
|
||||
v-if="responseString"
|
||||
:value="responseString"
|
||||
:lang="'json'"
|
||||
:lint="false"
|
||||
:options="{
|
||||
maxLines: Infinity,
|
||||
minLines: 16,
|
||||
autoScrollEditorIntoView: true,
|
||||
readOnly: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false,
|
||||
}"
|
||||
styles="border-b border-dividerLight"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
class="
|
||||
flex flex-col flex-1
|
||||
text-secondaryLight
|
||||
p-4
|
||||
items-center
|
||||
justify-center
|
||||
"
|
||||
>
|
||||
<div class="flex space-x-2 pb-4">
|
||||
<div class="flex flex-col space-y-4 items-end">
|
||||
<span class="flex flex-1 items-center">
|
||||
{{ $t("shortcut.request.send_request") }}
|
||||
</span>
|
||||
<span class="flex flex-1 items-center">
|
||||
{{ $t("shortcut.general.show_all") }}
|
||||
</span>
|
||||
<!-- <span class="flex flex-1 items-center">
|
||||
{{ $t("shortcut.general.command_menu") }}
|
||||
</span>
|
||||
<span class="flex flex-1 items-center">
|
||||
{{ $t("shortcut.general.help_menu") }}
|
||||
</span> -->
|
||||
</div>
|
||||
<div class="flex flex-col space-y-4">
|
||||
<div class="flex">
|
||||
<span class="shortcut-key">{{ getSpecialKey() }}</span>
|
||||
<span class="shortcut-key">G</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<span class="shortcut-key">{{ getSpecialKey() }}</span>
|
||||
<span class="shortcut-key">K</span>
|
||||
</div>
|
||||
<!-- <div class="flex">
|
||||
<span class="shortcut-key">/</span>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<span class="shortcut-key">?</span>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
<ButtonSecondary
|
||||
:label="$t('app.documentation')"
|
||||
to="https://docs.hoppscotch.io"
|
||||
icon="open_in_new"
|
||||
blank
|
||||
outline
|
||||
reverse
|
||||
/>
|
||||
</div>
|
||||
</AppSection>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {
|
||||
defineComponent,
|
||||
PropType,
|
||||
ref,
|
||||
useContext,
|
||||
} from "@nuxtjs/composition-api"
|
||||
import { GQLConnection } from "~/helpers/GQLConnection"
|
||||
import { getPlatformSpecialKey } from "~/helpers/platformutils"
|
||||
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
||||
import { useReadonlyStream } from "~/helpers/utils/composables"
|
||||
import { gqlResponse$ } from "~/newstore/GQLSession"
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
conn: {
|
||||
type: Object as PropType<GQLConnection>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const {
|
||||
$toast,
|
||||
app: { i18n },
|
||||
} = useContext()
|
||||
const t = i18n.t.bind(i18n)
|
||||
|
||||
const responseString = useReadonlyStream(gqlResponse$, "")
|
||||
|
||||
const downloadResponseIcon = ref("save_alt")
|
||||
const copyResponseIcon = ref("content_copy")
|
||||
|
||||
const copyResponse = () => {
|
||||
copyToClipboard(responseString.value!)
|
||||
copyResponseIcon.value = "done"
|
||||
setTimeout(() => (copyResponseIcon.value = "content_copy"), 1000)
|
||||
}
|
||||
|
||||
const downloadResponse = () => {
|
||||
const dataToWrite = responseString.value
|
||||
const file = new Blob([dataToWrite!], { type: "application/json" })
|
||||
const a = document.createElement("a")
|
||||
const url = URL.createObjectURL(file)
|
||||
a.href = url
|
||||
a.download = `${url.split("/").pop()!.split("#")[0].split("?")[0]}`
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
downloadResponseIcon.value = "done"
|
||||
$toast.success(t("state.download_started").toString(), {
|
||||
icon: "downloading",
|
||||
})
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a)
|
||||
URL.revokeObjectURL(url)
|
||||
downloadResponseIcon.value = "save_alt"
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
return {
|
||||
responseString,
|
||||
|
||||
downloadResponseIcon,
|
||||
copyResponseIcon,
|
||||
|
||||
downloadResponse,
|
||||
copyResponse,
|
||||
|
||||
getSpecialKey: getPlatformSpecialKey,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.shortcut-key {
|
||||
@apply bg-dividerLight;
|
||||
@apply rounded;
|
||||
@apply ml-2;
|
||||
@apply py-1;
|
||||
@apply px-2;
|
||||
@apply inline-flex;
|
||||
}
|
||||
</style>
|
||||
@@ -1,471 +0,0 @@
|
||||
<template>
|
||||
<aside>
|
||||
<SmartTabs styles="sticky z-10 top-0">
|
||||
<SmartTab :id="'docs'" :label="`Docs`" :selected="true">
|
||||
<AppSection label="docs">
|
||||
<div class="bg-primary flex top-sidebarPrimaryStickyFold z-10 sticky">
|
||||
<div class="search-wrapper">
|
||||
<input
|
||||
v-model="graphqlFieldsFilterText"
|
||||
type="search"
|
||||
:placeholder="$t('action.search')"
|
||||
class="bg-transparent flex w-full py-2 pr-2 pl-10"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
to="https://docs.hoppscotch.io/quickstart/graphql"
|
||||
blank
|
||||
:title="$t('app.wiki')"
|
||||
icon="help_outline"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<SmartTabs
|
||||
ref="gqlTabs"
|
||||
styles="border-t border-dividerLight sticky z-10 top-sidebarSecondaryStickyFold"
|
||||
>
|
||||
<div class="gqlTabs">
|
||||
<SmartTab
|
||||
v-if="queryFields.length > 0"
|
||||
:id="'queries'"
|
||||
:label="$t('tab.queries')"
|
||||
:selected="true"
|
||||
class="divide-y divide-dividerLight"
|
||||
>
|
||||
<GraphqlField
|
||||
v-for="(field, index) in filteredQueryFields"
|
||||
:key="`field-${index}`"
|
||||
:gql-field="field"
|
||||
:jump-type-callback="handleJumpToType"
|
||||
class="p-4"
|
||||
/>
|
||||
</SmartTab>
|
||||
<SmartTab
|
||||
v-if="mutationFields.length > 0"
|
||||
:id="'mutations'"
|
||||
:label="$t('graphql.mutations')"
|
||||
class="divide-y divide-dividerLight"
|
||||
>
|
||||
<GraphqlField
|
||||
v-for="(field, index) in filteredMutationFields"
|
||||
:key="`field-${index}`"
|
||||
:gql-field="field"
|
||||
:jump-type-callback="handleJumpToType"
|
||||
class="p-4"
|
||||
/>
|
||||
</SmartTab>
|
||||
<SmartTab
|
||||
v-if="subscriptionFields.length > 0"
|
||||
:id="'subscriptions'"
|
||||
:label="$t('graphql.subscriptions')"
|
||||
class="divide-y divide-dividerLight"
|
||||
>
|
||||
<GraphqlField
|
||||
v-for="(field, index) in filteredSubscriptionFields"
|
||||
:key="`field-${index}`"
|
||||
:gql-field="field"
|
||||
:jump-type-callback="handleJumpToType"
|
||||
class="p-4"
|
||||
/>
|
||||
</SmartTab>
|
||||
<SmartTab
|
||||
v-if="graphqlTypes.length > 0"
|
||||
:id="'types'"
|
||||
ref="typesTab"
|
||||
:label="$t('tab.types')"
|
||||
class="divide-y divide-dividerLight"
|
||||
>
|
||||
<GraphqlType
|
||||
v-for="(type, index) in filteredGraphqlTypes"
|
||||
:key="`type-${index}`"
|
||||
:gql-type="type"
|
||||
:gql-types="graphqlTypes"
|
||||
:is-highlighted="isGqlTypeHighlighted(type)"
|
||||
:highlighted-fields="getGqlTypeHighlightedFields(type)"
|
||||
:jump-type-callback="handleJumpToType"
|
||||
/>
|
||||
</SmartTab>
|
||||
</div>
|
||||
</SmartTabs>
|
||||
<div
|
||||
v-if="
|
||||
queryFields.length === 0 &&
|
||||
mutationFields.length === 0 &&
|
||||
subscriptionFields.length === 0 &&
|
||||
graphqlTypes.length === 0
|
||||
"
|
||||
class="
|
||||
flex flex-col
|
||||
text-secondaryLight
|
||||
p-4
|
||||
items-center
|
||||
justify-center
|
||||
"
|
||||
>
|
||||
<i class="opacity-75 pb-2 material-icons">link</i>
|
||||
<span class="text-center">
|
||||
{{ $t("empty.schema") }}
|
||||
</span>
|
||||
</div>
|
||||
</AppSection>
|
||||
</SmartTab>
|
||||
|
||||
<SmartTab :id="'history'" :label="$t('tab.history')">
|
||||
<History
|
||||
ref="graphqlHistoryComponent"
|
||||
:page="'graphql'"
|
||||
@useHistory="handleUseHistory"
|
||||
/>
|
||||
</SmartTab>
|
||||
|
||||
<SmartTab :id="'collections'" :label="$t('tab.collections')">
|
||||
<CollectionsGraphql />
|
||||
</SmartTab>
|
||||
|
||||
<SmartTab :id="'schema'" :label="`Schema`">
|
||||
<AppSection ref="schema" label="schema">
|
||||
<div
|
||||
v-if="schemaString"
|
||||
class="
|
||||
bg-primary
|
||||
flex flex-1
|
||||
top-sidebarPrimaryStickyFold
|
||||
pl-4
|
||||
z-10
|
||||
sticky
|
||||
items-center
|
||||
justify-between
|
||||
"
|
||||
>
|
||||
<label class="font-semibold text-secondaryLight">
|
||||
{{ $t("graphql.schema") }}
|
||||
</label>
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
to="https://docs.hoppscotch.io/quickstart/graphql"
|
||||
blank
|
||||
:title="$t('app.wiki')"
|
||||
icon="help_outline"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
ref="downloadSchema"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('action.download_file')"
|
||||
:icon="downloadSchemaIcon"
|
||||
@click.native="downloadSchema"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
ref="copySchemaCode"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('action.copy')"
|
||||
:icon="copySchemaIcon"
|
||||
@click.native="copySchema"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<SmartAceEditor
|
||||
v-if="schemaString"
|
||||
v-model="schemaString"
|
||||
:lang="'graphqlschema'"
|
||||
:options="{
|
||||
maxLines: Infinity,
|
||||
minLines: 16,
|
||||
autoScrollEditorIntoView: true,
|
||||
readOnly: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false,
|
||||
}"
|
||||
styles="border-b border-dividerLight"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
class="
|
||||
flex flex-col
|
||||
text-secondaryLight
|
||||
p-4
|
||||
items-center
|
||||
justify-center
|
||||
"
|
||||
>
|
||||
<i class="opacity-75 pb-2 material-icons">link</i>
|
||||
<span class="text-center">
|
||||
{{ $t("empty.schema") }}
|
||||
</span>
|
||||
</div>
|
||||
</AppSection>
|
||||
</SmartTab>
|
||||
</SmartTabs>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
nextTick,
|
||||
PropType,
|
||||
ref,
|
||||
useContext,
|
||||
} from "@nuxtjs/composition-api"
|
||||
import { GraphQLField, GraphQLType } from "graphql"
|
||||
import { map } from "rxjs/operators"
|
||||
import { GQLConnection } from "~/helpers/GQLConnection"
|
||||
import { GQLHeader } from "~/helpers/types/HoppGQLRequest"
|
||||
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
||||
import { useReadonlyStream } from "~/helpers/utils/composables"
|
||||
import {
|
||||
setGQLHeaders,
|
||||
setGQLQuery,
|
||||
setGQLResponse,
|
||||
setGQLURL,
|
||||
setGQLVariables,
|
||||
} from "~/newstore/GQLSession"
|
||||
|
||||
function isTextFoundInGraphqlFieldObject(
|
||||
text: string,
|
||||
field: GraphQLField<any, any>
|
||||
) {
|
||||
const normalizedText = text.toLowerCase()
|
||||
|
||||
const isFilterTextFoundInDescription = field.description
|
||||
? field.description.toLowerCase().includes(normalizedText)
|
||||
: false
|
||||
const isFilterTextFoundInName = field.name
|
||||
.toLowerCase()
|
||||
.includes(normalizedText)
|
||||
|
||||
return isFilterTextFoundInDescription || isFilterTextFoundInName
|
||||
}
|
||||
|
||||
function getFilteredGraphqlFields(
|
||||
filterText: string,
|
||||
fields: GraphQLField<any, any>[]
|
||||
) {
|
||||
if (!filterText) return fields
|
||||
|
||||
return fields.filter((field) =>
|
||||
isTextFoundInGraphqlFieldObject(filterText, field)
|
||||
)
|
||||
}
|
||||
|
||||
function getFilteredGraphqlTypes(filterText: string, types: GraphQLType[]) {
|
||||
if (!filterText) return types
|
||||
|
||||
return types.filter((type) => {
|
||||
const isFilterTextMatching = isTextFoundInGraphqlFieldObject(
|
||||
filterText,
|
||||
type as any
|
||||
)
|
||||
|
||||
if (isFilterTextMatching) {
|
||||
return true
|
||||
}
|
||||
|
||||
const isFilterTextMatchingAtLeastOneField = Object.values(
|
||||
(type as any)._fields || {}
|
||||
).some((field) => isTextFoundInGraphqlFieldObject(filterText, field as any))
|
||||
|
||||
return isFilterTextMatchingAtLeastOneField
|
||||
})
|
||||
}
|
||||
|
||||
function resolveRootType(type: GraphQLType) {
|
||||
let t: any = type
|
||||
while (t.ofType) t = t.ofType
|
||||
return t
|
||||
}
|
||||
|
||||
type GQLHistoryEntry = {
|
||||
url: string
|
||||
headers: GQLHeader[]
|
||||
query: string
|
||||
response: string
|
||||
variables: string
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
conn: {
|
||||
type: Object as PropType<GQLConnection>,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const {
|
||||
$toast,
|
||||
app: { i18n },
|
||||
} = useContext()
|
||||
const t = i18n.t.bind(i18n)
|
||||
|
||||
const queryFields = useReadonlyStream(
|
||||
props.conn.queryFields$.pipe(map((x) => x ?? [])),
|
||||
[]
|
||||
)
|
||||
const mutationFields = useReadonlyStream(
|
||||
props.conn.mutationFields$.pipe(map((x) => x ?? [])),
|
||||
[]
|
||||
)
|
||||
const subscriptionFields = useReadonlyStream(
|
||||
props.conn.subscriptionFields$.pipe(map((x) => x ?? [])),
|
||||
[]
|
||||
)
|
||||
const graphqlTypes = useReadonlyStream(
|
||||
props.conn.graphqlTypes$.pipe(map((x) => x ?? [])),
|
||||
[]
|
||||
)
|
||||
|
||||
const downloadSchemaIcon = ref("save_alt")
|
||||
const copySchemaIcon = ref("content_copy")
|
||||
|
||||
const graphqlFieldsFilterText = ref("")
|
||||
|
||||
const gqlTabs = ref<any | null>(null)
|
||||
const typesTab = ref<any | null>(null)
|
||||
|
||||
const filteredQueryFields = computed(() => {
|
||||
return getFilteredGraphqlFields(
|
||||
graphqlFieldsFilterText.value,
|
||||
queryFields.value as any
|
||||
)
|
||||
})
|
||||
|
||||
const filteredMutationFields = computed(() => {
|
||||
return getFilteredGraphqlFields(
|
||||
graphqlFieldsFilterText.value,
|
||||
mutationFields.value as any
|
||||
)
|
||||
})
|
||||
|
||||
const filteredSubscriptionFields = computed(() => {
|
||||
return getFilteredGraphqlFields(
|
||||
graphqlFieldsFilterText.value,
|
||||
subscriptionFields.value as any
|
||||
)
|
||||
})
|
||||
|
||||
const filteredGraphqlTypes = computed(() => {
|
||||
return getFilteredGraphqlTypes(
|
||||
graphqlFieldsFilterText.value,
|
||||
graphqlTypes.value as any
|
||||
)
|
||||
})
|
||||
|
||||
const isGqlTypeHighlighted = (gqlType: GraphQLType) => {
|
||||
if (!graphqlFieldsFilterText.value) return false
|
||||
|
||||
return isTextFoundInGraphqlFieldObject(
|
||||
graphqlFieldsFilterText.value,
|
||||
gqlType as any
|
||||
)
|
||||
}
|
||||
|
||||
const getGqlTypeHighlightedFields = (gqlType: GraphQLType) => {
|
||||
if (!graphqlFieldsFilterText.value) return []
|
||||
|
||||
const fields = Object.values((gqlType as any)._fields || {})
|
||||
if (!fields || fields.length === 0) return []
|
||||
|
||||
return fields.filter((field) =>
|
||||
isTextFoundInGraphqlFieldObject(
|
||||
graphqlFieldsFilterText.value,
|
||||
field as any
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
const handleJumpToType = async (type: GraphQLType) => {
|
||||
gqlTabs.value.selectTab(typesTab.value)
|
||||
await nextTick()
|
||||
|
||||
const rootTypeName = resolveRootType(type).name
|
||||
|
||||
const target = document.getElementById(`type_${rootTypeName}`)
|
||||
if (target) {
|
||||
gqlTabs.value.$el
|
||||
.querySelector(".gqlTabs")
|
||||
.scrollTo({ top: target.offsetTop, behavior: "smooth" })
|
||||
}
|
||||
}
|
||||
const schemaString = useReadonlyStream(
|
||||
props.conn.schemaString$.pipe(map((x) => x ?? "")),
|
||||
""
|
||||
)
|
||||
|
||||
const downloadSchema = () => {
|
||||
const dataToWrite = JSON.stringify(schemaString.value, null, 2)
|
||||
const file = new Blob([dataToWrite], { type: "application/graphql" })
|
||||
const a = document.createElement("a")
|
||||
const url = URL.createObjectURL(file)
|
||||
a.href = url
|
||||
a.download = `${
|
||||
url.split("/").pop()!.split("#")[0].split("?")[0]
|
||||
}.graphql`
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
downloadSchemaIcon.value = "done"
|
||||
$toast.success(t("state.download_started").toString(), {
|
||||
icon: "downloading",
|
||||
})
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a)
|
||||
URL.revokeObjectURL(url)
|
||||
downloadSchemaIcon.value = "save_alt"
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
const copySchema = () => {
|
||||
if (!schemaString.value) return
|
||||
|
||||
copyToClipboard(schemaString.value)
|
||||
copySchemaIcon.value = "done"
|
||||
setTimeout(() => (copySchemaIcon.value = "content_copy"), 1000)
|
||||
}
|
||||
|
||||
const handleUseHistory = (entry: GQLHistoryEntry) => {
|
||||
const url = entry.url
|
||||
const headers = entry.headers
|
||||
const gqlQueryString = entry.query
|
||||
const variableString = entry.variables
|
||||
const responseText = entry.response
|
||||
|
||||
setGQLURL(url)
|
||||
setGQLHeaders(headers)
|
||||
setGQLQuery(gqlQueryString)
|
||||
setGQLVariables(variableString)
|
||||
setGQLResponse(responseText)
|
||||
props.conn.reset()
|
||||
}
|
||||
|
||||
return {
|
||||
queryFields,
|
||||
mutationFields,
|
||||
subscriptionFields,
|
||||
graphqlTypes,
|
||||
schemaString,
|
||||
|
||||
graphqlFieldsFilterText,
|
||||
|
||||
filteredQueryFields,
|
||||
filteredMutationFields,
|
||||
filteredSubscriptionFields,
|
||||
filteredGraphqlTypes,
|
||||
|
||||
isGqlTypeHighlighted,
|
||||
getGqlTypeHighlightedFields,
|
||||
|
||||
gqlTabs,
|
||||
typesTab,
|
||||
handleJumpToType,
|
||||
|
||||
downloadSchema,
|
||||
downloadSchemaIcon,
|
||||
copySchemaIcon,
|
||||
copySchema,
|
||||
handleUseHistory,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -1,215 +0,0 @@
|
||||
<template>
|
||||
<AppSection label="headers">
|
||||
<div
|
||||
class="
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
top-upperSecondaryStickyFold
|
||||
pl-4
|
||||
z-10
|
||||
sticky
|
||||
items-center
|
||||
justify-between
|
||||
"
|
||||
>
|
||||
<label class="font-semibold text-secondaryLight">
|
||||
{{ $t("request.header_list") }}
|
||||
</label>
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
to="https://docs.hoppscotch.io/features/headers"
|
||||
blank
|
||||
:title="$t('app.wiki')"
|
||||
icon="help_outline"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('action.clear_all')"
|
||||
icon="clear_all"
|
||||
@click.native="clearContent"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('add.new')"
|
||||
icon="add"
|
||||
@click.native="addHeader"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="(header, index) in headers$"
|
||||
:key="`header-${index}`"
|
||||
class="divide-x divide-dividerLight border-b border-dividerLight flex"
|
||||
>
|
||||
<SmartAutoComplete
|
||||
:placeholder="$t('count.header', { count: index + 1 })"
|
||||
:source="commonHeaders"
|
||||
:spellcheck="false"
|
||||
:value="header.key"
|
||||
autofocus
|
||||
styles="
|
||||
bg-transparent
|
||||
flex
|
||||
flex-1
|
||||
py-1
|
||||
px-4
|
||||
truncate
|
||||
"
|
||||
:class="{ '!flex flex-1': EXPERIMENTAL_URL_BAR_ENABLED }"
|
||||
@input="
|
||||
updateHeader(index, {
|
||||
key: $event,
|
||||
value: header.value,
|
||||
active: header.active,
|
||||
})
|
||||
"
|
||||
/>
|
||||
<SmartEnvInput
|
||||
v-if="EXPERIMENTAL_URL_BAR_ENABLED"
|
||||
v-model="header.value"
|
||||
:placeholder="$t('count.value', { count: index + 1 })"
|
||||
styles="
|
||||
bg-transparent
|
||||
flex
|
||||
flex-1
|
||||
py-1
|
||||
px-4
|
||||
"
|
||||
@change="
|
||||
updateHeader(index, {
|
||||
key: header.key,
|
||||
value: $event,
|
||||
active: header.active,
|
||||
})
|
||||
"
|
||||
/>
|
||||
<input
|
||||
v-else
|
||||
class="bg-transparent flex flex-1 py-2 px-4"
|
||||
:placeholder="$t('count.value', { count: index + 1 })"
|
||||
:name="'value' + index"
|
||||
:value="header.value"
|
||||
@change="
|
||||
updateHeader(index, {
|
||||
key: header.key,
|
||||
value: $event.target.value,
|
||||
active: header.active,
|
||||
})
|
||||
"
|
||||
/>
|
||||
<span>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="
|
||||
header.hasOwnProperty('active')
|
||||
? header.active
|
||||
? $t('action.turn_off')
|
||||
: $t('action.turn_on')
|
||||
: $t('action.turn_off')
|
||||
"
|
||||
:icon="
|
||||
header.hasOwnProperty('active')
|
||||
? header.active
|
||||
? 'check_circle_outline'
|
||||
: 'radio_button_unchecked'
|
||||
: 'check_circle_outline'
|
||||
"
|
||||
color="green"
|
||||
@click.native="
|
||||
updateHeader(index, {
|
||||
key: header.key,
|
||||
value: header.value,
|
||||
active: header.hasOwnProperty('active') ? !header.active : false,
|
||||
})
|
||||
"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('action.remove')"
|
||||
icon="remove_circle_outline"
|
||||
color="red"
|
||||
@click.native="deleteHeader(index)"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="headers$.length === 0"
|
||||
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||
>
|
||||
<span class="text-center pb-4">
|
||||
{{ $t("empty.headers") }}
|
||||
</span>
|
||||
<ButtonSecondary
|
||||
filled
|
||||
:label="$t('add.new')"
|
||||
icon="add"
|
||||
@click.native="addHeader"
|
||||
/>
|
||||
</div>
|
||||
</AppSection>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import {
|
||||
restHeaders$,
|
||||
addRESTHeader,
|
||||
updateRESTHeader,
|
||||
deleteRESTHeader,
|
||||
deleteAllRESTHeaders,
|
||||
} from "~/newstore/RESTSession"
|
||||
import { commonHeaders } from "~/helpers/headers"
|
||||
import { useSetting } from "~/newstore/settings"
|
||||
import { useReadonlyStream } from "~/helpers/utils/composables"
|
||||
import { HoppRESTHeader } from "~/helpers/types/HoppRESTRequest"
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
return {
|
||||
headers$: useReadonlyStream(restHeaders$, []),
|
||||
EXPERIMENTAL_URL_BAR_ENABLED: useSetting("EXPERIMENTAL_URL_BAR_ENABLED"),
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
commonHeaders,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
headers$: {
|
||||
handler(newValue) {
|
||||
if (
|
||||
(newValue[newValue.length - 1]?.key !== "" ||
|
||||
newValue[newValue.length - 1]?.value !== "") &&
|
||||
newValue.length
|
||||
)
|
||||
this.addHeader()
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
// mounted() {
|
||||
// if (!this.headers$?.length) {
|
||||
// this.addHeader()
|
||||
// }
|
||||
// },
|
||||
methods: {
|
||||
addHeader() {
|
||||
addRESTHeader({ key: "", value: "", active: true })
|
||||
},
|
||||
updateHeader(index: number, item: HoppRESTHeader) {
|
||||
updateRESTHeader(index, item)
|
||||
},
|
||||
deleteHeader(index: number) {
|
||||
deleteRESTHeader(index)
|
||||
},
|
||||
clearContent() {
|
||||
deleteAllRESTHeaders()
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -1,137 +0,0 @@
|
||||
<template>
|
||||
<SmartModal v-if="show" :title="$t('import.curl')" @close="hideModal">
|
||||
<template #body>
|
||||
<div class="flex flex-col px-2">
|
||||
<textarea
|
||||
id="import-curl"
|
||||
v-model="curl"
|
||||
class="textarea floating-input"
|
||||
autofocus
|
||||
rows="8"
|
||||
placeholder=" "
|
||||
></textarea>
|
||||
<label for="import-curl">
|
||||
{{ $t("request.enter_curl") }}
|
||||
</label>
|
||||
</div>
|
||||
</template>
|
||||
<template #footer>
|
||||
<span>
|
||||
<ButtonPrimary
|
||||
:label="$t('import.title')"
|
||||
@click.native="handleImport"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
:label="$t('action.cancel')"
|
||||
@click.native="hideModal"
|
||||
/>
|
||||
</span>
|
||||
</template>
|
||||
</SmartModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import parseCurlCommand from "~/helpers/curlparser"
|
||||
import {
|
||||
HoppRESTHeader,
|
||||
HoppRESTParam,
|
||||
makeRESTRequest,
|
||||
} from "~/helpers/types/HoppRESTRequest"
|
||||
import { setRESTRequest } from "~/newstore/RESTSession"
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
show: Boolean,
|
||||
},
|
||||
emits: ["hide-modal"],
|
||||
data() {
|
||||
return {
|
||||
curl: "",
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
hideModal() {
|
||||
this.$emit("hide-modal")
|
||||
},
|
||||
handleImport() {
|
||||
const text = this.curl
|
||||
try {
|
||||
const parsedCurl = parseCurlCommand(text)
|
||||
const { origin, pathname } = new URL(
|
||||
parsedCurl.url.replace(/"/g, "").replace(/'/g, "")
|
||||
)
|
||||
const endpoint = origin + pathname
|
||||
const headers: HoppRESTHeader[] = []
|
||||
const params: HoppRESTParam[] = []
|
||||
if (parsedCurl.query) {
|
||||
for (const key of Object.keys(parsedCurl.query)) {
|
||||
const val = parsedCurl.query[key]!
|
||||
|
||||
if (Array.isArray(val)) {
|
||||
val.forEach((value) => {
|
||||
params.push({
|
||||
key,
|
||||
value,
|
||||
active: true,
|
||||
})
|
||||
})
|
||||
} else {
|
||||
params.push({
|
||||
key,
|
||||
value: val!,
|
||||
active: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
if (parsedCurl.headers) {
|
||||
for (const key of Object.keys(parsedCurl.headers)) {
|
||||
headers.push({
|
||||
key,
|
||||
value: parsedCurl.headers[key],
|
||||
active: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
const method = parsedCurl.method.toUpperCase()
|
||||
// let rawInput = false
|
||||
// let rawParams: any | null = null
|
||||
|
||||
// if (parsedCurl.data) {
|
||||
// rawInput = true
|
||||
// rawParams = parsedCurl.data
|
||||
// }
|
||||
|
||||
this.showCurlImportModal = false
|
||||
|
||||
setRESTRequest(
|
||||
makeRESTRequest({
|
||||
name: "Untitled request",
|
||||
endpoint,
|
||||
method,
|
||||
params,
|
||||
headers,
|
||||
preRequestScript: "",
|
||||
testScript: "",
|
||||
auth: {
|
||||
authType: "none",
|
||||
authActive: true,
|
||||
},
|
||||
body: {
|
||||
contentType: "application/json",
|
||||
body: "",
|
||||
},
|
||||
})
|
||||
)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
this.$toast.error(this.$t("error.curl_invalid_format").toString(), {
|
||||
icon: "error_outline",
|
||||
})
|
||||
}
|
||||
this.hideModal()
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -1,220 +0,0 @@
|
||||
<template>
|
||||
<AppSection label="parameters">
|
||||
<div
|
||||
class="
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
top-upperSecondaryStickyFold
|
||||
pl-4
|
||||
z-10
|
||||
sticky
|
||||
items-center
|
||||
justify-between
|
||||
"
|
||||
>
|
||||
<label class="font-semibold text-secondaryLight">
|
||||
{{ $t("request.parameter_list") }}
|
||||
</label>
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
to="https://docs.hoppscotch.io/features/parameters"
|
||||
blank
|
||||
:title="$t('app.wiki')"
|
||||
icon="help_outline"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('action.clear_all')"
|
||||
icon="clear_all"
|
||||
@click.native="clearContent"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('add.new')"
|
||||
icon="add"
|
||||
@click.native="addParam"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="(param, index) in params$"
|
||||
:key="`param-${index}`"
|
||||
class="divide-x divide-dividerLight border-b border-dividerLight flex"
|
||||
>
|
||||
<SmartEnvInput
|
||||
v-if="EXPERIMENTAL_URL_BAR_ENABLED"
|
||||
v-model="param.key"
|
||||
:placeholder="$t('count.parameter', { count: index + 1 })"
|
||||
styles="
|
||||
bg-transparent
|
||||
flex
|
||||
flex-1
|
||||
py-1
|
||||
px-4
|
||||
"
|
||||
@change="
|
||||
updateParam(index, {
|
||||
key: $event,
|
||||
value: param.value,
|
||||
active: param.active,
|
||||
})
|
||||
"
|
||||
/>
|
||||
<input
|
||||
v-else
|
||||
class="bg-transparent flex flex-1 py-2 px-4"
|
||||
:placeholder="$t('count.parameter', { count: index + 1 })"
|
||||
:name="'param' + index"
|
||||
:value="param.key"
|
||||
autofocus
|
||||
@change="
|
||||
updateParam(index, {
|
||||
key: $event.target.value,
|
||||
value: param.value,
|
||||
active: param.active,
|
||||
})
|
||||
"
|
||||
/>
|
||||
<SmartEnvInput
|
||||
v-if="EXPERIMENTAL_URL_BAR_ENABLED"
|
||||
v-model="param.value"
|
||||
:placeholder="$t('count.value', { count: index + 1 })"
|
||||
styles="
|
||||
bg-transparent
|
||||
flex
|
||||
flex-1
|
||||
py-1
|
||||
px-4
|
||||
"
|
||||
@change="
|
||||
updateParam(index, {
|
||||
key: param.key,
|
||||
value: $event,
|
||||
active: param.active,
|
||||
})
|
||||
"
|
||||
/>
|
||||
<input
|
||||
v-else
|
||||
class="bg-transparent flex flex-1 py-2 px-4"
|
||||
:placeholder="$t('count.value', { count: index + 1 })"
|
||||
:name="'value' + index"
|
||||
:value="param.value"
|
||||
@change="
|
||||
updateParam(index, {
|
||||
key: param.key,
|
||||
value: $event.target.value,
|
||||
active: param.active,
|
||||
})
|
||||
"
|
||||
/>
|
||||
<span>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="
|
||||
param.hasOwnProperty('active')
|
||||
? param.active
|
||||
? $t('action.turn_off')
|
||||
: $t('action.turn_on')
|
||||
: $t('action.turn_off')
|
||||
"
|
||||
:icon="
|
||||
param.hasOwnProperty('active')
|
||||
? param.active
|
||||
? 'check_circle_outline'
|
||||
: 'radio_button_unchecked'
|
||||
: 'check_circle_outline'
|
||||
"
|
||||
color="green"
|
||||
@click.native="
|
||||
updateParam(index, {
|
||||
key: param.key,
|
||||
value: param.value,
|
||||
active: param.hasOwnProperty('active') ? !param.active : false,
|
||||
})
|
||||
"
|
||||
/>
|
||||
</span>
|
||||
<span>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('action.remove')"
|
||||
icon="remove_circle_outline"
|
||||
color="red"
|
||||
@click.native="deleteParam(index)"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
v-if="params$.length === 0"
|
||||
class="flex flex-col text-secondaryLight p-4 items-center justify-center"
|
||||
>
|
||||
<span class="text-center pb-4">
|
||||
{{ $t("empty.parameters") }}
|
||||
</span>
|
||||
<ButtonSecondary
|
||||
:label="$t('add.new')"
|
||||
icon="add"
|
||||
filled
|
||||
@click.native="addParam"
|
||||
/>
|
||||
</div>
|
||||
</AppSection>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import { HoppRESTParam } from "~/helpers/types/HoppRESTRequest"
|
||||
import { useReadonlyStream } from "~/helpers/utils/composables"
|
||||
import {
|
||||
restParams$,
|
||||
addRESTParam,
|
||||
updateRESTParam,
|
||||
deleteRESTParam,
|
||||
deleteAllRESTParams,
|
||||
} from "~/newstore/RESTSession"
|
||||
import { useSetting } from "~/newstore/settings"
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
return {
|
||||
params$: useReadonlyStream(restParams$, []),
|
||||
EXPERIMENTAL_URL_BAR_ENABLED: useSetting("EXPERIMENTAL_URL_BAR_ENABLED"),
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
params$: {
|
||||
handler(newValue) {
|
||||
if (
|
||||
(newValue[newValue.length - 1]?.key !== "" ||
|
||||
newValue[newValue.length - 1]?.value !== "") &&
|
||||
newValue.length
|
||||
)
|
||||
this.addParam()
|
||||
},
|
||||
deep: true,
|
||||
},
|
||||
},
|
||||
// mounted() {
|
||||
// if (!this.params$?.length) {
|
||||
// this.addParam()
|
||||
// }
|
||||
// },
|
||||
methods: {
|
||||
addParam() {
|
||||
addRESTParam({ key: "", value: "", active: true })
|
||||
},
|
||||
updateParam(index: number, item: HoppRESTParam) {
|
||||
updateRESTParam(index, item)
|
||||
},
|
||||
deleteParam(index: number) {
|
||||
deleteRESTParam(index)
|
||||
},
|
||||
clearContent() {
|
||||
deleteAllRESTParams()
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -1,136 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
class="
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
top-upperTertiaryStickyFold
|
||||
pl-4
|
||||
z-10
|
||||
sticky
|
||||
items-center
|
||||
justify-between
|
||||
"
|
||||
>
|
||||
<label class="font-semibold text-secondaryLight">
|
||||
{{ $t("request.raw_body") }}
|
||||
</label>
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
to="https://docs.hoppscotch.io/features/body"
|
||||
blank
|
||||
:title="$t('app.wiki')"
|
||||
icon="help_outline"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('action.clear')"
|
||||
icon="clear_all"
|
||||
@click.native="clearContent('rawParams', $event)"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-if="contentType && contentType.endsWith('json')"
|
||||
ref="prettifyRequest"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('action.prettify')"
|
||||
:icon="prettifyIcon"
|
||||
@click.native="prettifyRequestBody"
|
||||
/>
|
||||
<label for="payload">
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('import.json')"
|
||||
icon="post_add"
|
||||
@click.native="$refs.payload.click()"
|
||||
/>
|
||||
</label>
|
||||
<input
|
||||
ref="payload"
|
||||
class="input"
|
||||
name="payload"
|
||||
type="file"
|
||||
@change="uploadPayload"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative">
|
||||
<SmartAceEditor
|
||||
v-model="rawParamsBody"
|
||||
:lang="rawInputEditorLang"
|
||||
:options="{
|
||||
maxLines: Infinity,
|
||||
minLines: 16,
|
||||
autoScrollEditorIntoView: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false,
|
||||
}"
|
||||
styles="border-b border-dividerLight"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import { getEditorLangForMimeType } from "~/helpers/editorutils"
|
||||
import { pluckRef } from "~/helpers/utils/composables"
|
||||
import { useRESTRequestBody } from "~/newstore/RESTSession"
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
contentType: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
return {
|
||||
rawParamsBody: pluckRef(useRESTRequestBody(), "body"),
|
||||
prettifyIcon: "photo_filter",
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
rawInputEditorLang() {
|
||||
return getEditorLangForMimeType(this.contentType)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
clearContent() {
|
||||
this.rawParamsBody = ""
|
||||
},
|
||||
uploadPayload() {
|
||||
const file = this.$refs.payload.files[0]
|
||||
if (file !== undefined && file !== null) {
|
||||
const reader = new FileReader()
|
||||
reader.onload = ({ target }) => {
|
||||
this.rawParamsBody = target.result
|
||||
}
|
||||
reader.readAsText(file)
|
||||
this.$toast.success(this.$t("state.file_imported"), {
|
||||
icon: "attach_file",
|
||||
})
|
||||
} else {
|
||||
this.$toast.error(this.$t("action.choose_file"), {
|
||||
icon: "attach_file",
|
||||
})
|
||||
}
|
||||
this.$refs.payload.value = ""
|
||||
},
|
||||
prettifyRequestBody() {
|
||||
try {
|
||||
const jsonObj = JSON.parse(this.rawParamsBody)
|
||||
this.rawParamsBody = JSON.stringify(jsonObj, null, 2)
|
||||
this.prettifyIcon = "done"
|
||||
setTimeout(() => (this.prettifyIcon = "photo_filter"), 1000)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
this.$toast.error(`${this.$t("error.json_prettify_invalid_body")}`, {
|
||||
icon: "error_outline",
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -1,80 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
class="
|
||||
bg-primaryLight
|
||||
rounded
|
||||
flex
|
||||
grid
|
||||
p-4
|
||||
gap-4
|
||||
grid-cols-1
|
||||
md:grid-cols-2
|
||||
lg:grid-cols-3
|
||||
"
|
||||
>
|
||||
<div
|
||||
v-for="(cta, index) in ctas"
|
||||
:key="`cta-${index}`"
|
||||
class="flex-col p-8 inline-flex"
|
||||
>
|
||||
<i class="text-accent text-3xl material-icons">{{ cta.icon }}</i>
|
||||
<div class="flex-grow">
|
||||
<h2 class="mt-4 text-lg text-secondaryDark mb-2 transition">
|
||||
{{ cta.title }}
|
||||
</h2>
|
||||
<p>
|
||||
{{ cta.description }}
|
||||
</p>
|
||||
<p class="mt-2">
|
||||
<SmartLink :to="cta.link.target" class="link" blank>
|
||||
{{ cta.link.title }}
|
||||
<i class="material-icons">chevron_right</i>
|
||||
</SmartLink>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
|
||||
export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
ctas: [
|
||||
{
|
||||
icon: "layers",
|
||||
title: "Feature",
|
||||
description:
|
||||
"Get up and running with Kooli in as little as 10 minutes.",
|
||||
link: {
|
||||
title: "Feature",
|
||||
target: "https://docs.hoppscotch.io/api",
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "local_library",
|
||||
title: "Feature",
|
||||
description:
|
||||
"Explore and start integrating Kooli's products and tools.",
|
||||
link: {
|
||||
title: "Feature",
|
||||
target: "https://docs.hoppscotch.io/guides",
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "local_library",
|
||||
title: "Feature",
|
||||
description:
|
||||
"Explore and start integrating Kooli's products and tools.",
|
||||
link: {
|
||||
title: "Feature",
|
||||
target: "https://docs.hoppscotch.io/guides",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -1,85 +0,0 @@
|
||||
<template>
|
||||
<div class="flex flex-col p-4">
|
||||
<div class="flex flex-col items-center">
|
||||
<p class="my-4 text-center text-accent tracking-widest">FEATURES</p>
|
||||
</div>
|
||||
<div class="grid gap-4 grid-cols-1 md:grid-cols-2 lg:grid-cols-3">
|
||||
<div
|
||||
v-for="(feature, index) in features"
|
||||
:key="`feature-${index}`"
|
||||
class="flex-col p-8 inline-flex"
|
||||
>
|
||||
<i class="text-accent text-4xl material-icons">{{ feature.icon }}</i>
|
||||
<div class="flex-grow">
|
||||
<h2 class="mt-4 text-lg text-secondaryDark mb-2 transition">
|
||||
{{ feature.title }}
|
||||
</h2>
|
||||
<p>
|
||||
{{ feature.description }}
|
||||
</p>
|
||||
<p class="mt-2">
|
||||
<NuxtLink :to="feature.link.target" class="link">
|
||||
{{ feature.link.title }}
|
||||
<i class="material-icons">chevron_right</i>
|
||||
</NuxtLink>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
|
||||
export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
features: [
|
||||
{
|
||||
icon: "offline_bolt",
|
||||
title: "Feature",
|
||||
description:
|
||||
"Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nam vel vero quia tenetur obcaecati. Distinctio nesciunt obcaecati deserunt.",
|
||||
link: { title: "Learn more", target: "/settings" },
|
||||
},
|
||||
{
|
||||
icon: "stars",
|
||||
title: "Feature",
|
||||
description:
|
||||
"Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nam vel vero quia tenetur obcaecati. Distinctio nesciunt obcaecati deserunt.",
|
||||
link: { title: "Learn more", target: "/settings" },
|
||||
},
|
||||
{
|
||||
icon: "supervised_user_circle",
|
||||
title: "Feature",
|
||||
description:
|
||||
"Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nam vel vero quia tenetur obcaecati. Distinctio nesciunt obcaecati deserunt.",
|
||||
link: { title: "Learn more", target: "/settings" },
|
||||
},
|
||||
{
|
||||
icon: "build_circle",
|
||||
title: "Feature",
|
||||
description:
|
||||
"Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nam vel vero quia tenetur obcaecati. Distinctio nesciunt obcaecati deserunt.",
|
||||
link: { title: "Learn more", target: "/settings" },
|
||||
},
|
||||
{
|
||||
icon: "monetization_on",
|
||||
title: "Feature",
|
||||
description:
|
||||
"Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nam vel vero quia tenetur obcaecati. Distinctio nesciunt obcaecati deserunt.",
|
||||
link: { title: "Learn more", target: "/settings" },
|
||||
},
|
||||
{
|
||||
icon: "group_work",
|
||||
title: "Feature",
|
||||
description:
|
||||
"Lorem ipsum dolor, sit amet consectetur adipisicing elit. Nam vel vero quia tenetur obcaecati. Distinctio nesciunt obcaecati deserunt.",
|
||||
link: { title: "Learn more", target: "/settings" },
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -1,166 +0,0 @@
|
||||
<template>
|
||||
<footer class="flex flex-col p-6">
|
||||
<nav class="grid gap-4 grid-cols-2 md:grid-cols-4">
|
||||
<div class="flex flex-col space-y-2">
|
||||
<h4 class="my-2">Hoppscotch</h4>
|
||||
<ul class="space-y-4">
|
||||
<li>
|
||||
<SmartChangeLanguage />
|
||||
</li>
|
||||
<li>
|
||||
<SmartColorModePicker />
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="flex flex-col space-y-2">
|
||||
<h4 class="my-2">Solutions</h4>
|
||||
<ul class="space-y-2">
|
||||
<li
|
||||
v-for="(item, index) in navigation.solutions"
|
||||
:key="`item-${index}`"
|
||||
>
|
||||
<SmartAnchor
|
||||
:label="item.name"
|
||||
:to="item.link"
|
||||
class="footer-nav"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="flex flex-col space-y-2">
|
||||
<h4 class="my-2">Platform</h4>
|
||||
<ul class="space-y-2">
|
||||
<li
|
||||
v-for="(item, index) in navigation.platform"
|
||||
:key="`item-${index}`"
|
||||
>
|
||||
<SmartAnchor
|
||||
:label="item.name"
|
||||
:to="item.link"
|
||||
class="footer-nav"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="flex flex-col space-y-2">
|
||||
<h4 class="my-2">Company</h4>
|
||||
<ul class="space-y-2">
|
||||
<li
|
||||
v-for="(item, index) in navigation.company"
|
||||
:key="`item-${index}`"
|
||||
>
|
||||
<SmartAnchor
|
||||
:label="item.name"
|
||||
:to="item.link"
|
||||
class="footer-nav"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
|
||||
export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
navigation: {
|
||||
solutions: [
|
||||
{
|
||||
name: "RESTful",
|
||||
link: "/",
|
||||
},
|
||||
{
|
||||
name: "WebSocket",
|
||||
link: "/realtime",
|
||||
},
|
||||
{
|
||||
name: "SSE",
|
||||
link: "/realtime",
|
||||
},
|
||||
{
|
||||
name: "Socket.IO",
|
||||
link: "/realtime",
|
||||
},
|
||||
{
|
||||
name: "MQTT",
|
||||
link: "/realtime",
|
||||
},
|
||||
{
|
||||
name: "GraphQL",
|
||||
link: "/graphql",
|
||||
},
|
||||
],
|
||||
platform: [
|
||||
{
|
||||
name: "API Designing",
|
||||
link: "/",
|
||||
},
|
||||
{
|
||||
name: "API Development",
|
||||
link: "/",
|
||||
},
|
||||
{
|
||||
name: "API Testing",
|
||||
link: "/",
|
||||
},
|
||||
{
|
||||
name: "API Deployment",
|
||||
link: "/",
|
||||
},
|
||||
{
|
||||
name: "API Documentation",
|
||||
link: "/documentation",
|
||||
},
|
||||
{
|
||||
name: "Integrations",
|
||||
link: "/",
|
||||
},
|
||||
],
|
||||
company: [
|
||||
{
|
||||
name: "About",
|
||||
link: "/",
|
||||
},
|
||||
{
|
||||
name: "Careers",
|
||||
link: "/careers",
|
||||
},
|
||||
{
|
||||
name: "Support",
|
||||
link: "/",
|
||||
},
|
||||
{
|
||||
name: "Contact",
|
||||
link: "/",
|
||||
},
|
||||
{
|
||||
name: "Blog",
|
||||
link: "https://blog.hoppscotch.io",
|
||||
},
|
||||
{
|
||||
name: "Community",
|
||||
link: "/",
|
||||
},
|
||||
{
|
||||
name: "Open Source",
|
||||
link: "https://github.com/hoppscotch",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.footer-nav {
|
||||
@apply px-2 py-1;
|
||||
@apply -mx-2 -my-1;
|
||||
@apply hover:text-secondaryDark;
|
||||
@apply focus-visible:text-secondaryDark;
|
||||
}
|
||||
</style>
|
||||
@@ -1,72 +0,0 @@
|
||||
<template>
|
||||
<div class="flex flex-col p-6 relative">
|
||||
<div class="flex flex-col mt-16 items-center justify-center">
|
||||
<h2
|
||||
class="
|
||||
font-bold
|
||||
text-accent text-center
|
||||
leading-none
|
||||
tracking-tighter
|
||||
text-4xl
|
||||
md:text-6xl
|
||||
lg:text-8xl
|
||||
"
|
||||
>
|
||||
Open Source
|
||||
</h2>
|
||||
<h3
|
||||
class="
|
||||
font-extrabold
|
||||
my-4
|
||||
text-center text-secondaryDark
|
||||
leading-none
|
||||
tracking-tighter
|
||||
text-3xl
|
||||
md:text-4xl
|
||||
lg:text-5xl
|
||||
"
|
||||
>
|
||||
API Development Ecosystem
|
||||
</h3>
|
||||
<p class="my-4 text-lg text-center max-w-2xl">
|
||||
Thousands of developers and companies build, ship, and maintain their
|
||||
APIs on Hoppscotch — the transparent and most flexible API development
|
||||
ecosystem in the world.
|
||||
</p>
|
||||
<div class="flex space-x-4 my-8 justify-center items-center">
|
||||
<ButtonPrimary
|
||||
label="Get Started"
|
||||
icon="arrow_forward"
|
||||
reverse
|
||||
large
|
||||
@click.native="showLogin = true"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
to="https://github.com/hoppscotch/hoppscotch"
|
||||
blank
|
||||
filled
|
||||
outline
|
||||
label="GitHub"
|
||||
svg="github"
|
||||
large
|
||||
:shortcut="['30k Stars']"
|
||||
/>
|
||||
</div>
|
||||
<LandingStats />
|
||||
<LandingScreenshot />
|
||||
</div>
|
||||
<FirebaseLogin :show="showLogin" @hide-modal="showLogin = false" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
|
||||
export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
showLogin: false,
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -1,19 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
class="
|
||||
bg-gradient-to-r
|
||||
from-gradientFrom
|
||||
via-gradientVia
|
||||
to-gradientTo
|
||||
flex flex-col
|
||||
items-center
|
||||
justify-center
|
||||
"
|
||||
>
|
||||
<img
|
||||
src="images/screenshots/light_rest.png"
|
||||
alt="Screenshot"
|
||||
class="rounded-lg ring-dividerLight mt-8 max-w-5/6 ring-4"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1,29 +0,0 @@
|
||||
<template>
|
||||
<div class="flex space-x-16 p-6">
|
||||
<div v-for="(stat, index) in stats" :key="`stat-${index}`">
|
||||
<span class="text-xl">
|
||||
{{ stat.count }}<span class="text-secondaryLight">+</span>
|
||||
</span>
|
||||
<br />
|
||||
<span class="text-sm">
|
||||
{{ stat.audience }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
|
||||
export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
stats: [
|
||||
{ count: "350k", audience: "Developers" },
|
||||
{ count: "5k", audience: "Organizations" },
|
||||
{ count: "1m", audience: "Requests" },
|
||||
],
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -1,46 +0,0 @@
|
||||
<template>
|
||||
<div class="bg-primaryLight rounded flex flex-col mx-6 p-4">
|
||||
<div class="flex flex-col items-center">
|
||||
<p class="my-4 text-center tracking-widest">EMPOWERING DEVELOPERS FROM</p>
|
||||
</div>
|
||||
<div class="grid gap-4 grid-cols-3 md:grid-cols-4 lg:grid-cols-6">
|
||||
<div
|
||||
v-for="(user, index) in users"
|
||||
:key="`user-${index}`"
|
||||
class="flex-col px-4 inline-flex items-center justify-center"
|
||||
>
|
||||
<img
|
||||
:src="`/images/users/${user.image}`"
|
||||
alt="Profile picture"
|
||||
loading="lazy"
|
||||
class="flex-col object-contain object-center h-24 w-24 inline-flex"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
|
||||
export default defineComponent({
|
||||
data() {
|
||||
return {
|
||||
users: [
|
||||
{ title: "Accenture", image: "accenture.svg" },
|
||||
{ title: "ByteDance", image: "bytedance.svg" },
|
||||
{ title: "Decathlon", image: "decathlon.svg" },
|
||||
{ title: "GitHub", image: "github.svg" },
|
||||
{ title: "Gojek", image: "gojek.svg" },
|
||||
{ title: "Google", image: "google.svg" },
|
||||
{ title: "Microsoft", image: "microsoft.svg" },
|
||||
{ title: "PayTM", image: "paytm.svg" },
|
||||
{ title: "Twilio", image: "twilio.svg" },
|
||||
{ title: "Udemy", image: "udemy.svg" },
|
||||
{ title: "Zendesk", image: "zendesk.svg" },
|
||||
{ title: "Zoho", image: "zoho.svg" },
|
||||
],
|
||||
}
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -1,153 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
class="
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
top-lowerSecondaryStickyFold
|
||||
pl-4
|
||||
z-10
|
||||
sticky
|
||||
items-center
|
||||
justify-between
|
||||
"
|
||||
>
|
||||
<label class="font-semibold text-secondaryLight">
|
||||
{{ $t("response.body") }}
|
||||
</label>
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
v-if="response.body"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="
|
||||
previewEnabled ? $t('hide.preview') : $t('response.preview_html')
|
||||
"
|
||||
:icon="!previewEnabled ? 'visibility' : 'visibility_off'"
|
||||
@click.native.prevent="togglePreview"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-if="response.body"
|
||||
ref="downloadResponse"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('action.download_file')"
|
||||
:icon="downloadIcon"
|
||||
@click.native="downloadResponse"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-if="response.body"
|
||||
ref="copyResponse"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('action.copy')"
|
||||
:icon="copyIcon"
|
||||
@click.native="copyResponse"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative">
|
||||
<SmartAceEditor
|
||||
:value="responseBodyText"
|
||||
:lang="'html'"
|
||||
:options="{
|
||||
maxLines: Infinity,
|
||||
minLines: 16,
|
||||
autoScrollEditorIntoView: true,
|
||||
readOnly: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false,
|
||||
}"
|
||||
styles="border-b border-dividerLight"
|
||||
/>
|
||||
<iframe
|
||||
ref="previewFrame"
|
||||
:class="{ hidden: !previewEnabled }"
|
||||
class="covers-response"
|
||||
src="about:blank"
|
||||
></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import TextContentRendererMixin from "./mixins/TextContentRendererMixin"
|
||||
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
||||
|
||||
export default defineComponent({
|
||||
mixins: [TextContentRendererMixin],
|
||||
props: {
|
||||
response: { type: Object, default: () => {} },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
downloadIcon: "save_alt",
|
||||
copyIcon: "content_copy",
|
||||
previewEnabled: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
downloadResponse() {
|
||||
const dataToWrite = this.responseBodyText
|
||||
const file = new Blob([dataToWrite], { type: "text/html" })
|
||||
const a = document.createElement("a")
|
||||
const url = URL.createObjectURL(file)
|
||||
a.href = url
|
||||
// TODO get uri from meta
|
||||
a.download = `${url.split("/").pop().split("#")[0].split("?")[0]}`
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
this.downloadIcon = "done"
|
||||
this.$toast.success(this.$t("state.download_started"), {
|
||||
icon: "downloading",
|
||||
})
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a)
|
||||
URL.revokeObjectURL(url)
|
||||
this.downloadIcon = "save_alt"
|
||||
}, 1000)
|
||||
},
|
||||
copyResponse() {
|
||||
copyToClipboard(this.responseBodyText)
|
||||
this.copyIcon = "done"
|
||||
this.$toast.success(this.$t("state.copied_to_clipboard"), {
|
||||
icon: "content_paste",
|
||||
})
|
||||
setTimeout(() => (this.copyIcon = "content_copy"), 1000)
|
||||
},
|
||||
togglePreview() {
|
||||
this.previewEnabled = !this.previewEnabled
|
||||
if (this.previewEnabled) {
|
||||
if (
|
||||
this.$refs.previewFrame.getAttribute("data-previewing-url") ===
|
||||
this.url
|
||||
)
|
||||
return
|
||||
// Use DOMParser to parse document HTML.
|
||||
const previewDocument = new DOMParser().parseFromString(
|
||||
this.responseBodyText,
|
||||
"text/html"
|
||||
)
|
||||
// Inject <base href="..."> tag to head, to fix relative CSS/HTML paths.
|
||||
previewDocument.head.innerHTML =
|
||||
`<base href="${this.url}">` + previewDocument.head.innerHTML
|
||||
// Finally, set the iframe source to the resulting HTML.
|
||||
this.$refs.previewFrame.srcdoc =
|
||||
previewDocument.documentElement.outerHTML
|
||||
this.$refs.previewFrame.setAttribute("data-previewing-url", this.url)
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.covers-response {
|
||||
@apply absolute;
|
||||
@apply inset-0;
|
||||
@apply bg-white;
|
||||
@apply h-full;
|
||||
@apply w-full;
|
||||
@apply border;
|
||||
@apply border-dividerLight;
|
||||
}
|
||||
</style>
|
||||
@@ -1,123 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
class="
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
top-lowerSecondaryStickyFold
|
||||
pl-4
|
||||
z-10
|
||||
sticky
|
||||
items-center
|
||||
justify-between
|
||||
"
|
||||
>
|
||||
<label class="font-semibold text-secondaryLight">
|
||||
{{ $t("response.body") }}
|
||||
</label>
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
v-if="response.body"
|
||||
ref="downloadResponse"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('action.download_file')"
|
||||
:icon="downloadIcon"
|
||||
@click.native="downloadResponse"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-if="response.body"
|
||||
ref="copyResponse"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('action.copy')"
|
||||
:icon="copyIcon"
|
||||
@click.native="copyResponse"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative">
|
||||
<SmartAceEditor
|
||||
:value="jsonBodyText"
|
||||
:lang="'json'"
|
||||
:provide-outline="true"
|
||||
:options="{
|
||||
maxLines: Infinity,
|
||||
minLines: 16,
|
||||
autoScrollEditorIntoView: true,
|
||||
readOnly: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false,
|
||||
}"
|
||||
styles="border-b border-dividerLight"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import TextContentRendererMixin from "./mixins/TextContentRendererMixin"
|
||||
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
||||
|
||||
export default defineComponent({
|
||||
mixins: [TextContentRendererMixin],
|
||||
props: {
|
||||
response: { type: Object, default: () => {} },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
downloadIcon: "save_alt",
|
||||
copyIcon: "content_copy",
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
jsonBodyText() {
|
||||
try {
|
||||
return JSON.stringify(JSON.parse(this.responseBodyText), null, 2)
|
||||
} catch (e) {
|
||||
// Most probs invalid JSON was returned, so drop prettification (should we warn ?)
|
||||
return this.responseBodyText
|
||||
}
|
||||
},
|
||||
responseType() {
|
||||
return (
|
||||
this.response.headers.find(
|
||||
(h) => h.key.toLowerCase() === "content-type"
|
||||
).value || ""
|
||||
)
|
||||
.split(";")[0]
|
||||
.toLowerCase()
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
downloadResponse() {
|
||||
const dataToWrite = this.responseBodyText
|
||||
const file = new Blob([dataToWrite], { type: "application/json" })
|
||||
const a = document.createElement("a")
|
||||
const url = URL.createObjectURL(file)
|
||||
a.href = url
|
||||
// TODO get uri from meta
|
||||
a.download = `${url.split("/").pop().split("#")[0].split("?")[0]}`
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
this.downloadIcon = "done"
|
||||
this.$toast.success(this.$t("state.download_started"), {
|
||||
icon: "downloading",
|
||||
})
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a)
|
||||
URL.revokeObjectURL(url)
|
||||
this.downloadIcon = "save_alt"
|
||||
}, 1000)
|
||||
},
|
||||
copyResponse() {
|
||||
copyToClipboard(this.responseBodyText)
|
||||
this.copyIcon = "done"
|
||||
this.$toast.success(this.$t("state.copied_to_clipboard"), {
|
||||
icon: "content_paste",
|
||||
})
|
||||
setTimeout(() => (this.copyIcon = "content_copy"), 1000)
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -1,114 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
class="
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
top-lowerSecondaryStickyFold
|
||||
pl-4
|
||||
z-10
|
||||
sticky
|
||||
items-center
|
||||
justify-between
|
||||
"
|
||||
>
|
||||
<label class="font-semibold text-secondaryLight">
|
||||
{{ $t("response.body") }}
|
||||
</label>
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
v-if="response.body"
|
||||
ref="downloadResponse"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('action.download_file')"
|
||||
:icon="downloadIcon"
|
||||
@click.native="downloadResponse"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-if="response.body"
|
||||
ref="copyResponse"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('action.copy')"
|
||||
:icon="copyIcon"
|
||||
@click.native="copyResponse"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative">
|
||||
<SmartAceEditor
|
||||
:value="responseBodyText"
|
||||
:lang="'plain_text'"
|
||||
:options="{
|
||||
maxLines: Infinity,
|
||||
minLines: 16,
|
||||
autoScrollEditorIntoView: true,
|
||||
readOnly: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false,
|
||||
}"
|
||||
styles="border-b border-dividerLight"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import TextContentRendererMixin from "./mixins/TextContentRendererMixin"
|
||||
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
||||
|
||||
export default defineComponent({
|
||||
mixins: [TextContentRendererMixin],
|
||||
props: {
|
||||
response: { type: Object, default: () => {} },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
downloadIcon: "save_alt",
|
||||
copyIcon: "content_copy",
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
responseType() {
|
||||
return (
|
||||
this.response.headers.find(
|
||||
(h) => h.key.toLowerCase() === "content-type"
|
||||
).value || ""
|
||||
)
|
||||
.split(";")[0]
|
||||
.toLowerCase()
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
downloadResponse() {
|
||||
const dataToWrite = this.responseBodyText
|
||||
const file = new Blob([dataToWrite], { type: this.responseType })
|
||||
const a = document.createElement("a")
|
||||
const url = URL.createObjectURL(file)
|
||||
a.href = url
|
||||
// TODO get uri from meta
|
||||
a.download = `${url.split("/").pop().split("#")[0].split("?")[0]}`
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
this.downloadIcon = "done"
|
||||
this.$toast.success(this.$t("state.download_started"), {
|
||||
icon: "downloading",
|
||||
})
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a)
|
||||
URL.revokeObjectURL(url)
|
||||
this.downloadIcon = "save_alt"
|
||||
}, 1000)
|
||||
},
|
||||
copyResponse() {
|
||||
copyToClipboard(this.responseBodyText)
|
||||
this.copyIcon = "done"
|
||||
this.$toast.success(this.$t("state.copied_to_clipboard"), {
|
||||
icon: "content_paste",
|
||||
})
|
||||
setTimeout(() => (this.copyIcon = "content_copy"), 1000)
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -1,114 +0,0 @@
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
class="
|
||||
bg-primary
|
||||
border-b border-dividerLight
|
||||
flex flex-1
|
||||
top-lowerSecondaryStickyFold
|
||||
pl-4
|
||||
z-10
|
||||
sticky
|
||||
items-center
|
||||
justify-between
|
||||
"
|
||||
>
|
||||
<label class="font-semibold text-secondaryLight">
|
||||
{{ $t("response.body") }}
|
||||
</label>
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
v-if="response.body"
|
||||
ref="downloadResponse"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('action.download_file')"
|
||||
:icon="downloadIcon"
|
||||
@click.native="downloadResponse"
|
||||
/>
|
||||
<ButtonSecondary
|
||||
v-if="response.body"
|
||||
ref="copyResponse"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('action.copy')"
|
||||
:icon="copyIcon"
|
||||
@click.native="copyResponse"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="relative">
|
||||
<SmartAceEditor
|
||||
:value="responseBodyText"
|
||||
:lang="'xml'"
|
||||
:options="{
|
||||
maxLines: Infinity,
|
||||
minLines: 16,
|
||||
autoScrollEditorIntoView: true,
|
||||
readOnly: true,
|
||||
showPrintMargin: false,
|
||||
useWorker: false,
|
||||
}"
|
||||
styles="border-b border-dividerLight"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import TextContentRendererMixin from "./mixins/TextContentRendererMixin"
|
||||
import { copyToClipboard } from "~/helpers/utils/clipboard"
|
||||
|
||||
export default defineComponent({
|
||||
mixins: [TextContentRendererMixin],
|
||||
props: {
|
||||
response: { type: Object, default: () => {} },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
copyIcon: "content_copy",
|
||||
downloadIcon: "save_alt",
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
responseType() {
|
||||
return (
|
||||
this.response.headers.find(
|
||||
(h) => h.key.toLowerCase() === "content-type"
|
||||
).value || ""
|
||||
)
|
||||
.split(";")[0]
|
||||
.toLowerCase()
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
downloadResponse() {
|
||||
const dataToWrite = this.responseBodyText
|
||||
const file = new Blob([dataToWrite], { type: this.responseType })
|
||||
const a = document.createElement("a")
|
||||
const url = URL.createObjectURL(file)
|
||||
a.href = url
|
||||
// TODO get uri from meta
|
||||
a.download = `${url.split("/").pop().split("#")[0].split("?")[0]}`
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
this.downloadIcon = "done"
|
||||
this.$toast.success(this.$t("state.download_started"), {
|
||||
icon: "downloading",
|
||||
})
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(a)
|
||||
URL.revokeObjectURL(url)
|
||||
this.downloadIcon = "save_alt"
|
||||
}, 1000)
|
||||
},
|
||||
copyResponse() {
|
||||
copyToClipboard(this.responseBodyText)
|
||||
this.copyIcon = "done"
|
||||
this.$toast.success(this.$t("state.copied_to_clipboard"), {
|
||||
icon: "content_paste",
|
||||
})
|
||||
setTimeout(() => (this.copyIcon = "content_copy"), 1000)
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -1,282 +0,0 @@
|
||||
<template>
|
||||
<div class="show-if-initialized" :class="{ initialized }">
|
||||
<pre ref="editor" :class="styles"></pre>
|
||||
<div
|
||||
v-if="provideOutline"
|
||||
class="
|
||||
bg-primaryLight
|
||||
border-t border-divider
|
||||
flex flex-nowrap flex-1
|
||||
py-1
|
||||
px-4
|
||||
bottom-0
|
||||
z-10
|
||||
sticky
|
||||
overflow-auto
|
||||
hide-scrollbar
|
||||
"
|
||||
>
|
||||
<div
|
||||
v-for="(p, index) in currentPath"
|
||||
:key="`p-${index}`"
|
||||
class="
|
||||
cursor-pointer
|
||||
flex-grow-0 flex-shrink-0
|
||||
text-secondaryLight
|
||||
inline-flex
|
||||
items-center
|
||||
hover:text-secondary
|
||||
"
|
||||
>
|
||||
<span @click="onBlockClick(index)">
|
||||
{{ p }}
|
||||
</span>
|
||||
<i v-if="index + 1 !== currentPath.length" class="mx-2 material-icons">
|
||||
chevron_right
|
||||
</i>
|
||||
<tippy
|
||||
v-if="siblingDropDownIndex == index"
|
||||
ref="options"
|
||||
interactive
|
||||
trigger="click"
|
||||
theme="popover"
|
||||
arrow
|
||||
>
|
||||
<SmartItem
|
||||
v-for="(sibling, siblingIndex) in currentSibling"
|
||||
:key="`p-${index}-sibling-${siblingIndex}`"
|
||||
:label="sibling.key ? sibling.key.value : i"
|
||||
@click.native="goToSibling(sibling)"
|
||||
/>
|
||||
</tippy>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ace from "ace-builds"
|
||||
import "ace-builds/webpack-resolver"
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import jsonParse from "~/helpers/jsonParse"
|
||||
import debounce from "~/helpers/utils/debounce"
|
||||
import outline from "~/helpers/outline"
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
provideOutline: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false,
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
theme: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
lang: {
|
||||
type: String,
|
||||
default: "json",
|
||||
},
|
||||
lint: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
required: false,
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
styles: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
initialized: false,
|
||||
editor: null,
|
||||
cacheValue: "",
|
||||
outline: outline(),
|
||||
currentPath: [],
|
||||
currentSibling: [],
|
||||
siblingDropDownIndex: null,
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
appFontSize() {
|
||||
return getComputedStyle(document.documentElement).getPropertyValue(
|
||||
"--body-font-size"
|
||||
)
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
value(value) {
|
||||
if (value !== this.cacheValue) {
|
||||
this.editor.session.setValue(value, 1)
|
||||
this.cacheValue = value
|
||||
if (this.lint) this.provideLinting(value)
|
||||
}
|
||||
},
|
||||
theme() {
|
||||
this.initialized = false
|
||||
this.editor.setTheme(`ace/theme/${this.defineTheme()}`, () => {
|
||||
this.$nextTick().then(() => {
|
||||
this.initialized = true
|
||||
})
|
||||
})
|
||||
},
|
||||
lang(value) {
|
||||
this.editor.getSession().setMode(`ace/mode/${value}`)
|
||||
},
|
||||
options(value) {
|
||||
this.editor.setOptions(value)
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
const editor = ace.edit(this.$refs.editor, {
|
||||
mode: `ace/mode/${this.lang}`,
|
||||
...this.options,
|
||||
})
|
||||
|
||||
// Set the theme and show the editor only after it's been set to prevent FOUC.
|
||||
editor.setTheme(`ace/theme/${this.defineTheme()}`, () => {
|
||||
this.$nextTick().then(() => {
|
||||
this.initialized = true
|
||||
})
|
||||
})
|
||||
|
||||
editor.setFontSize(this.appFontSize)
|
||||
|
||||
if (this.value) editor.setValue(this.value, 1)
|
||||
|
||||
this.editor = editor
|
||||
this.cacheValue = this.value
|
||||
|
||||
if (this.lang === "json" && this.provideOutline)
|
||||
this.initOutline(this.value)
|
||||
|
||||
editor.on("change", () => {
|
||||
const content = editor.getValue()
|
||||
this.$emit("input", content)
|
||||
this.cacheValue = content
|
||||
|
||||
if (this.provideOutline) debounce(this.initOutline(content), 500)
|
||||
|
||||
if (this.lint) this.provideLinting(content)
|
||||
})
|
||||
|
||||
if (this.lang === "json" && this.provideOutline) {
|
||||
editor.session.selection.on("changeCursor", () => {
|
||||
const index = editor.session.doc.positionToIndex(
|
||||
editor.selection.getCursor(),
|
||||
0
|
||||
)
|
||||
const path = this.outline.genPath(index)
|
||||
if (path.success) {
|
||||
this.currentPath = path.res
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Disable linting, if lint prop is false
|
||||
if (this.lint) this.provideLinting(this.value)
|
||||
},
|
||||
|
||||
destroyed() {
|
||||
this.editor.destroy()
|
||||
},
|
||||
|
||||
methods: {
|
||||
defineTheme() {
|
||||
if (this.theme) {
|
||||
return this.theme
|
||||
}
|
||||
const strip = (str) =>
|
||||
str.replace(/#/g, "").replace(/ /g, "").replace(/"/g, "")
|
||||
return strip(
|
||||
window
|
||||
.getComputedStyle(document.documentElement)
|
||||
.getPropertyValue("--editor-theme")
|
||||
)
|
||||
},
|
||||
|
||||
provideLinting: debounce(function (code) {
|
||||
if (this.lang === "json") {
|
||||
try {
|
||||
jsonParse(code)
|
||||
this.editor.session.setAnnotations([])
|
||||
} catch (e) {
|
||||
const pos = this.editor.session
|
||||
.getDocument()
|
||||
.indexToPosition(e.start, 0)
|
||||
this.editor.session.setAnnotations([
|
||||
{
|
||||
row: pos.row,
|
||||
column: pos.column,
|
||||
text: e.message,
|
||||
type: "error",
|
||||
},
|
||||
])
|
||||
}
|
||||
}
|
||||
}, 2000),
|
||||
|
||||
onBlockClick(index) {
|
||||
if (this.siblingDropDownIndex === index) {
|
||||
this.clearSiblingList()
|
||||
} else {
|
||||
this.currentSibling = this.outline.getSiblings(index)
|
||||
if (this.currentSibling.length) this.siblingDropDownIndex = index
|
||||
}
|
||||
},
|
||||
clearSiblingList() {
|
||||
this.currentSibling = []
|
||||
this.siblingDropDownIndex = null
|
||||
},
|
||||
goToSibling(obj) {
|
||||
this.clearSiblingList()
|
||||
if (obj.start) {
|
||||
const pos = this.editor.session.doc.indexToPosition(obj.start, 0)
|
||||
if (pos) {
|
||||
this.editor.session.selection.moveCursorTo(pos.row, pos.column, true)
|
||||
this.editor.session.selection.clearSelection()
|
||||
this.editor.scrollToLine(pos.row, false, true, null)
|
||||
}
|
||||
}
|
||||
},
|
||||
initOutline: debounce(function (content) {
|
||||
if (this.lang === "json") {
|
||||
try {
|
||||
this.outline.init(content)
|
||||
|
||||
if (content[0] === "[") this.currentPath.push("[]")
|
||||
else this.currentPath.push("{}")
|
||||
} catch (e) {
|
||||
console.log("Outline error: ", e)
|
||||
}
|
||||
}
|
||||
}),
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.show-if-initialized {
|
||||
&.initialized {
|
||||
@apply opacity-100;
|
||||
}
|
||||
|
||||
& > * {
|
||||
@apply transition-none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,68 +0,0 @@
|
||||
<template>
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
v-for="(color, index) of colors"
|
||||
:key="`color-${index}`"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t(getColorModeName(color))"
|
||||
:class="{
|
||||
'bg-primaryLight !text-accent hover:text-accent': color === active,
|
||||
}"
|
||||
class="rounded"
|
||||
:icon="getIcon(color)"
|
||||
@click.native="setBGMode(color)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import {
|
||||
applySetting,
|
||||
HoppBgColor,
|
||||
HoppBgColors,
|
||||
useSetting,
|
||||
} from "~/newstore/settings"
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
return {
|
||||
colors: HoppBgColors,
|
||||
active: useSetting("BG_COLOR"),
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setBGMode(color: HoppBgColor) {
|
||||
applySetting("BG_COLOR", color)
|
||||
},
|
||||
getIcon(color: HoppBgColor) {
|
||||
switch (color) {
|
||||
case "system":
|
||||
return "devices"
|
||||
case "light":
|
||||
return "light_mode"
|
||||
case "dark":
|
||||
return "nights_stay"
|
||||
case "black":
|
||||
return "dark_mode"
|
||||
default:
|
||||
return "devices"
|
||||
}
|
||||
},
|
||||
getColorModeName(colorMode: string) {
|
||||
switch (colorMode) {
|
||||
case "system":
|
||||
return "settings.system_mode"
|
||||
case "light":
|
||||
return "settings.light_mode"
|
||||
case "dark":
|
||||
return "settings.dark_mode"
|
||||
case "black":
|
||||
return "settings.black_mode"
|
||||
default:
|
||||
return "settings.system_mode"
|
||||
}
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -1,21 +0,0 @@
|
||||
<template>
|
||||
<component :is="src" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
src() {
|
||||
return require(`~/assets/icons/${this.name}.svg?inline`)
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -1,292 +0,0 @@
|
||||
<template>
|
||||
<div class="show-if-initialized" :class="{ initialized }">
|
||||
<pre ref="editor" :class="styles"></pre>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ace from "ace-builds"
|
||||
import "ace-builds/webpack-resolver"
|
||||
import "ace-builds/src-noconflict/ext-language_tools"
|
||||
import "ace-builds/src-noconflict/mode-graphqlschema"
|
||||
import * as esprima from "esprima"
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import debounce from "~/helpers/utils/debounce"
|
||||
import {
|
||||
getPreRequestScriptCompletions,
|
||||
getTestScriptCompletions,
|
||||
performPreRequestLinting,
|
||||
performTestLinting,
|
||||
} from "~/helpers/tern"
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
theme: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: null,
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
styles: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
completeMode: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: "none",
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
initialized: false,
|
||||
editor: null,
|
||||
cacheValue: "",
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
appFontSize() {
|
||||
return getComputedStyle(document.documentElement).getPropertyValue(
|
||||
"--body-font-size"
|
||||
)
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
value(value) {
|
||||
if (value !== this.cacheValue) {
|
||||
this.editor.session.setValue(value, 1)
|
||||
this.cacheValue = value
|
||||
if (this.lint) this.provideLinting(value)
|
||||
}
|
||||
},
|
||||
theme() {
|
||||
this.initialized = false
|
||||
this.editor.setTheme(`ace/theme/${this.defineTheme()}`, () => {
|
||||
this.$nextTick()
|
||||
.then(() => {
|
||||
this.initialized = true
|
||||
})
|
||||
.catch(() => {
|
||||
// nextTick shouldn't really ever throw but still
|
||||
this.initialized = true
|
||||
})
|
||||
})
|
||||
},
|
||||
options(value) {
|
||||
this.editor.setOptions(value)
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
// const langTools = ace.require("ace/ext/language_tools")
|
||||
|
||||
const editor = ace.edit(this.$refs.editor, {
|
||||
mode: `ace/mode/javascript`,
|
||||
enableBasicAutocompletion: true,
|
||||
enableLiveAutocompletion: true,
|
||||
...this.options,
|
||||
})
|
||||
|
||||
// Set the theme and show the editor only after it's been set to prevent FOUC.
|
||||
editor.setTheme(`ace/theme/${this.defineTheme()}`, () => {
|
||||
this.$nextTick()
|
||||
.then(() => {
|
||||
this.initialized = true
|
||||
})
|
||||
.catch(() => {
|
||||
// nextTIck shouldn't really ever throw but still
|
||||
this.initialized = true
|
||||
})
|
||||
})
|
||||
|
||||
editor.setFontSize(this.appFontSize)
|
||||
|
||||
const completer = {
|
||||
getCompletions: (
|
||||
editor,
|
||||
_session,
|
||||
{ row, column },
|
||||
_prefix,
|
||||
callback
|
||||
) => {
|
||||
if (this.completeMode === "pre") {
|
||||
getPreRequestScriptCompletions(editor.getValue(), row, column)
|
||||
.then((res) => {
|
||||
callback(
|
||||
null,
|
||||
res.completions.map((r, index, arr) => ({
|
||||
name: r.name,
|
||||
value: r.name,
|
||||
score: (arr.length - index) / arr.length,
|
||||
meta: r.type,
|
||||
}))
|
||||
)
|
||||
})
|
||||
.catch(() => callback(null, []))
|
||||
} else if (this.completeMode === "test") {
|
||||
getTestScriptCompletions(editor.getValue(), row, column)
|
||||
.then((res) => {
|
||||
callback(
|
||||
null,
|
||||
res.completions.map((r, index, arr) => ({
|
||||
name: r.name,
|
||||
value: r.name,
|
||||
score: (arr.length - index) / arr.length,
|
||||
meta: r.type,
|
||||
}))
|
||||
)
|
||||
})
|
||||
.catch(() => callback(null, []))
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
editor.completers = [completer]
|
||||
|
||||
if (this.value) editor.setValue(this.value, 1)
|
||||
|
||||
this.editor = editor
|
||||
this.cacheValue = this.value
|
||||
|
||||
editor.on("change", () => {
|
||||
const content = editor.getValue()
|
||||
this.$emit("input", content)
|
||||
this.cacheValue = content
|
||||
this.provideLinting(content)
|
||||
})
|
||||
|
||||
this.provideLinting(this.value)
|
||||
},
|
||||
|
||||
destroyed() {
|
||||
this.editor.destroy()
|
||||
},
|
||||
|
||||
methods: {
|
||||
defineTheme() {
|
||||
if (this.theme) {
|
||||
return this.theme
|
||||
}
|
||||
const strip = (str) =>
|
||||
str.replace(/#/g, "").replace(/ /g, "").replace(/"/g, "")
|
||||
return strip(
|
||||
window
|
||||
.getComputedStyle(document.documentElement)
|
||||
.getPropertyValue("--editor-theme")
|
||||
)
|
||||
},
|
||||
|
||||
provideLinting: debounce(function (code) {
|
||||
let results = []
|
||||
|
||||
const lintFunc =
|
||||
this.completeMode === "pre"
|
||||
? performPreRequestLinting
|
||||
: performTestLinting
|
||||
|
||||
lintFunc(code)
|
||||
.then((semanticLints) => {
|
||||
results = results.concat(
|
||||
semanticLints.map((lint) => ({
|
||||
row: lint.from.line,
|
||||
column: lint.from.ch,
|
||||
text: `[semantic] ${lint.message}`,
|
||||
type: "error",
|
||||
}))
|
||||
)
|
||||
|
||||
try {
|
||||
const res = esprima.parseScript(code, { tolerant: true })
|
||||
if (res.errors && res.errors.length > 0) {
|
||||
results = results.concat(
|
||||
res.errors.map((err) => {
|
||||
const pos = this.editor.session
|
||||
.getDocument()
|
||||
.indexToPosition(err.index, 0)
|
||||
|
||||
return {
|
||||
row: pos.row,
|
||||
column: pos.column,
|
||||
text: `[syntax] ${err.description}`,
|
||||
type: "error",
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
} catch (e) {
|
||||
const pos = this.editor.session
|
||||
.getDocument()
|
||||
.indexToPosition(e.index, 0)
|
||||
results = results.concat([
|
||||
{
|
||||
row: pos.row,
|
||||
column: pos.column,
|
||||
text: `[syntax] ${e.description}`,
|
||||
type: "error",
|
||||
},
|
||||
])
|
||||
}
|
||||
|
||||
this.editor.session.setAnnotations(results)
|
||||
})
|
||||
.catch(() => {
|
||||
try {
|
||||
const res = esprima.parseScript(code, { tolerant: true })
|
||||
if (res.errors && res.errors.length > 0) {
|
||||
results = results.concat(
|
||||
res.errors.map((err) => {
|
||||
const pos = this.editor.session
|
||||
.getDocument()
|
||||
.indexToPosition(err.index, 0)
|
||||
|
||||
return {
|
||||
row: pos.row,
|
||||
column: pos.column,
|
||||
text: `[syntax] ${err.description}`,
|
||||
type: "error",
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
} catch (e) {
|
||||
const pos = this.editor.session
|
||||
.getDocument()
|
||||
.indexToPosition(e.index, 0)
|
||||
results = results.concat([
|
||||
{
|
||||
row: pos.row,
|
||||
column: pos.column,
|
||||
text: `[syntax] ${e.description}`,
|
||||
type: "error",
|
||||
},
|
||||
])
|
||||
}
|
||||
|
||||
this.editor.session.setAnnotations(results)
|
||||
})
|
||||
}, 2000),
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.show-if-initialized {
|
||||
&.initialized {
|
||||
@apply opacity-100;
|
||||
}
|
||||
|
||||
& > * {
|
||||
@apply transition-none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,22 +0,0 @@
|
||||
<template>
|
||||
<svg
|
||||
class="h-4 animate-spin w-4"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<circle
|
||||
class="opacity-25"
|
||||
cx="12"
|
||||
cy="12"
|
||||
r="10"
|
||||
stroke="currentColor"
|
||||
stroke-width="4"
|
||||
/>
|
||||
<path
|
||||
class="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -1,81 +0,0 @@
|
||||
<template>
|
||||
<SmartModal v-if="show" :title="$t('team.new')" @close="hideModal">
|
||||
<template #body>
|
||||
<div class="flex flex-col px-2">
|
||||
<input
|
||||
id="selectLabelTeamAdd"
|
||||
v-model="name"
|
||||
v-focus
|
||||
class="input floating-input"
|
||||
placeholder=" "
|
||||
type="text"
|
||||
@keyup.enter="addNewTeam"
|
||||
/>
|
||||
<label for="selectLabelTeamAdd">
|
||||
{{ $t("action.label") }}
|
||||
</label>
|
||||
</div>
|
||||
</template>
|
||||
<template #footer>
|
||||
<span>
|
||||
<ButtonPrimary :label="$t('action.save')" @click.native="addNewTeam" />
|
||||
<ButtonSecondary
|
||||
:label="$t('action.cancel')"
|
||||
@click.native="hideModal"
|
||||
/>
|
||||
</span>
|
||||
</template>
|
||||
</SmartModal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import * as teamUtils from "~/helpers/teams/utils"
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
show: Boolean,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addNewTeam() {
|
||||
// We save the user input in case of an error
|
||||
const name = this.name
|
||||
// We clear it early to give the UI a snappy feel
|
||||
this.name = ""
|
||||
if (!name) {
|
||||
this.$toast.error(this.$t("empty.team_name"), {
|
||||
icon: "error_outline",
|
||||
})
|
||||
return
|
||||
}
|
||||
if (name !== null && name.replace(/\s/g, "").length < 6) {
|
||||
this.$toast.error(this.$t("team.name_length_insufficient"), {
|
||||
icon: "error_outline",
|
||||
})
|
||||
return
|
||||
}
|
||||
// Call to the graphql mutation
|
||||
teamUtils
|
||||
.createTeam(this.$apollo, name)
|
||||
.then(() => {
|
||||
this.hideModal()
|
||||
})
|
||||
.catch((e) => {
|
||||
console.error(e)
|
||||
// We restore the initial user input
|
||||
this.name = name
|
||||
})
|
||||
this.hideModal()
|
||||
},
|
||||
hideModal() {
|
||||
this.name = null
|
||||
this.$emit("hide-modal")
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -1,387 +0,0 @@
|
||||
<template>
|
||||
<SmartModal v-if="show" :title="$t('team.edit')" @close="hideModal">
|
||||
<template #body>
|
||||
<div class="flex flex-col px-2">
|
||||
<div class="flex relative">
|
||||
<input
|
||||
id="selectLabelTeamEdit"
|
||||
v-model="name"
|
||||
v-focus
|
||||
class="input floating-input"
|
||||
placeholder=" "
|
||||
type="text"
|
||||
@keyup.enter="saveTeam"
|
||||
/>
|
||||
<label for="selectLabelTeamEdit">
|
||||
{{ $t("action.label") }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="flex flex-1 justify-between items-center">
|
||||
<label for="memberList" class="p-4">
|
||||
{{ $t("team.members") }}
|
||||
</label>
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
icon="add"
|
||||
:label="$t('add.new')"
|
||||
@click.native="addTeamMember"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divide-y divide-dividerLight border-divider border rounded">
|
||||
<div
|
||||
v-for="(member, index) in members"
|
||||
:key="`member-${index}`"
|
||||
class="divide-x divide-dividerLight flex"
|
||||
>
|
||||
<input
|
||||
class="bg-transparent flex flex-1 py-2 px-4"
|
||||
:placeholder="$t('team.email')"
|
||||
:name="'param' + index"
|
||||
:value="member.user.email"
|
||||
readonly
|
||||
/>
|
||||
<span>
|
||||
<tippy
|
||||
:ref="`memberOptions-${index}`"
|
||||
interactive
|
||||
trigger="click"
|
||||
theme="popover"
|
||||
arrow
|
||||
>
|
||||
<template #trigger>
|
||||
<span class="select-wrapper">
|
||||
<input
|
||||
class="
|
||||
bg-transparent
|
||||
cursor-pointer
|
||||
flex flex-1
|
||||
py-2
|
||||
px-4
|
||||
"
|
||||
:placeholder="$t('team.permissions')"
|
||||
:name="'value' + index"
|
||||
:value="
|
||||
typeof member.role === 'string'
|
||||
? member.role
|
||||
: JSON.stringify(member.role)
|
||||
"
|
||||
readonly
|
||||
/>
|
||||
</span>
|
||||
</template>
|
||||
<SmartItem
|
||||
label="OWNER"
|
||||
@click.native="updateMemberRole(index, 'OWNER')"
|
||||
/>
|
||||
<SmartItem
|
||||
label="EDITOR"
|
||||
@click.native="updateMemberRole(index, 'EDITOR')"
|
||||
/>
|
||||
<SmartItem
|
||||
label="VIEWER"
|
||||
@click.native="updateMemberRole(index, 'VIEWER')"
|
||||
/>
|
||||
</tippy>
|
||||
</span>
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
id="member"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('action.remove')"
|
||||
icon="remove_circle_outline"
|
||||
color="red"
|
||||
@click.native="removeExistingTeamMember(member.user.uid)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-for="(member, index) in newMembers"
|
||||
:key="`new-member-${index}`"
|
||||
class="divide-x divide-dividerLight flex"
|
||||
>
|
||||
<input
|
||||
v-model="member.key"
|
||||
class="bg-transparent flex flex-1 py-2 px-4"
|
||||
:placeholder="$t('team.email')"
|
||||
:name="'member' + index"
|
||||
autofocus
|
||||
/>
|
||||
<span>
|
||||
<tippy
|
||||
:ref="`newMemberOptions-${index}`"
|
||||
interactive
|
||||
trigger="click"
|
||||
theme="popover"
|
||||
arrow
|
||||
>
|
||||
<template #trigger>
|
||||
<span class="select-wrapper">
|
||||
<input
|
||||
class="
|
||||
bg-transparent
|
||||
cursor-pointer
|
||||
flex flex-1
|
||||
py-2
|
||||
px-4
|
||||
"
|
||||
:placeholder="$t('team.permissions')"
|
||||
:name="'value' + index"
|
||||
:value="
|
||||
typeof member.value === 'string'
|
||||
? member.value
|
||||
: JSON.stringify(member.value)
|
||||
"
|
||||
readonly
|
||||
/>
|
||||
</span>
|
||||
</template>
|
||||
<SmartItem
|
||||
label="OWNER"
|
||||
@click.native="updateNewMemberRole(index, 'OWNER')"
|
||||
/>
|
||||
<SmartItem
|
||||
label="EDITOR"
|
||||
@click.native="updateNewMemberRole(index, 'EDITOR')"
|
||||
/>
|
||||
<SmartItem
|
||||
label="VIEWER"
|
||||
@click.native="updateNewMemberRole(index, 'VIEWER')"
|
||||
/>
|
||||
</tippy>
|
||||
</span>
|
||||
<div class="flex">
|
||||
<ButtonSecondary
|
||||
id="member"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('action.remove')"
|
||||
icon="remove_circle_outline"
|
||||
color="red"
|
||||
@click.native="removeTeamMember(index)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="members.length === 0 && newMembers.length === 0"
|
||||
class="
|
||||
flex flex-col
|
||||
text-secondaryLight
|
||||
p-4
|
||||
items-center
|
||||
justify-center
|
||||
"
|
||||
>
|
||||
<i class="opacity-75 pb-2 material-icons">layers</i>
|
||||
<span class="text-center pb-4">
|
||||
{{ $t("empty.members") }}
|
||||
</span>
|
||||
<ButtonSecondary
|
||||
:label="$t('add.new')"
|
||||
filled
|
||||
@click.native="addTeamMember"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #footer>
|
||||
<span>
|
||||
<ButtonPrimary :label="$t('action.save')" @click.native="saveTeam" />
|
||||
<ButtonSecondary
|
||||
:label="$t('action.cancel')"
|
||||
@click.native="hideModal"
|
||||
/>
|
||||
</span>
|
||||
</template>
|
||||
</SmartModal>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import cloneDeep from "lodash/cloneDeep"
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import * as teamUtils from "~/helpers/teams/utils"
|
||||
import TeamMemberAdapter from "~/helpers/teams/TeamMemberAdapter"
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
show: Boolean,
|
||||
editingTeam: { type: Object, default: () => {} },
|
||||
editingteamID: { type: String, default: null },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
rename: null,
|
||||
members: [],
|
||||
newMembers: [],
|
||||
membersAdapter: new TeamMemberAdapter(null),
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
editingTeamCopy() {
|
||||
return this.editingTeam
|
||||
},
|
||||
name: {
|
||||
get() {
|
||||
return this.editingTeam.name
|
||||
},
|
||||
set(name) {
|
||||
this.rename = name
|
||||
},
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
editingteamID(teamID) {
|
||||
this.membersAdapter.changeTeamID(teamID)
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.membersAdapter.members$.subscribe((list) => {
|
||||
this.members = cloneDeep(list)
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
updateMemberRole(id, role) {
|
||||
this.members[id].role = role
|
||||
this.$refs[`memberOptions-${id}`][0].tippy().hide()
|
||||
},
|
||||
updateNewMemberRole(id, role) {
|
||||
this.newMembers[id].value = role
|
||||
this.$refs[`newMemberOptions-${id}`][0].tippy().hide()
|
||||
},
|
||||
addTeamMember() {
|
||||
const member = { key: "", value: "" }
|
||||
this.newMembers.push(member)
|
||||
},
|
||||
removeExistingTeamMember(userID) {
|
||||
teamUtils
|
||||
.removeTeamMember(this.$apollo, userID, this.editingteamID)
|
||||
.then(() => {
|
||||
this.$toast.success(this.$t("team.member_removed"), {
|
||||
icon: "done",
|
||||
})
|
||||
this.hideModal()
|
||||
})
|
||||
.catch((e) => {
|
||||
this.$toast.error(this.$t("error.something_went_wrong"), {
|
||||
icon: "error_outline",
|
||||
})
|
||||
console.error(e)
|
||||
})
|
||||
},
|
||||
removeTeamMember(index) {
|
||||
this.newMembers.splice(index, 1)
|
||||
},
|
||||
validateEmail(emailID) {
|
||||
if (
|
||||
/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/.test(
|
||||
emailID
|
||||
)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
saveTeam() {
|
||||
if (
|
||||
this.$data.rename !== null &&
|
||||
this.$data.rename.replace(/\s/g, "").length < 6
|
||||
) {
|
||||
this.$toast.error(this.$t("team.name_length_insufficient"), {
|
||||
icon: "error_outline",
|
||||
})
|
||||
return
|
||||
}
|
||||
let invalidEmail = false
|
||||
this.$data.newMembers.forEach((element) => {
|
||||
if (!this.validateEmail(element.key)) {
|
||||
this.$toast.error(this.$t("team.invalid_email_format"), {
|
||||
icon: "error_outline",
|
||||
})
|
||||
invalidEmail = true
|
||||
}
|
||||
})
|
||||
if (invalidEmail) return
|
||||
let invalidPermission = false
|
||||
this.$data.newMembers.forEach((element) => {
|
||||
if (!element.value) {
|
||||
this.$toast.error(this.$t("invalid_member_permission"), {
|
||||
icon: "error_outline",
|
||||
})
|
||||
invalidPermission = true
|
||||
}
|
||||
})
|
||||
if (invalidPermission) return
|
||||
this.$data.newMembers.forEach((element) => {
|
||||
// Call to the graphql mutation
|
||||
teamUtils
|
||||
.addTeamMemberByEmail(
|
||||
this.$apollo,
|
||||
element.value,
|
||||
element.key,
|
||||
this.editingteamID
|
||||
)
|
||||
.then(() => {
|
||||
this.$toast.success(this.$t("team.saved"), {
|
||||
icon: "done",
|
||||
})
|
||||
})
|
||||
.catch((e) => {
|
||||
this.$toast.error(e, {
|
||||
icon: "error_outline",
|
||||
})
|
||||
console.error(e)
|
||||
})
|
||||
})
|
||||
this.members.forEach((element) => {
|
||||
teamUtils
|
||||
.updateTeamMemberRole(
|
||||
this.$apollo,
|
||||
element.user.uid,
|
||||
element.role,
|
||||
this.editingteamID
|
||||
)
|
||||
.then(() => {
|
||||
this.$toast.success(this.$t("team.member_role_updated"), {
|
||||
icon: "done",
|
||||
})
|
||||
})
|
||||
.catch((e) => {
|
||||
this.$toast.error(e, {
|
||||
icon: "error_outline",
|
||||
})
|
||||
console.error(e)
|
||||
})
|
||||
})
|
||||
if (this.$data.rename !== null) {
|
||||
const newName =
|
||||
this.name === this.$data.rename ? this.name : this.$data.rename
|
||||
if (!/\S/.test(newName))
|
||||
return this.$toast.error(this.$t("empty.team_name"), {
|
||||
icon: "error_outline",
|
||||
})
|
||||
// Call to the graphql mutation
|
||||
if (this.name !== this.rename)
|
||||
teamUtils
|
||||
.renameTeam(this.$apollo, newName, this.editingteamID)
|
||||
.then(() => {
|
||||
this.$toast.success(this.$t("team.saved"), {
|
||||
icon: "done",
|
||||
})
|
||||
})
|
||||
.catch((e) => {
|
||||
this.$toast.error(this.$t("error.something_went_wrong"), {
|
||||
icon: "error_outline",
|
||||
})
|
||||
console.error(e)
|
||||
})
|
||||
}
|
||||
this.hideModal()
|
||||
},
|
||||
hideModal() {
|
||||
this.rename = null
|
||||
this.newMembers = []
|
||||
this.$emit("hide-modal")
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -1,111 +0,0 @@
|
||||
<template>
|
||||
<div class="border border-dividerLight rounded flex flex-1 items-end">
|
||||
<div class="flex flex-1 items-start">
|
||||
<div class="p-4">
|
||||
<label
|
||||
class="cursor-pointer transition hover:text-secondaryDark"
|
||||
@click="team.myRole === 'OWNER' ? $emit('edit-team') : ''"
|
||||
>
|
||||
{{ team.name || $t("state.nothing_found") }}
|
||||
</label>
|
||||
<div class="flex -space-x-1 mt-2 overflow-hidden">
|
||||
<img
|
||||
v-for="(member, index) in team.members"
|
||||
:key="`member-${index}`"
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="member.user.displayName"
|
||||
:src="member.user.photoURL"
|
||||
:alt="member.user.displayName"
|
||||
class="rounded-full h-5 ring-primary ring-2 w-5 inline-block"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span>
|
||||
<tippy ref="options" interactive trigger="click" theme="popover" arrow>
|
||||
<template #trigger>
|
||||
<ButtonSecondary
|
||||
v-tippy="{ theme: 'tooltip' }"
|
||||
:title="$t('action.more')"
|
||||
icon="more_vert"
|
||||
/>
|
||||
</template>
|
||||
<SmartItem
|
||||
v-if="team.myRole === 'OWNER'"
|
||||
icon="create"
|
||||
:label="$t('action.edit')"
|
||||
@click.native="
|
||||
$emit('edit-team')
|
||||
$refs.options.tippy().hide()
|
||||
"
|
||||
/>
|
||||
<SmartItem
|
||||
v-if="team.myRole === 'OWNER'"
|
||||
icon="delete"
|
||||
color="red"
|
||||
:label="$t('action.delete')"
|
||||
@click.native="
|
||||
deleteTeam()
|
||||
$refs.options.tippy().hide()
|
||||
"
|
||||
/>
|
||||
<SmartItem
|
||||
v-if="!(team.myRole === 'OWNER' && team.ownersCount == 1)"
|
||||
icon="remove"
|
||||
:label="$t('team.exit')"
|
||||
@click.native="
|
||||
exitTeam()
|
||||
$refs.options.tippy().hide()
|
||||
"
|
||||
/>
|
||||
</tippy>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import * as teamUtils from "~/helpers/teams/utils"
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
team: { type: Object, default: () => {} },
|
||||
teamID: { type: String, default: null },
|
||||
},
|
||||
methods: {
|
||||
deleteTeam() {
|
||||
if (!confirm(this.$t("confirm.remove_team"))) return
|
||||
// Call to the graphql mutation
|
||||
teamUtils
|
||||
.deleteTeam(this.$apollo, this.teamID)
|
||||
.then(() => {
|
||||
this.$toast.success(this.$t("team.deleted"), {
|
||||
icon: "done",
|
||||
})
|
||||
})
|
||||
.catch((e) => {
|
||||
this.$toast.error(this.$t("error.something_went_wrong"), {
|
||||
icon: "error_outline",
|
||||
})
|
||||
console.error(e)
|
||||
})
|
||||
},
|
||||
exitTeam() {
|
||||
if (!confirm("Are you sure you want to exit this team?")) return
|
||||
teamUtils
|
||||
.exitTeam(this.$apollo, this.teamID)
|
||||
.then(() => {
|
||||
this.$toast.success(this.$t("team.left"), {
|
||||
icon: "done",
|
||||
})
|
||||
})
|
||||
.catch((e) => {
|
||||
this.$toast.error(this.$t("error.something_went_wrong"), {
|
||||
icon: "error_outline",
|
||||
})
|
||||
console.error(e)
|
||||
})
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -1,128 +0,0 @@
|
||||
<template>
|
||||
<AppSection label="teams">
|
||||
<h4 class="text-secondaryDark">
|
||||
{{ $t("team.title") }}
|
||||
</h4>
|
||||
<div class="mt-1 text-secondaryLight">
|
||||
<SmartAnchor
|
||||
:label="$t('team.join_beta')"
|
||||
to="https://hoppscotch.io/beta"
|
||||
blank
|
||||
class="link"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-4 mt-4">
|
||||
<ButtonSecondary
|
||||
:label="$t('team.create_new')"
|
||||
outline
|
||||
@click.native="displayModalAdd(true)"
|
||||
/>
|
||||
<p v-if="$apollo.queries.myTeams.loading">
|
||||
{{ $t("state.loading") }}
|
||||
</p>
|
||||
<div v-if="myTeams.length === 0" class="flex items-center">
|
||||
<i class="mr-4 material-icons">help_outline</i>
|
||||
{{ $t("empty.teams") }}
|
||||
</div>
|
||||
<div v-else class="grid gap-4 sm:grid-cols-2 md:grid-cols-3">
|
||||
<TeamsTeam
|
||||
v-for="(team, index) in myTeams"
|
||||
:key="`team-${index}`"
|
||||
:team-i-d="team.id"
|
||||
:team="team"
|
||||
@edit-team="editTeam(team, team.id)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<TeamsAdd :show="showModalAdd" @hide-modal="displayModalAdd(false)" />
|
||||
<TeamsEdit
|
||||
:team="myTeams[0]"
|
||||
:show="showModalEdit"
|
||||
:editing-team="editingTeam"
|
||||
:editingteam-i-d="editingteamID"
|
||||
@hide-modal="displayModalEdit(false)"
|
||||
/>
|
||||
</AppSection>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "@nuxtjs/composition-api"
|
||||
import gql from "graphql-tag"
|
||||
import { currentUser$ } from "~/helpers/fb/auth"
|
||||
import { useReadonlyStream } from "~/helpers/utils/composables"
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
return {
|
||||
currentUser: useReadonlyStream(currentUser$, null),
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showModalAdd: false,
|
||||
showModalEdit: false,
|
||||
editingTeam: {},
|
||||
editingteamID: "",
|
||||
me: {},
|
||||
myTeams: [],
|
||||
}
|
||||
},
|
||||
apollo: {
|
||||
me: {
|
||||
query: gql`
|
||||
query GetMe {
|
||||
me {
|
||||
uid
|
||||
eaInvited
|
||||
}
|
||||
}
|
||||
`,
|
||||
pollInterval: 100000,
|
||||
},
|
||||
myTeams: {
|
||||
query: gql`
|
||||
query GetMyTeams {
|
||||
myTeams {
|
||||
id
|
||||
name
|
||||
myRole
|
||||
ownersCount
|
||||
members {
|
||||
user {
|
||||
photoURL
|
||||
displayName
|
||||
email
|
||||
uid
|
||||
}
|
||||
role
|
||||
}
|
||||
}
|
||||
}
|
||||
`,
|
||||
pollInterval: 10000,
|
||||
},
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener("keydown", this._keyListener)
|
||||
},
|
||||
methods: {
|
||||
displayModalAdd(shouldDisplay) {
|
||||
this.showModalAdd = shouldDisplay
|
||||
},
|
||||
displayModalEdit(shouldDisplay) {
|
||||
this.showModalEdit = shouldDisplay
|
||||
|
||||
if (!shouldDisplay) this.resetSelectedData()
|
||||
},
|
||||
editTeam(team, teamID) {
|
||||
this.editingTeam = team
|
||||
this.editingteamID = teamID
|
||||
this.displayModalEdit(true)
|
||||
},
|
||||
resetSelectedData() {
|
||||
this.$data.editingTeam = undefined
|
||||
this.$data.editingteamID = undefined
|
||||
},
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@@ -4,8 +4,10 @@
|
||||
"indexes": "firestore.indexes.json"
|
||||
},
|
||||
"hosting": {
|
||||
"predeploy": ["mv .env.example .env && npm ci && npm run generate"],
|
||||
"public": "dist",
|
||||
"predeploy": [
|
||||
"cd packages/hoppscotch-app && mv .env.example .env && cd ../.. && npm install -g pnpm && pnpm i && pnpm run generate"
|
||||
],
|
||||
"public": "packages/hoppscotch-app/dist",
|
||||
"ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
|
||||
"rewrites": [
|
||||
{
|
||||
|
||||
@@ -1,155 +0,0 @@
|
||||
import { Observable } from "rxjs"
|
||||
import { filter } from "rxjs/operators"
|
||||
import getEnvironmentVariablesFromScript from "./preRequest"
|
||||
import { getEffectiveRESTRequest } from "./utils/EffectiveURL"
|
||||
import { HoppRESTResponse } from "./types/HoppRESTResponse"
|
||||
import { createRESTNetworkRequestStream } from "./network"
|
||||
import runTestScriptWithVariables, {
|
||||
transformResponseForTesting,
|
||||
} from "./postwomanTesting"
|
||||
import { HoppTestData, HoppTestResult } from "./types/HoppTestResult"
|
||||
import { getRESTRequest, setRESTTestResults } from "~/newstore/RESTSession"
|
||||
|
||||
/**
|
||||
* Runs a REST network request along with all the
|
||||
* other side processes (like running test scripts)
|
||||
*/
|
||||
export function runRESTRequest$(): Observable<HoppRESTResponse> {
|
||||
const envs = getEnvironmentVariablesFromScript(
|
||||
getRESTRequest().preRequestScript
|
||||
)
|
||||
|
||||
const effectiveRequest = getEffectiveRESTRequest(getRESTRequest(), {
|
||||
name: "Env",
|
||||
variables: Object.keys(envs).map((key) => {
|
||||
return {
|
||||
key,
|
||||
value: envs[key],
|
||||
}
|
||||
}),
|
||||
})
|
||||
|
||||
const stream = createRESTNetworkRequestStream(effectiveRequest)
|
||||
|
||||
// Run Test Script when request ran successfully
|
||||
const subscription = stream
|
||||
.pipe(filter((res) => res.type === "success"))
|
||||
.subscribe((res) => {
|
||||
const testReport: {
|
||||
report: "" // ¯\_(ツ)_/¯
|
||||
testResults: Array<
|
||||
| {
|
||||
result: "FAIL"
|
||||
message: string
|
||||
styles: { icon: "close"; class: "cl-error-response" }
|
||||
}
|
||||
| {
|
||||
result: "PASS"
|
||||
message: string
|
||||
styles: { icon: "check"; class: "success-response" }
|
||||
}
|
||||
| { startBlock: string; styles: { icon: ""; class: "" } }
|
||||
| { endBlock: true; styles: { icon: ""; class: "" } }
|
||||
>
|
||||
errors: [] // ¯\_(ツ)_/¯
|
||||
} = runTestScriptWithVariables(effectiveRequest.testScript, {
|
||||
response: transformResponseForTesting(res),
|
||||
}) as any
|
||||
|
||||
setRESTTestResults(translateToNewTestResults(testReport))
|
||||
|
||||
subscription.unsubscribe()
|
||||
})
|
||||
|
||||
return stream
|
||||
}
|
||||
|
||||
function isTestPass(x: any): x is {
|
||||
result: "PASS"
|
||||
styles: { icon: "check"; class: "success-response" }
|
||||
} {
|
||||
return x.result !== undefined && x.result === "PASS"
|
||||
}
|
||||
|
||||
function isTestFail(x: any): x is {
|
||||
result: "FAIL"
|
||||
message: string
|
||||
styles: { icon: "close"; class: "cl-error-response" }
|
||||
} {
|
||||
return x.result !== undefined && x.result === "FAIL"
|
||||
}
|
||||
|
||||
function isStartBlock(
|
||||
x: any
|
||||
): x is { startBlock: string; styles: { icon: ""; class: "" } } {
|
||||
return x.startBlock !== undefined
|
||||
}
|
||||
|
||||
function isEndBlock(
|
||||
x: any
|
||||
): x is { endBlock: true; styles: { icon: ""; class: "" } } {
|
||||
return x.endBlock !== undefined
|
||||
}
|
||||
|
||||
function translateToNewTestResults(testReport: {
|
||||
report: "" // ¯\_(ツ)_/¯
|
||||
testResults: Array<
|
||||
| {
|
||||
result: "FAIL"
|
||||
message: string
|
||||
styles: { icon: "close"; class: "cl-error-response" }
|
||||
}
|
||||
| {
|
||||
result: "PASS"
|
||||
message: string
|
||||
styles: { icon: "check"; class: "success-response" }
|
||||
}
|
||||
| { startBlock: string; styles: { icon: ""; class: "" } }
|
||||
| { endBlock: true; styles: { icon: ""; class: "" } }
|
||||
>
|
||||
errors: [] // ¯\_(ツ)_/¯
|
||||
}): HoppTestResult {
|
||||
// Build a stack of test data which we eventually build up based on the results
|
||||
const testsStack: HoppTestData[] = [
|
||||
{
|
||||
description: "root",
|
||||
tests: [],
|
||||
expectResults: [],
|
||||
},
|
||||
]
|
||||
|
||||
testReport.testResults.forEach((result) => {
|
||||
// This is a test block start, push an empty test to the stack
|
||||
if (isStartBlock(result)) {
|
||||
testsStack.push({
|
||||
description: result.startBlock,
|
||||
tests: [],
|
||||
expectResults: [],
|
||||
})
|
||||
} else if (isEndBlock(result)) {
|
||||
// End of the block, pop the stack and add it as a child to the current stack top
|
||||
const testData = testsStack.pop()!
|
||||
testsStack[testsStack.length - 1].tests.push(testData)
|
||||
} else if (isTestPass(result)) {
|
||||
// A normal PASS expectation
|
||||
testsStack[testsStack.length - 1].expectResults.push({
|
||||
status: "pass",
|
||||
message: result.message,
|
||||
})
|
||||
} else if (isTestFail(result)) {
|
||||
// A normal FAIL expectation
|
||||
testsStack[testsStack.length - 1].expectResults.push({
|
||||
status: "fail",
|
||||
message: result.message,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// We should end up with only the root stack entry
|
||||
if (testsStack.length !== 1) throw new Error("Invalid test result structure")
|
||||
|
||||
return {
|
||||
expectResults: testsStack[0].expectResults,
|
||||
tests: testsStack[0].tests,
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
const mimeToMode = {
|
||||
"text/plain": "plain_text",
|
||||
"text/html": "html",
|
||||
"application/xml": "xml",
|
||||
"application/hal+json": "json",
|
||||
"application/vnd.api+json": "json",
|
||||
"application/json": "json",
|
||||
}
|
||||
|
||||
export function getEditorLangForMimeType(mimeType) {
|
||||
return mimeToMode[mimeType] || "plain_text"
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
import jsonLens from "./jsonLens"
|
||||
import rawLens from "./rawLens"
|
||||
import imageLens from "./imageLens"
|
||||
import htmlLens from "./htmlLens"
|
||||
import xmlLens from "./xmlLens"
|
||||
|
||||
export const lenses = [jsonLens, imageLens, htmlLens, xmlLens, rawLens]
|
||||
|
||||
export function getSuitableLenses(response) {
|
||||
const contentType = response.headers.find((h) => h.key === "content-type")
|
||||
|
||||
if (!contentType) return [rawLens]
|
||||
|
||||
const result = []
|
||||
for (const lens of lenses) {
|
||||
if (lens.isSupportedContentType(contentType.value)) result.push(lens)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export function getLensRenderers() {
|
||||
const response = {}
|
||||
for (const lens of lenses) {
|
||||
response[lens.renderer] = lens.rendererImport
|
||||
}
|
||||
return response
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
const rawLens = {
|
||||
lensName: "response.raw",
|
||||
isSupportedContentType: () => true,
|
||||
renderer: "raw",
|
||||
rendererImport: () => import("~/components/lenses/renderers/RawLensRenderer"),
|
||||
}
|
||||
|
||||
export default rawLens
|
||||
@@ -1,124 +0,0 @@
|
||||
import jsonParse from "./jsonParse"
|
||||
|
||||
export default () => {
|
||||
let jsonAST = {}
|
||||
let path = []
|
||||
|
||||
const init = (jsonStr) => {
|
||||
jsonAST = jsonParse(jsonStr)
|
||||
linkParents(jsonAST)
|
||||
}
|
||||
|
||||
const setNewText = (jsonStr) => {
|
||||
init(jsonStr)
|
||||
path = []
|
||||
}
|
||||
|
||||
const linkParents = (node) => {
|
||||
if (node.kind === "Object") {
|
||||
if (node.members) {
|
||||
node.members.forEach((m) => {
|
||||
m.parent = node
|
||||
linkParents(m)
|
||||
})
|
||||
}
|
||||
} else if (node.kind === "Array") {
|
||||
if (node.values) {
|
||||
node.values.forEach((v) => {
|
||||
v.parent = node
|
||||
linkParents(v)
|
||||
})
|
||||
}
|
||||
} else if (node.kind === "Member") {
|
||||
if (node.value) {
|
||||
node.value.parent = node
|
||||
linkParents(node.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const genPath = (index) => {
|
||||
let output = {}
|
||||
path = []
|
||||
let current = jsonAST
|
||||
if (current.kind === "Object") {
|
||||
path.push({ label: "{}", obj: "root" })
|
||||
} else if (current.kind === "Array") {
|
||||
path.push({ label: "[]", obj: "root" })
|
||||
}
|
||||
let over = false
|
||||
|
||||
try {
|
||||
while (!over) {
|
||||
if (current.kind === "Object") {
|
||||
let i = 0
|
||||
let found = false
|
||||
while (i < current.members.length) {
|
||||
const m = current.members[i]
|
||||
if (m.start <= index && m.end >= index) {
|
||||
path.push({ label: m.key.value, obj: m })
|
||||
current = current.members[i]
|
||||
found = true
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
if (!found) over = true
|
||||
} else if (current.kind === "Array") {
|
||||
if (current.values) {
|
||||
let i = 0
|
||||
let found = false
|
||||
while (i < current.values.length) {
|
||||
const m = current.values[i]
|
||||
if (m.start <= index && m.end >= index) {
|
||||
path.push({ label: `[${i.toString()}]`, obj: m })
|
||||
current = current.values[i]
|
||||
found = true
|
||||
break
|
||||
}
|
||||
i++
|
||||
}
|
||||
if (!found) over = true
|
||||
} else over = true
|
||||
} else if (current.kind === "Member") {
|
||||
if (current.value) {
|
||||
if (current.value.start <= index && current.value.end >= index) {
|
||||
current = current.value
|
||||
} else over = true
|
||||
} else over = true
|
||||
} else if (
|
||||
current.kind === "String" ||
|
||||
current.kind === "Number" ||
|
||||
current.kind === "Boolean" ||
|
||||
current.kind === "Null"
|
||||
) {
|
||||
if (current.start <= index && current.end >= index) {
|
||||
path.push({ label: `${current.value}`, obj: current })
|
||||
}
|
||||
over = true
|
||||
}
|
||||
}
|
||||
output = { success: true, res: path.map((p) => p.label) }
|
||||
} catch (e) {
|
||||
output = { success: false, res: e }
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
const getSiblings = (index) => {
|
||||
const parent = path[index]?.obj?.parent
|
||||
if (!parent) return []
|
||||
else if (parent.kind === "Object") {
|
||||
return parent.members
|
||||
} else if (parent.kind === "Array") {
|
||||
return parent.values
|
||||
} else return []
|
||||
}
|
||||
|
||||
return {
|
||||
init,
|
||||
genPath,
|
||||
getSiblings,
|
||||
setNewText,
|
||||
}
|
||||
}
|
||||
@@ -1,321 +0,0 @@
|
||||
import { HoppRESTResponse } from "./types/HoppRESTResponse"
|
||||
|
||||
const styles = {
|
||||
PASS: { icon: "check", class: "success-response" },
|
||||
FAIL: { icon: "close", class: "cl-error-response" },
|
||||
ERROR: { icon: "close", class: "cl-error-response" },
|
||||
}
|
||||
|
||||
type TestScriptResponse = {
|
||||
body: any
|
||||
headers: any[]
|
||||
status: number
|
||||
|
||||
__newRes: HoppRESTResponse
|
||||
}
|
||||
|
||||
type TestScriptVariables = {
|
||||
response: TestScriptResponse
|
||||
}
|
||||
|
||||
type TestReportStartBlock = {
|
||||
startBlock: string
|
||||
}
|
||||
|
||||
type TestReportEndBlock = {
|
||||
endBlock: true
|
||||
}
|
||||
|
||||
type TestReportEntry = {
|
||||
result: "PASS" | "FAIL" | "ERROR"
|
||||
message: string
|
||||
styles: {
|
||||
icon: string
|
||||
}
|
||||
}
|
||||
|
||||
type TestReport = TestReportStartBlock | TestReportEntry | TestReportEndBlock
|
||||
|
||||
export default function runTestScriptWithVariables(
|
||||
script: string,
|
||||
variables?: TestScriptVariables
|
||||
) {
|
||||
const pw = {
|
||||
_errors: [],
|
||||
_testReports: [] as TestReport[],
|
||||
_report: "",
|
||||
expect(value: any) {
|
||||
try {
|
||||
return expect(value, this._testReports)
|
||||
} catch (e) {
|
||||
pw._testReports.push({
|
||||
result: "ERROR",
|
||||
message: e.toString(),
|
||||
styles: styles.ERROR,
|
||||
})
|
||||
}
|
||||
},
|
||||
test: (descriptor: string, func: () => void) =>
|
||||
test(descriptor, func, pw._testReports),
|
||||
// globals that the script is allowed to have access to.
|
||||
}
|
||||
Object.assign(pw, variables)
|
||||
|
||||
// run pre-request script within this function so that it has access to the pw object.
|
||||
// eslint-disable-next-line no-new-func
|
||||
new Function("pw", script)(pw)
|
||||
|
||||
return {
|
||||
report: pw._report,
|
||||
errors: pw._errors,
|
||||
testResults: pw._testReports,
|
||||
}
|
||||
}
|
||||
|
||||
function test(
|
||||
descriptor: string,
|
||||
func: () => void,
|
||||
_testReports: TestReport[]
|
||||
) {
|
||||
_testReports.push({ startBlock: descriptor })
|
||||
try {
|
||||
func()
|
||||
} catch (e) {
|
||||
_testReports.push({ result: "ERROR", message: e, styles: styles.ERROR })
|
||||
}
|
||||
_testReports.push({ endBlock: true })
|
||||
|
||||
// TODO: Organize and generate text report of each {descriptor: true} section in testReports.
|
||||
// add checkmark or x depending on if each testReport is pass=true or pass=false
|
||||
}
|
||||
|
||||
function expect(expectValue: any, _testReports: TestReport[]) {
|
||||
return new Expectation(expectValue, null, _testReports)
|
||||
}
|
||||
|
||||
class Expectation {
|
||||
private expectValue: any
|
||||
private not: true | Expectation
|
||||
private _testReports: TestReport[]
|
||||
|
||||
constructor(
|
||||
expectValue: any,
|
||||
_not: boolean | null,
|
||||
_testReports: TestReport[]
|
||||
) {
|
||||
this.expectValue = expectValue
|
||||
this.not = _not || new Expectation(this.expectValue, true, _testReports)
|
||||
this._testReports = _testReports // this values is used within Test.it, which wraps Expectation and passes _testReports value.
|
||||
}
|
||||
|
||||
private _satisfies(expectValue: any, targetValue?: any): boolean {
|
||||
// Used for testing if two values match the expectation, which could be === OR !==, depending on if not
|
||||
// was used. Expectation#_satisfies prevents the need to have an if(this.not) branch in every test method.
|
||||
// Signature is _satisfies([expectValue,] targetValue): if only one argument is given, it is assumed the targetValue, and expectValue is set to this.expectValue
|
||||
if (!targetValue) {
|
||||
targetValue = expectValue
|
||||
expectValue = this.expectValue
|
||||
}
|
||||
if (this.not === true) {
|
||||
// test the inverse. this.not is always truthly, but an Expectation that is inverted will always be strictly `true`
|
||||
return expectValue !== targetValue
|
||||
} else {
|
||||
return expectValue === targetValue
|
||||
}
|
||||
}
|
||||
|
||||
_fmtNot(message: string) {
|
||||
// given a string with "(not)" in it, replaces with "not" or "", depending if the expectation is expecting the positive or inverse (this._not)
|
||||
if (this.not === true) {
|
||||
return message.replace("(not)", "not ")
|
||||
} else {
|
||||
return message.replace("(not)", "")
|
||||
}
|
||||
}
|
||||
|
||||
_fail(message: string) {
|
||||
return this._testReports.push({
|
||||
result: "FAIL",
|
||||
message,
|
||||
styles: styles.FAIL,
|
||||
})
|
||||
}
|
||||
|
||||
_pass(message: string) {
|
||||
return this._testReports.push({
|
||||
result: "PASS",
|
||||
message,
|
||||
styles: styles.PASS,
|
||||
})
|
||||
}
|
||||
|
||||
// TEST METHODS DEFINED BELOW
|
||||
// these are the usual methods that would follow expect(...)
|
||||
toBe(value: any) {
|
||||
return this._satisfies(value)
|
||||
? this._pass(
|
||||
this._fmtNot(`${this.expectValue} do (not)match with ${value}`)
|
||||
)
|
||||
: this._fail(
|
||||
this._fmtNot(`Expected ${this.expectValue} (not)to be ${value}`)
|
||||
)
|
||||
}
|
||||
|
||||
toHaveProperty(value: string) {
|
||||
return this._satisfies(
|
||||
Object.prototype.hasOwnProperty.call(this.expectValue, value),
|
||||
true
|
||||
)
|
||||
? this._pass(
|
||||
this._fmtNot(`${this.expectValue} do (not)have property ${value}`)
|
||||
)
|
||||
: this._fail(
|
||||
this._fmtNot(
|
||||
`Expected object ${this.expectValue} to (not)have property ${value}`
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
toBeLevel2xx() {
|
||||
const code = parseInt(this.expectValue, 10)
|
||||
if (Number.isNaN(code)) {
|
||||
return this._fail(
|
||||
`Expected 200-level status but could not parse value ${this.expectValue}`
|
||||
)
|
||||
}
|
||||
return this._satisfies(code >= 200 && code < 300, true)
|
||||
? this._pass(
|
||||
this._fmtNot(`${this.expectValue} is (not)a 200-level status`)
|
||||
)
|
||||
: this._fail(
|
||||
this._fmtNot(
|
||||
`Expected ${this.expectValue} to (not)be 200-level status`
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
toBeLevel3xx() {
|
||||
const code = parseInt(this.expectValue, 10)
|
||||
if (Number.isNaN(code)) {
|
||||
return this._fail(
|
||||
`Expected 300-level status but could not parse value ${this.expectValue}`
|
||||
)
|
||||
}
|
||||
return this._satisfies(code >= 300 && code < 400, true)
|
||||
? this._pass(
|
||||
this._fmtNot(`${this.expectValue} is (not)a 300-level status`)
|
||||
)
|
||||
: this._fail(
|
||||
this._fmtNot(
|
||||
`Expected ${this.expectValue} to (not)be 300-level status`
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
toBeLevel4xx() {
|
||||
const code = parseInt(this.expectValue, 10)
|
||||
if (Number.isNaN(code)) {
|
||||
return this._fail(
|
||||
`Expected 400-level status but could not parse value ${this.expectValue}`
|
||||
)
|
||||
}
|
||||
return this._satisfies(code >= 400 && code < 500, true)
|
||||
? this._pass(
|
||||
this._fmtNot(`${this.expectValue} is (not)a 400-level status`)
|
||||
)
|
||||
: this._fail(
|
||||
this._fmtNot(
|
||||
`Expected ${this.expectValue} to (not)be 400-level status`
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
toBeLevel5xx() {
|
||||
const code = parseInt(this.expectValue, 10)
|
||||
if (Number.isNaN(code)) {
|
||||
return this._fail(
|
||||
`Expected 500-level status but could not parse value ${this.expectValue}`
|
||||
)
|
||||
}
|
||||
return this._satisfies(code >= 500 && code < 600, true)
|
||||
? this._pass(
|
||||
this._fmtNot(`${this.expectValue} is (not)a 500-level status`)
|
||||
)
|
||||
: this._fail(
|
||||
this._fmtNot(
|
||||
`Expected ${this.expectValue} to (not)be 500-level status`
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
toHaveLength(expectedLength: number) {
|
||||
const actualLength = this.expectValue.length
|
||||
return this._satisfies(actualLength, expectedLength)
|
||||
? this._pass(
|
||||
this._fmtNot(
|
||||
`Length expectation of (not)being ${expectedLength} is kept`
|
||||
)
|
||||
)
|
||||
: this._fail(
|
||||
this._fmtNot(
|
||||
`Expected length to be ${expectedLength} but actual length was ${actualLength}`
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
toBeType(expectedType: string) {
|
||||
const actualType = typeof this.expectValue
|
||||
if (
|
||||
![
|
||||
"string",
|
||||
"boolean",
|
||||
"number",
|
||||
"object",
|
||||
"undefined",
|
||||
"bigint",
|
||||
"symbol",
|
||||
"function",
|
||||
].includes(expectedType)
|
||||
) {
|
||||
return this._fail(
|
||||
this._fmtNot(
|
||||
`Argument for toBeType should be "string", "boolean", "number", "object", "undefined", "bigint", "symbol" or "function"`
|
||||
)
|
||||
)
|
||||
}
|
||||
return this._satisfies(actualType, expectedType)
|
||||
? this._pass(this._fmtNot(`The type is (not)"${expectedType}"`))
|
||||
: this._fail(
|
||||
this._fmtNot(
|
||||
`Expected type to be "${expectedType}" but actual type was "${actualType}"`
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export function transformResponseForTesting(
|
||||
response: HoppRESTResponse
|
||||
): TestScriptResponse {
|
||||
if (response.type === "loading") {
|
||||
throw new Error("Cannot transform loading responses")
|
||||
}
|
||||
|
||||
if (response.type === "network_fail") {
|
||||
throw new Error("Cannot transform failed responses")
|
||||
}
|
||||
|
||||
let body: any = new TextDecoder("utf-8").decode(response.body)
|
||||
|
||||
// Try parsing to JSON
|
||||
try {
|
||||
body = JSON.parse(body)
|
||||
} catch (_) {}
|
||||
|
||||
return {
|
||||
body,
|
||||
headers: response.headers,
|
||||
status: response.statusCode,
|
||||
|
||||
__newRes: response,
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import {
|
||||
getCurrentEnvironment,
|
||||
getGlobalVariables,
|
||||
} from "~/newstore/environments"
|
||||
|
||||
export default function getEnvironmentVariablesFromScript(script: string) {
|
||||
const _variables: Record<string, string> = {}
|
||||
|
||||
const currentEnv = getCurrentEnvironment()
|
||||
|
||||
for (const variable of currentEnv.variables) {
|
||||
_variables[variable.key] = variable.value
|
||||
}
|
||||
|
||||
const globalEnv = getGlobalVariables()
|
||||
|
||||
if (globalEnv) {
|
||||
for (const variable of globalEnv) {
|
||||
_variables[variable.key] = variable.value
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// the pw object is the proxy by which pre-request scripts can pass variables to the request.
|
||||
// for security and control purposes, this is the only way a pre-request script should modify variables.
|
||||
const pw = {
|
||||
environment: {
|
||||
set: (key: string, value: string) => (_variables[key] = value),
|
||||
},
|
||||
env: {
|
||||
set: (key: string, value: string) => (_variables[key] = value),
|
||||
},
|
||||
// globals that the script is allowed to have access to.
|
||||
}
|
||||
|
||||
// run pre-request script within this function so that it has access to the pw object.
|
||||
// eslint-disable-next-line no-new-func
|
||||
new Function("pw", script)(pw)
|
||||
} catch (_e) {}
|
||||
|
||||
return _variables
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
export function getSourcePrefix(source) {
|
||||
const sourceEmojis = {
|
||||
// Source used for info messages.
|
||||
info: "\tℹ️ [INFO]:\t",
|
||||
// Source used for client to server messages.
|
||||
client: "\t⬅️ [SENT]:\t",
|
||||
// Source used for server to client messages.
|
||||
server: "\t➡️ [RECEIVED]:\t",
|
||||
}
|
||||
if (Object.keys(sourceEmojis).includes(source)) return sourceEmojis[source]
|
||||
return ""
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
<template>
|
||||
<Nuxt />
|
||||
</template>
|
||||
494
locales/cn.json
@@ -1,494 +0,0 @@
|
||||
{
|
||||
"action": {
|
||||
"cancel": "取消",
|
||||
"choose_file": "选择一个文件",
|
||||
"clear": "清除",
|
||||
"clear_all": "清除所有",
|
||||
"connect": "连接",
|
||||
"copy": "Copy",
|
||||
"delete": "删除",
|
||||
"disconnect": "断开",
|
||||
"dismiss": "Dismiss",
|
||||
"download_file": "下载文件",
|
||||
"edit": "编辑",
|
||||
"go_back": "Go back",
|
||||
"label": "标签",
|
||||
"learn_more": "Learn more",
|
||||
"more": "更多的",
|
||||
"new": "新的",
|
||||
"no": "不",
|
||||
"preserve_current": "保持电流",
|
||||
"prettify": "美化",
|
||||
"remove": "消除",
|
||||
"replace_current": "替换当前",
|
||||
"replace_json": "替换为 JSON",
|
||||
"restore": "恢复",
|
||||
"save": "节省",
|
||||
"search": "搜索",
|
||||
"send": "发送",
|
||||
"start": "开始",
|
||||
"stop": "停止",
|
||||
"turn_off": "Turn off",
|
||||
"turn_on": "Turn on",
|
||||
"undo": "撤消",
|
||||
"yes": "是的"
|
||||
},
|
||||
"add": {
|
||||
"new": "Add new",
|
||||
"star": "Add star"
|
||||
},
|
||||
"app": {
|
||||
"chat_with_us": "Chat with us",
|
||||
"contact_us": "联系我们",
|
||||
"copy": "Copy",
|
||||
"documentation": "Documentation",
|
||||
"help": "Help, feedback and</br>documentation",
|
||||
"home": "家",
|
||||
"invite": "Invite",
|
||||
"invite_description": "In Hoppscotch, we designed a simple and intuitive interface for creating and managing your APIs. Hoppscotch is a tool that helps you build, test, document and share your APIs.",
|
||||
"invite_your_friends": "Invite your friends",
|
||||
"join_discord_community": "Join our Discord community",
|
||||
"keyboard_shortcuts": "Keyboard shortcuts",
|
||||
"name": "Hoppscotch",
|
||||
"new_version_found": "New version found. Refresh to update.",
|
||||
"proxy_privacy_policy": "代理隐私政策",
|
||||
"reload": "重新加载",
|
||||
"search": "Search",
|
||||
"share": "Share",
|
||||
"shortcuts": "快捷方式",
|
||||
"spotlight": "聚光灯",
|
||||
"status": "Status",
|
||||
"terms_and_privacy": "Terms and privacy",
|
||||
"twitter": "Twitter",
|
||||
"type_a_command_search": "Type a command or search…",
|
||||
"version": "v2.0",
|
||||
"we_use_cookies": "我们使用cookies",
|
||||
"whats_new": "What's new?",
|
||||
"wiki": "维基"
|
||||
},
|
||||
"auth": {
|
||||
"account_exists": "Account exists with different credential - Login to link both accounts",
|
||||
"all_sign_in_options": "所有登录选项",
|
||||
"continue_with_email": "继续使用电子邮件",
|
||||
"continue_with_github": "继续使用 GitHub",
|
||||
"continue_with_google": "继续使用 Google",
|
||||
"email": "Email",
|
||||
"logged_out": "登出",
|
||||
"login": "登录",
|
||||
"login_success": "登录成功",
|
||||
"login_to_hoppscotch": "登录 Hoppscotch",
|
||||
"logout": "登出",
|
||||
"re_enter_email": "重新输入电子邮件",
|
||||
"send_magic_link": "Send a magic link",
|
||||
"sync": "同步",
|
||||
"we_sent_magic_link": "We sent you a magic link!",
|
||||
"we_sent_magic_link_description": "Check your inbox - we sent an email to {email}. It contains a magic link that will log you in."
|
||||
},
|
||||
"authorization": {
|
||||
"generate_token": "生成令牌",
|
||||
"include_in_url": "包含在网址中",
|
||||
"learn": "学习怎样",
|
||||
"password": "密码",
|
||||
"token": "令牌",
|
||||
"type": "授权类型",
|
||||
"username": "用户名"
|
||||
},
|
||||
"collection": {
|
||||
"created": "Collection created",
|
||||
"edit": "Edit Collection",
|
||||
"invalid_name": "Please provide a valid name for the collection",
|
||||
"my_collections": "我的收藏",
|
||||
"name": "My New Collection",
|
||||
"new": "New Collection",
|
||||
"renamed": "收藏更名",
|
||||
"save_as": "Save as",
|
||||
"select": "Select a Collection",
|
||||
"select_location": "Select location",
|
||||
"select_team": "选择一个团队",
|
||||
"team_collections": "团队收藏"
|
||||
},
|
||||
"confirm": {
|
||||
"logout": "Are you sure you want to logout?",
|
||||
"remove_collection": "Are you sure you want to permanently delete this collection?",
|
||||
"remove_environment": "Are you sure you want to permanently delete this environment?",
|
||||
"remove_folder": "Are you sure you want to permanently delete this folder?",
|
||||
"remove_history": "Are you sure you want to permanently delete all history?",
|
||||
"remove_request": "Are you sure you want to permanently delete this request?",
|
||||
"remove_team": "Are you sure you want to delete this team?",
|
||||
"remove_telemetry": "Are you sure you want to opt-out of Telemetry?",
|
||||
"sync": "您确定要同步此工作区吗?"
|
||||
},
|
||||
"count": {
|
||||
"header": "Header {count}",
|
||||
"message": "Message {count}",
|
||||
"parameter": "Parameter {count}",
|
||||
"protocol": "Protocol {count}",
|
||||
"value": "Value {count}",
|
||||
"variable": "Variable {count}"
|
||||
},
|
||||
"documentation": {
|
||||
"generate": "生成文档",
|
||||
"generate_message": "导入任何 Hoppscotch 集合以随时随地生成 API 文档。"
|
||||
},
|
||||
"empty": {
|
||||
"authorization": "This request does not use any authorization",
|
||||
"body": "This request does not have a body",
|
||||
"collection": "Collection is empty",
|
||||
"collections": "Collections are empty",
|
||||
"environments": "Environments are empty",
|
||||
"folder": "Folder is empty",
|
||||
"headers": "This request does not have any headers",
|
||||
"history": "History is empty",
|
||||
"members": "Team is empty",
|
||||
"parameters": "This request does not have any parameters",
|
||||
"protocols": "Protocols are empty",
|
||||
"schema": "Connect to a GraphQL endpoint",
|
||||
"team_name": "Team name empty",
|
||||
"teams": "Teams are empty",
|
||||
"tests": "There are no tests for this request"
|
||||
},
|
||||
"environment": {
|
||||
"create_new": "创造新环境",
|
||||
"edit": "编辑环境",
|
||||
"invalid_name": "请为环境提供一个有效的名称",
|
||||
"new": "新的环境",
|
||||
"no_environment": "无环境",
|
||||
"select": "选择环境",
|
||||
"title": "环境",
|
||||
"variable_list": "变量列表"
|
||||
},
|
||||
"error": {
|
||||
"browser_support_sse": "此浏览器似乎不支持服务器发送事件。",
|
||||
"check_console_details": "Check console log for details.",
|
||||
"curl_invalid_format": "cURL 格式不正确",
|
||||
"empty_req_name": "空请求名称",
|
||||
"f12_details": "(F12 详情)",
|
||||
"gql_prettify_invalid_query": "无法美化无效查询,解决查询语法错误并重试",
|
||||
"json_prettify_invalid_body": "无法美化无效正文,解决 json 语法错误并重试",
|
||||
"network_fail": "Could not send request",
|
||||
"no_duration": "无持续时间",
|
||||
"something_went_wrong": "Something went wrong"
|
||||
},
|
||||
"export": {
|
||||
"as_json": "Export as JSON",
|
||||
"create_secret_gist": "Create secret Gist",
|
||||
"gist_created": "Gist created",
|
||||
"require_github": "Login with GitHub to create secret gist"
|
||||
},
|
||||
"folder": {
|
||||
"created": "Folder created",
|
||||
"edit": "Edit Folder",
|
||||
"invalid_name": "请提供文件夹的名称",
|
||||
"new": "New Folder",
|
||||
"renamed": "Folder renamed"
|
||||
},
|
||||
"graphql": {
|
||||
"mutations": "突变",
|
||||
"schema": "架构",
|
||||
"subscriptions": "订阅"
|
||||
},
|
||||
"header": {
|
||||
"account": "帐户",
|
||||
"install_pwa": "Install app",
|
||||
"login": "Login",
|
||||
"save_workspace": "Save My Workspace"
|
||||
},
|
||||
"helpers": {
|
||||
"authorization": "The authorization header will be automatically generated when you send the request.",
|
||||
"generate_documentation_first": "先生成文档",
|
||||
"network_fail": "Unable to reach the API endpoint. Check your network connection and try again.",
|
||||
"offline": "You seem to be offline. Data in this workspace might not be up to date.",
|
||||
"offline_short": "You seem to be offline.",
|
||||
"post_request_tests": "Test scripts are written in JavaScript, and are run after the response is received.",
|
||||
"pre_request_script": "Pre-request scripts are written in JavaScript, and are run before the request is sent.",
|
||||
"tests": "Write a test script to automate debugging."
|
||||
},
|
||||
"hide": {
|
||||
"more": "Hide more",
|
||||
"preview": "Hide Preview",
|
||||
"sidebar": "Hide sidebar"
|
||||
},
|
||||
"import": {
|
||||
"collections": "Import collections",
|
||||
"curl": "Import cURL",
|
||||
"failed": "Import failed",
|
||||
"from_gist": "Import from Gist",
|
||||
"from_my_collections": "Import from My Collections",
|
||||
"gist_url": "Enter Gist URL",
|
||||
"json": "Import from JSON",
|
||||
"title": "Import"
|
||||
},
|
||||
"layout": {
|
||||
"zen_mode": "Zen mode"
|
||||
},
|
||||
"modal": {
|
||||
"collections": "收藏",
|
||||
"confirm": "Confirm",
|
||||
"edit_request": "编辑请求",
|
||||
"import_export": "Import / Export"
|
||||
},
|
||||
"mqtt": {
|
||||
"communication": "沟通",
|
||||
"log": "日志",
|
||||
"message": "信息",
|
||||
"publish": "Publish",
|
||||
"subscribe": "Subscribe",
|
||||
"topic": "Topic",
|
||||
"topic_name": "Topic Name",
|
||||
"topic_title": "Publish / Subscribe topic",
|
||||
"unsubscribe": "Unsubscribe",
|
||||
"url": "网址"
|
||||
},
|
||||
"navigation": {
|
||||
"doc": "Docs",
|
||||
"graphql": "GraphQL",
|
||||
"realtime": "Realtime",
|
||||
"rest": "REST",
|
||||
"settings": "Settings"
|
||||
},
|
||||
"preRequest": {
|
||||
"javascript_code": "JavaScript 代码",
|
||||
"learn": "阅读文档",
|
||||
"script": "Pre-Request Script",
|
||||
"snippets": "Snippets"
|
||||
},
|
||||
"remove": {
|
||||
"star": "Remove star"
|
||||
},
|
||||
"request": {
|
||||
"added": "添加了请求",
|
||||
"authorization": "授权",
|
||||
"body": "请求正文",
|
||||
"choose_language": "Choose language",
|
||||
"content_type": "内容类型",
|
||||
"copy_link": "Copy link",
|
||||
"duration": "期间",
|
||||
"enter_curl": "输入卷曲",
|
||||
"generate_code": "Generate code",
|
||||
"generated_code": "Generated code",
|
||||
"header_list": "标题列表",
|
||||
"invalid_name": "请提供请求的名称",
|
||||
"method": "方法",
|
||||
"name": "Request name",
|
||||
"parameter_list": "查询参数",
|
||||
"parameters": "参数",
|
||||
"payload": "有效载荷",
|
||||
"query": "询问",
|
||||
"raw_body": "原始请求正文",
|
||||
"renamed": "请求重命名",
|
||||
"run": "跑",
|
||||
"save": "Save",
|
||||
"save_as": "Save as",
|
||||
"saved": "请求已保存",
|
||||
"share": "Share",
|
||||
"title": "要求",
|
||||
"type": "请求类型",
|
||||
"url": "网址",
|
||||
"variables": "变量"
|
||||
},
|
||||
"response": {
|
||||
"body": "响应体",
|
||||
"headers": "标题",
|
||||
"html": "HTML",
|
||||
"image": "图像",
|
||||
"json": "JSON",
|
||||
"preview_html": "预览 HTML",
|
||||
"raw": "生的",
|
||||
"size": "尺寸",
|
||||
"status": "地位",
|
||||
"time": "时间",
|
||||
"title": "回复",
|
||||
"waiting_for_connection": "等待连接",
|
||||
"xml": "XML"
|
||||
},
|
||||
"settings": {
|
||||
"accent_color": "Accent color",
|
||||
"account": "帐户",
|
||||
"account_description": "自定义您的帐户设置。",
|
||||
"account_email_description": "您的主要电子邮件地址。",
|
||||
"account_name_description": "这是您的显示名称。",
|
||||
"background": "背景",
|
||||
"black_mode": "黑色的",
|
||||
"change_font_size": "Change font size",
|
||||
"choose_language": "Choose language",
|
||||
"dark_mode": "黑暗的",
|
||||
"experiments": "Experiments",
|
||||
"experiments_notice": "This is a collection of experiments we're working on that might turn out to be useful, fun, both, or neither. They're not final and may not be stable, so if something overly weird happens, don't panic. Just turn the dang thing off. Jokes aside, ",
|
||||
"extension_ver_not_reported": "未报告",
|
||||
"extension_version": "扩展版本",
|
||||
"extensions": "扩展",
|
||||
"extensions_use_toggle": "使用浏览器扩展发送请求(如果存在)",
|
||||
"font_size": "Font size",
|
||||
"font_size_large": "Large",
|
||||
"font_size_medium": "Medium",
|
||||
"font_size_small": "Small",
|
||||
"interceptor": "Interceptor",
|
||||
"interceptor_description": "应用程序和 API 之间的中间件。",
|
||||
"language": "Language",
|
||||
"light_mode": "光",
|
||||
"navigation_sidebar": "Navigation sidebar",
|
||||
"official_proxy_hosting": "官方代理由 Hoppscotch 托管。",
|
||||
"proxy": "代理人",
|
||||
"proxy_url": "代理网址",
|
||||
"proxy_use_toggle": "使用代理中间件发送请求",
|
||||
"read_the": "阅读",
|
||||
"reset_default": "重置为默认",
|
||||
"sync": "同步",
|
||||
"sync_collections": "收藏",
|
||||
"sync_description": "这些设置会同步到云。",
|
||||
"sync_environments": "环境",
|
||||
"sync_history": "历史",
|
||||
"system_mode": "系统",
|
||||
"telemetry": "Telemetry",
|
||||
"telemetry_helps_us": "Telemetry helps us to personalize our operations and deliver the best experience to you.",
|
||||
"theme": "主题",
|
||||
"theme_description": "自定义您的应用程序主题。",
|
||||
"use_experimental_url_bar": "Use experimental URL bar with environment highlighting",
|
||||
"user": "用户"
|
||||
},
|
||||
"shortcut": {
|
||||
"general": {
|
||||
"close_current_menu": "Close current menu",
|
||||
"command_menu": "Search & command menu",
|
||||
"help_menu": "Help menu",
|
||||
"show_all": "Keyboard shortcuts",
|
||||
"title": "一般"
|
||||
},
|
||||
"miscellaneous": {
|
||||
"invite": "Invite people to Hoppscotch",
|
||||
"title": "各种各样的"
|
||||
},
|
||||
"navigation": {
|
||||
"back": "Go back to previous page",
|
||||
"documentation": "Go to Documentation page",
|
||||
"forward": "Go forward to next page",
|
||||
"graphql": "Go to GraphQL page",
|
||||
"realtime": "Go to Realtime page",
|
||||
"rest": "Go to REST page",
|
||||
"settings": "Go to Settings page",
|
||||
"title": "导航"
|
||||
},
|
||||
"request": {
|
||||
"copy_request_link": "复制请求链接",
|
||||
"delete_method": "选择删除方法",
|
||||
"get_method": "选择GET方法",
|
||||
"head_method": "选择 HEAD 方法",
|
||||
"method": "方法",
|
||||
"next_method": "选择下一个方法",
|
||||
"path": "小路",
|
||||
"post_method": "选择POST方式",
|
||||
"previous_method": "选择上一个方法",
|
||||
"put_method": "选择 PUT 方法",
|
||||
"reset_request": "重置请求",
|
||||
"save_to_collections": "保存到收藏",
|
||||
"send_request": "发送请求",
|
||||
"title": "要求"
|
||||
}
|
||||
},
|
||||
"show": {
|
||||
"code": "Show code",
|
||||
"more": "Show more",
|
||||
"sidebar": "Show sidebar"
|
||||
},
|
||||
"socketio": {
|
||||
"communication": "沟通",
|
||||
"event_name": "事件名称",
|
||||
"events": "活动",
|
||||
"log": "日志",
|
||||
"url": "网址"
|
||||
},
|
||||
"sse": {
|
||||
"event_type": "事件类型",
|
||||
"log": "日志",
|
||||
"url": "网址"
|
||||
},
|
||||
"state": {
|
||||
"cleared": "清除",
|
||||
"connected": "连接的",
|
||||
"connected_to": "已连接到{name}",
|
||||
"connecting_to": "正在连接到{name}...",
|
||||
"copied_to_clipboard": "复制到剪贴板",
|
||||
"deleted": "已删除",
|
||||
"deprecated": "已弃用",
|
||||
"disabled": "已禁用",
|
||||
"disconnected": "断开连接",
|
||||
"disconnected_from": "与{name}断开连接",
|
||||
"docs_generated": "生成的文档",
|
||||
"download_started": "下载开始",
|
||||
"enabled": "启用",
|
||||
"file_imported": "文件导入",
|
||||
"finished_in": "在 {duration} 毫秒内完成",
|
||||
"history_deleted": "历史记录已删除",
|
||||
"loading": "正在加载...",
|
||||
"none": "没有任何",
|
||||
"nothing_found": "没有找到",
|
||||
"waiting_send_request": "等待发送请求"
|
||||
},
|
||||
"support": {
|
||||
"changelog": "Read more about latest releases",
|
||||
"chat": "Questions? Chat with us!",
|
||||
"community": "Ask questions and help others",
|
||||
"documentation": "Read more about Hoppscotch",
|
||||
"forum": "Ask questions and get answers",
|
||||
"shortcuts": "Browse app faster",
|
||||
"team": "Get in touch with the team",
|
||||
"title": "Support",
|
||||
"twitter": "Follow us on Twitter"
|
||||
},
|
||||
"tab": {
|
||||
"authorization": "授权",
|
||||
"body": "身体",
|
||||
"collections": "收藏",
|
||||
"headers": "标题",
|
||||
"history": "历史",
|
||||
"mqtt": "MQTT",
|
||||
"parameters": "参数",
|
||||
"pre_request_script": "预请求脚本",
|
||||
"queries": "查询",
|
||||
"query": "询问",
|
||||
"socketio": "套接字接口",
|
||||
"sse": "上证所",
|
||||
"tests": "测试",
|
||||
"types": "类型",
|
||||
"variables": "变量",
|
||||
"websocket": "WebSocket"
|
||||
},
|
||||
"team": {
|
||||
"create_new": "Create new team",
|
||||
"deleted": "Team deleted",
|
||||
"edit": "Edit Team",
|
||||
"email": "E-mail",
|
||||
"exit": "Exit Team",
|
||||
"exit_disabled": "Only owner cannot exit the team",
|
||||
"invalid_email_format": "Email format is invalid",
|
||||
"invalid_member_permission": "Please provide a valid permission to the team member",
|
||||
"join_beta": "加入 Beta 计划以访问团队。",
|
||||
"left": "You left the team",
|
||||
"member_removed": "User removed",
|
||||
"member_role_updated": "User roles updated",
|
||||
"members": "Members",
|
||||
"name_length_insufficient": "Team name should be atleast 6 characters long",
|
||||
"new": "New Team",
|
||||
"new_created": "New team created",
|
||||
"new_name": "My New Team",
|
||||
"no_access": "You do not have edit access to these collections",
|
||||
"permissions": "Permissions",
|
||||
"saved": "Team saved",
|
||||
"title": "Teams"
|
||||
},
|
||||
"test": {
|
||||
"javascript_code": "JavaScript 代码",
|
||||
"learn": "阅读文档",
|
||||
"report": "Test Report",
|
||||
"results": "Test Results",
|
||||
"script": "Script",
|
||||
"snippets": "Snippets"
|
||||
},
|
||||
"websocket": {
|
||||
"communication": "沟通",
|
||||
"log": "日志",
|
||||
"message": "信息",
|
||||
"protocols": "Protocols",
|
||||
"url": "网址"
|
||||
}
|
||||
}
|
||||
494
locales/tw.json
@@ -1,494 +0,0 @@
|
||||
{
|
||||
"action": {
|
||||
"cancel": "取消",
|
||||
"choose_file": "選擇一個文件",
|
||||
"clear": "清除",
|
||||
"clear_all": "清除所有",
|
||||
"connect": "連接",
|
||||
"copy": "Copy",
|
||||
"delete": "刪除",
|
||||
"disconnect": "斷開",
|
||||
"dismiss": "Dismiss",
|
||||
"download_file": "下載文件",
|
||||
"edit": "編輯",
|
||||
"go_back": "Go back",
|
||||
"label": "標籤",
|
||||
"learn_more": "Learn more",
|
||||
"more": "更多的",
|
||||
"new": "新的",
|
||||
"no": "不",
|
||||
"preserve_current": "保持電流",
|
||||
"prettify": "美化",
|
||||
"remove": "消除",
|
||||
"replace_current": "替換當前",
|
||||
"replace_json": "替換為 JSON",
|
||||
"restore": "恢復",
|
||||
"save": "節省",
|
||||
"search": "搜索",
|
||||
"send": "發送",
|
||||
"start": "開始",
|
||||
"stop": "停止",
|
||||
"turn_off": "Turn off",
|
||||
"turn_on": "Turn on",
|
||||
"undo": "撤消",
|
||||
"yes": "是的"
|
||||
},
|
||||
"add": {
|
||||
"new": "Add new",
|
||||
"star": "Add star"
|
||||
},
|
||||
"app": {
|
||||
"chat_with_us": "Chat with us",
|
||||
"contact_us": "聯繫我們",
|
||||
"copy": "Copy",
|
||||
"documentation": "Documentation",
|
||||
"help": "Help, feedback and</br>documentation",
|
||||
"home": "家",
|
||||
"invite": "Invite",
|
||||
"invite_description": "In Hoppscotch, we designed a simple and intuitive interface for creating and managing your APIs. Hoppscotch is a tool that helps you build, test, document and share your APIs.",
|
||||
"invite_your_friends": "Invite your friends",
|
||||
"join_discord_community": "Join our Discord community",
|
||||
"keyboard_shortcuts": "Keyboard shortcuts",
|
||||
"name": "Hoppscotch",
|
||||
"new_version_found": "New version found. Refresh to update.",
|
||||
"proxy_privacy_policy": "代理隱私政策",
|
||||
"reload": "重新加載",
|
||||
"search": "Search",
|
||||
"share": "Share",
|
||||
"shortcuts": "快捷方式",
|
||||
"spotlight": "聚光燈",
|
||||
"status": "Status",
|
||||
"terms_and_privacy": "Terms and privacy",
|
||||
"twitter": "Twitter",
|
||||
"type_a_command_search": "Type a command or search…",
|
||||
"version": "v2.0",
|
||||
"we_use_cookies": "我們使用cookies",
|
||||
"whats_new": "What's new?",
|
||||
"wiki": "維基"
|
||||
},
|
||||
"auth": {
|
||||
"account_exists": "Account exists with different credential - Login to link both accounts",
|
||||
"all_sign_in_options": "所有登錄選項",
|
||||
"continue_with_email": "繼續使用電子郵件",
|
||||
"continue_with_github": "繼續使用 GitHub",
|
||||
"continue_with_google": "繼續使用 Google",
|
||||
"email": "Email",
|
||||
"logged_out": "登出",
|
||||
"login": "登錄",
|
||||
"login_success": "登錄成功",
|
||||
"login_to_hoppscotch": "登錄 Hoppscotch",
|
||||
"logout": "登出",
|
||||
"re_enter_email": "重新輸入電子郵件",
|
||||
"send_magic_link": "Send a magic link",
|
||||
"sync": "同步",
|
||||
"we_sent_magic_link": "We sent you a magic link!",
|
||||
"we_sent_magic_link_description": "Check your inbox - we sent an email to {email}. It contains a magic link that will log you in."
|
||||
},
|
||||
"authorization": {
|
||||
"generate_token": "生成令牌",
|
||||
"include_in_url": "包含在網址中",
|
||||
"learn": "學習怎樣",
|
||||
"password": "密碼",
|
||||
"token": "令牌",
|
||||
"type": "授權類型",
|
||||
"username": "用戶名"
|
||||
},
|
||||
"collection": {
|
||||
"created": "Collection created",
|
||||
"edit": "Edit Collection",
|
||||
"invalid_name": "Please provide a valid name for the collection",
|
||||
"my_collections": "我的收藏",
|
||||
"name": "My New Collection",
|
||||
"new": "New Collection",
|
||||
"renamed": "收藏更名",
|
||||
"save_as": "Save as",
|
||||
"select": "Select a Collection",
|
||||
"select_location": "Select location",
|
||||
"select_team": "選擇一個團隊",
|
||||
"team_collections": "團隊收藏"
|
||||
},
|
||||
"confirm": {
|
||||
"logout": "Are you sure you want to logout?",
|
||||
"remove_collection": "Are you sure you want to permanently delete this collection?",
|
||||
"remove_environment": "Are you sure you want to permanently delete this environment?",
|
||||
"remove_folder": "Are you sure you want to permanently delete this folder?",
|
||||
"remove_history": "Are you sure you want to permanently delete all history?",
|
||||
"remove_request": "Are you sure you want to permanently delete this request?",
|
||||
"remove_team": "Are you sure you want to delete this team?",
|
||||
"remove_telemetry": "Are you sure you want to opt-out of Telemetry?",
|
||||
"sync": "您確定要同步此工作區嗎?"
|
||||
},
|
||||
"count": {
|
||||
"header": "Header {count}",
|
||||
"message": "Message {count}",
|
||||
"parameter": "Parameter {count}",
|
||||
"protocol": "Protocol {count}",
|
||||
"value": "Value {count}",
|
||||
"variable": "Variable {count}"
|
||||
},
|
||||
"documentation": {
|
||||
"generate": "生成文檔",
|
||||
"generate_message": "導入任何 Hoppscotch 集合以隨時隨地生成 API 文檔。"
|
||||
},
|
||||
"empty": {
|
||||
"authorization": "This request does not use any authorization",
|
||||
"body": "This request does not have a body",
|
||||
"collection": "Collection is empty",
|
||||
"collections": "Collections are empty",
|
||||
"environments": "Environments are empty",
|
||||
"folder": "Folder is empty",
|
||||
"headers": "This request does not have any headers",
|
||||
"history": "History is empty",
|
||||
"members": "Team is empty",
|
||||
"parameters": "This request does not have any parameters",
|
||||
"protocols": "Protocols are empty",
|
||||
"schema": "Connect to a GraphQL endpoint",
|
||||
"team_name": "Team name empty",
|
||||
"teams": "Teams are empty",
|
||||
"tests": "There are no tests for this request"
|
||||
},
|
||||
"environment": {
|
||||
"create_new": "創造新環境",
|
||||
"edit": "編輯環境",
|
||||
"invalid_name": "請為環境提供一個有效的名稱",
|
||||
"new": "新的環境",
|
||||
"no_environment": "無環境",
|
||||
"select": "選擇環境",
|
||||
"title": "環境",
|
||||
"variable_list": "變量列表"
|
||||
},
|
||||
"error": {
|
||||
"browser_support_sse": "此瀏覽器似乎不支持服務器發送事件。",
|
||||
"check_console_details": "Check console log for details.",
|
||||
"curl_invalid_format": "cURL 格式不正確",
|
||||
"empty_req_name": "空請求名稱",
|
||||
"f12_details": "(F12 詳情)",
|
||||
"gql_prettify_invalid_query": "無法美化無效查詢,解決查詢語法錯誤並重試",
|
||||
"json_prettify_invalid_body": "無法美化無效正文,解決 json 語法錯誤並重試",
|
||||
"network_fail": "Could not send request",
|
||||
"no_duration": "無持續時間",
|
||||
"something_went_wrong": "Something went wrong"
|
||||
},
|
||||
"export": {
|
||||
"as_json": "Export as JSON",
|
||||
"create_secret_gist": "Create secret Gist",
|
||||
"gist_created": "Gist created",
|
||||
"require_github": "Login with GitHub to create secret gist"
|
||||
},
|
||||
"folder": {
|
||||
"created": "Folder created",
|
||||
"edit": "Edit Folder",
|
||||
"invalid_name": "請提供文件夾的名稱",
|
||||
"new": "New Folder",
|
||||
"renamed": "Folder renamed"
|
||||
},
|
||||
"graphql": {
|
||||
"mutations": "突變",
|
||||
"schema": "架構",
|
||||
"subscriptions": "訂閱"
|
||||
},
|
||||
"header": {
|
||||
"account": "帳戶",
|
||||
"install_pwa": "Install app",
|
||||
"login": "Login",
|
||||
"save_workspace": "Save My Workspace"
|
||||
},
|
||||
"helpers": {
|
||||
"authorization": "The authorization header will be automatically generated when you send the request.",
|
||||
"generate_documentation_first": "先生成文檔",
|
||||
"network_fail": "Unable to reach the API endpoint. Check your network connection and try again.",
|
||||
"offline": "You seem to be offline. Data in this workspace might not be up to date.",
|
||||
"offline_short": "You seem to be offline.",
|
||||
"post_request_tests": "Test scripts are written in JavaScript, and are run after the response is received.",
|
||||
"pre_request_script": "Pre-request scripts are written in JavaScript, and are run before the request is sent.",
|
||||
"tests": "Write a test script to automate debugging."
|
||||
},
|
||||
"hide": {
|
||||
"more": "Hide more",
|
||||
"preview": "Hide Preview",
|
||||
"sidebar": "Hide sidebar"
|
||||
},
|
||||
"import": {
|
||||
"collections": "Import collections",
|
||||
"curl": "Import cURL",
|
||||
"failed": "Import failed",
|
||||
"from_gist": "Import from Gist",
|
||||
"from_my_collections": "Import from My Collections",
|
||||
"gist_url": "Enter Gist URL",
|
||||
"json": "Import from JSON",
|
||||
"title": "Import"
|
||||
},
|
||||
"layout": {
|
||||
"zen_mode": "Zen mode"
|
||||
},
|
||||
"modal": {
|
||||
"collections": "收藏",
|
||||
"confirm": "Confirm",
|
||||
"edit_request": "編輯請求",
|
||||
"import_export": "Import / Export"
|
||||
},
|
||||
"mqtt": {
|
||||
"communication": "溝通",
|
||||
"log": "日誌",
|
||||
"message": "信息",
|
||||
"publish": "Publish",
|
||||
"subscribe": "Subscribe",
|
||||
"topic": "Topic",
|
||||
"topic_name": "Topic Name",
|
||||
"topic_title": "Publish / Subscribe topic",
|
||||
"unsubscribe": "Unsubscribe",
|
||||
"url": "網址"
|
||||
},
|
||||
"navigation": {
|
||||
"doc": "Docs",
|
||||
"graphql": "GraphQL",
|
||||
"realtime": "Realtime",
|
||||
"rest": "REST",
|
||||
"settings": "Settings"
|
||||
},
|
||||
"preRequest": {
|
||||
"javascript_code": "JavaScript 代碼",
|
||||
"learn": "閱讀文檔",
|
||||
"script": "Pre-Request Script",
|
||||
"snippets": "Snippets"
|
||||
},
|
||||
"remove": {
|
||||
"star": "Remove star"
|
||||
},
|
||||
"request": {
|
||||
"added": "添加了請求",
|
||||
"authorization": "授權",
|
||||
"body": "請求正文",
|
||||
"choose_language": "Choose language",
|
||||
"content_type": "內容類型",
|
||||
"copy_link": "Copy link",
|
||||
"duration": "期間",
|
||||
"enter_curl": "輸入捲曲",
|
||||
"generate_code": "Generate code",
|
||||
"generated_code": "Generated code",
|
||||
"header_list": "標題列表",
|
||||
"invalid_name": "請提供請求的名稱",
|
||||
"method": "方法",
|
||||
"name": "Request name",
|
||||
"parameter_list": "查詢參數",
|
||||
"parameters": "參數",
|
||||
"payload": "有效載荷",
|
||||
"query": "詢問",
|
||||
"raw_body": "原始請求正文",
|
||||
"renamed": "請求重命名",
|
||||
"run": "跑步",
|
||||
"save": "Save",
|
||||
"save_as": "Save as",
|
||||
"saved": "請求已保存",
|
||||
"share": "Share",
|
||||
"title": "要求",
|
||||
"type": "請求類型",
|
||||
"url": "網址",
|
||||
"variables": "變量"
|
||||
},
|
||||
"response": {
|
||||
"body": "響應體",
|
||||
"headers": "標題",
|
||||
"html": "HTML",
|
||||
"image": "圖片",
|
||||
"json": "JSON",
|
||||
"preview_html": "預覽 HTML",
|
||||
"raw": "生的",
|
||||
"size": "尺寸",
|
||||
"status": "地位",
|
||||
"time": "時間",
|
||||
"title": "回复",
|
||||
"waiting_for_connection": "等待連接",
|
||||
"xml": "XML"
|
||||
},
|
||||
"settings": {
|
||||
"accent_color": "Accent color",
|
||||
"account": "帳戶",
|
||||
"account_description": "自定義您的帳戶設置。",
|
||||
"account_email_description": "您的主要電子郵件地址。",
|
||||
"account_name_description": "這是您的顯示名稱。",
|
||||
"background": "背景",
|
||||
"black_mode": "黑色的",
|
||||
"change_font_size": "Change font size",
|
||||
"choose_language": "Choose language",
|
||||
"dark_mode": "黑暗的",
|
||||
"experiments": "Experiments",
|
||||
"experiments_notice": "This is a collection of experiments we're working on that might turn out to be useful, fun, both, or neither. They're not final and may not be stable, so if something overly weird happens, don't panic. Just turn the dang thing off. Jokes aside, ",
|
||||
"extension_ver_not_reported": "未報告",
|
||||
"extension_version": "擴展版本",
|
||||
"extensions": "擴展",
|
||||
"extensions_use_toggle": "使用瀏覽器擴展發送請求(如果存在)",
|
||||
"font_size": "Font size",
|
||||
"font_size_large": "Large",
|
||||
"font_size_medium": "Medium",
|
||||
"font_size_small": "Small",
|
||||
"interceptor": "Interceptor",
|
||||
"interceptor_description": "應用程序和 API 之間的中間件。",
|
||||
"language": "Language",
|
||||
"light_mode": "光",
|
||||
"navigation_sidebar": "Navigation sidebar",
|
||||
"official_proxy_hosting": "官方代理由 Hoppscotch 託管。",
|
||||
"proxy": "代理人",
|
||||
"proxy_url": "代理網址",
|
||||
"proxy_use_toggle": "使用代理中間件發送請求",
|
||||
"read_the": "閱讀",
|
||||
"reset_default": "重置為默認",
|
||||
"sync": "同步",
|
||||
"sync_collections": "收藏",
|
||||
"sync_description": "這些設置會同步到雲。",
|
||||
"sync_environments": "環境",
|
||||
"sync_history": "歷史",
|
||||
"system_mode": "系統",
|
||||
"telemetry": "Telemetry",
|
||||
"telemetry_helps_us": "Telemetry helps us to personalize our operations and deliver the best experience to you.",
|
||||
"theme": "主題",
|
||||
"theme_description": "自定義您的應用程序主題。",
|
||||
"use_experimental_url_bar": "Use experimental URL bar with environment highlighting",
|
||||
"user": "用戶"
|
||||
},
|
||||
"shortcut": {
|
||||
"general": {
|
||||
"close_current_menu": "Close current menu",
|
||||
"command_menu": "Search & command menu",
|
||||
"help_menu": "Help menu",
|
||||
"show_all": "Keyboard shortcuts",
|
||||
"title": "一般"
|
||||
},
|
||||
"miscellaneous": {
|
||||
"invite": "Invite people to Hoppscotch",
|
||||
"title": "各種各樣的"
|
||||
},
|
||||
"navigation": {
|
||||
"back": "Go back to previous page",
|
||||
"documentation": "Go to Documentation page",
|
||||
"forward": "Go forward to next page",
|
||||
"graphql": "Go to GraphQL page",
|
||||
"realtime": "Go to Realtime page",
|
||||
"rest": "Go to REST page",
|
||||
"settings": "Go to Settings page",
|
||||
"title": "導航"
|
||||
},
|
||||
"request": {
|
||||
"copy_request_link": "複製請求鏈接",
|
||||
"delete_method": "選擇刪除方法",
|
||||
"get_method": "選擇GET方法",
|
||||
"head_method": "選擇 HEAD 方法",
|
||||
"method": "方法",
|
||||
"next_method": "選擇下一個方法",
|
||||
"path": "小路",
|
||||
"post_method": "選擇POST方式",
|
||||
"previous_method": "選擇上一個方法",
|
||||
"put_method": "選擇 PUT 方法",
|
||||
"reset_request": "重置請求",
|
||||
"save_to_collections": "保存到收藏",
|
||||
"send_request": "發送請求",
|
||||
"title": "要求"
|
||||
}
|
||||
},
|
||||
"show": {
|
||||
"code": "Show code",
|
||||
"more": "Show more",
|
||||
"sidebar": "Show sidebar"
|
||||
},
|
||||
"socketio": {
|
||||
"communication": "溝通",
|
||||
"event_name": "事件名稱",
|
||||
"events": "活動",
|
||||
"log": "日誌",
|
||||
"url": "網址"
|
||||
},
|
||||
"sse": {
|
||||
"event_type": "事件類型",
|
||||
"log": "日誌",
|
||||
"url": "網址"
|
||||
},
|
||||
"state": {
|
||||
"cleared": "清除",
|
||||
"connected": "連接的",
|
||||
"connected_to": "已連接到{name}",
|
||||
"connecting_to": "正在連接到{name}...",
|
||||
"copied_to_clipboard": "複製到剪貼板",
|
||||
"deleted": "已刪除",
|
||||
"deprecated": "已棄用",
|
||||
"disabled": "已禁用",
|
||||
"disconnected": "斷開連接",
|
||||
"disconnected_from": "與{name}斷開連接",
|
||||
"docs_generated": "生成的文檔",
|
||||
"download_started": "下載開始",
|
||||
"enabled": "啟用",
|
||||
"file_imported": "文件導入",
|
||||
"finished_in": "在 {duration} 毫秒內完成",
|
||||
"history_deleted": "歷史記錄已刪除",
|
||||
"loading": "正在加載...",
|
||||
"none": "沒有任何",
|
||||
"nothing_found": "沒有找到",
|
||||
"waiting_send_request": "等待發送請求"
|
||||
},
|
||||
"support": {
|
||||
"changelog": "Read more about latest releases",
|
||||
"chat": "Questions? Chat with us!",
|
||||
"community": "Ask questions and help others",
|
||||
"documentation": "Read more about Hoppscotch",
|
||||
"forum": "Ask questions and get answers",
|
||||
"shortcuts": "Browse app faster",
|
||||
"team": "Get in touch with the team",
|
||||
"title": "Support",
|
||||
"twitter": "Follow us on Twitter"
|
||||
},
|
||||
"tab": {
|
||||
"authorization": "授權",
|
||||
"body": "身體",
|
||||
"collections": "收藏",
|
||||
"headers": "標題",
|
||||
"history": "歷史",
|
||||
"mqtt": "MQTT",
|
||||
"parameters": "參數",
|
||||
"pre_request_script": "預請求腳本",
|
||||
"queries": "查詢",
|
||||
"query": "詢問",
|
||||
"socketio": "套接字接口",
|
||||
"sse": "上證所",
|
||||
"tests": "測試",
|
||||
"types": "類型",
|
||||
"variables": "變量",
|
||||
"websocket": "WebSocket"
|
||||
},
|
||||
"team": {
|
||||
"create_new": "Create new team",
|
||||
"deleted": "Team deleted",
|
||||
"edit": "Edit Team",
|
||||
"email": "E-mail",
|
||||
"exit": "Exit Team",
|
||||
"exit_disabled": "Only owner cannot exit the team",
|
||||
"invalid_email_format": "Email format is invalid",
|
||||
"invalid_member_permission": "Please provide a valid permission to the team member",
|
||||
"join_beta": "加入 Beta 計劃以訪問團隊。",
|
||||
"left": "You left the team",
|
||||
"member_removed": "User removed",
|
||||
"member_role_updated": "User roles updated",
|
||||
"members": "Members",
|
||||
"name_length_insufficient": "Team name should be atleast 6 characters long",
|
||||
"new": "New Team",
|
||||
"new_created": "New team created",
|
||||
"new_name": "My New Team",
|
||||
"no_access": "You do not have edit access to these collections",
|
||||
"permissions": "Permissions",
|
||||
"saved": "Team saved",
|
||||
"title": "Teams"
|
||||
},
|
||||
"test": {
|
||||
"javascript_code": "JavaScript 代碼",
|
||||
"learn": "閱讀文檔",
|
||||
"report": "Test Report",
|
||||
"results": "Test Results",
|
||||
"script": "Script",
|
||||
"snippets": "Snippets"
|
||||
},
|
||||
"websocket": {
|
||||
"communication": "溝通",
|
||||
"log": "日誌",
|
||||
"message": "信息",
|
||||
"protocols": "Protocols",
|
||||
"url": "網址"
|
||||
}
|
||||
}
|
||||
29
netlify.toml
@@ -1,6 +1,17 @@
|
||||
[build.environment]
|
||||
NODE_VERSION = "14"
|
||||
NPM_FLAGS = "--prefix=/dev/null"
|
||||
|
||||
[build]
|
||||
functions = "netlify/"
|
||||
environment = { NODE_VERSION = "14" }
|
||||
base = "/"
|
||||
publish = "packages/hoppscotch-app/dist"
|
||||
command = "npx pnpm i --store=node_modules/.pnpm-store && npx pnpm run generate"
|
||||
|
||||
[[headers]]
|
||||
for = "/*"
|
||||
[headers.values]
|
||||
X-Frame-Options = "DENY"
|
||||
X-XSS-Protection = "1; mode=block"
|
||||
|
||||
[[redirects]]
|
||||
from = "/discord"
|
||||
@@ -22,7 +33,7 @@
|
||||
|
||||
[[redirects]]
|
||||
from = "/careers"
|
||||
to = "https://www.notion.so/hoppscotch/3b9d5d5239a043bfb91701faabf5b8f0"
|
||||
to = "https://hoppscotch.notion.site/3b9d5d5239a043bfb91701faabf5b8f0"
|
||||
status = 301
|
||||
force = true
|
||||
|
||||
@@ -31,3 +42,15 @@
|
||||
to = "http://eepurl.com/hy0eWH"
|
||||
status = 301
|
||||
force = true
|
||||
|
||||
[[redirects]]
|
||||
from = "/twitter"
|
||||
to = "https://twitter.com/hoppscotch_io"
|
||||
status = 301
|
||||
force = true
|
||||
|
||||
[[redirects]]
|
||||
from = "/github"
|
||||
to = "https://github.com/hoppscotch/hoppscotch"
|
||||
status = 301
|
||||
force = true
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
// Docs on event and context https://www.netlify.com/docs/functions/#the-handler-method
|
||||
exports.handler = (event) => {
|
||||
switch (event.httpMethod) {
|
||||
case "GET":
|
||||
try {
|
||||
const name = event.queryStringParameters.name || "World"
|
||||
return {
|
||||
statusCode: 200,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ message: `Hello ${name}` }),
|
||||
}
|
||||
} catch (e) {
|
||||
return { statusCode: 500, body: e.toString() }
|
||||
}
|
||||
|
||||
default:
|
||||
return { statusCode: 405, body: "Method Not Allowed" }
|
||||
}
|
||||
}
|
||||
62456
package-lock.json
generated
113
package.json
@@ -1,108 +1,29 @@
|
||||
{
|
||||
"name": "hoppscotch",
|
||||
"version": "2.0.0",
|
||||
"name": "hoppscotch-app",
|
||||
"version": "2.1.0",
|
||||
"description": "Open source API development ecosystem",
|
||||
"author": "Hoppscotch (support@hoppscotch.io)",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "nuxt",
|
||||
"build": "vue-tsc --noEmit && nuxt build",
|
||||
"start": "nuxt start",
|
||||
"generate": "nuxt generate --modern",
|
||||
"analyze": "npx nuxt build -a",
|
||||
"lint:script": "eslint --ext .ts,.js,.vue --ignore-path .gitignore .",
|
||||
"lint:style": "stylelint **/*.{css,scss,vue} --ignore-path .gitignore",
|
||||
"lint": "npm run lint:script && npm run lint:style",
|
||||
"lintfix": "eslint --ext .ts,.js,.vue --ignore-path .gitignore . --fix",
|
||||
"test": "jest",
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
"prepare": "husky install",
|
||||
"pre-commit": "lint-staged"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{ts,js,vue}": "eslint",
|
||||
"*.{css,scss,vue}": "stylelint"
|
||||
"dev": "pnpm -r do-dev",
|
||||
"generate": "pnpm -r do-build-prod",
|
||||
"start": "pnpm -r do-prod-start",
|
||||
"lint": "pnpm -r do-lint",
|
||||
"lintfix": "pnpm -r do-lintfix",
|
||||
"pre-commit": "pnpm -r do-lint",
|
||||
"test": "pnpm -r do-test"
|
||||
},
|
||||
"workspaces": [
|
||||
"./packages/*"
|
||||
],
|
||||
"dependencies": {
|
||||
"@apollo/client": "^3.4.8",
|
||||
"@nuxtjs/axios": "^5.13.6",
|
||||
"@nuxtjs/composition-api": "^0.27.0",
|
||||
"@nuxtjs/gtm": "^2.4.0",
|
||||
"@nuxtjs/i18n": "^7.0.2",
|
||||
"@nuxtjs/robots": "^2.5.0",
|
||||
"@nuxtjs/sitemap": "^2.4.0",
|
||||
"@nuxtjs/toast": "^3.3.1",
|
||||
"ace-builds": "^1.4.12",
|
||||
"acorn": "^8.4.1",
|
||||
"acorn-walk": "^8.1.1",
|
||||
"core-js": "^3.16.2",
|
||||
"esprima": "^4.0.1",
|
||||
"firebase": "^8.10.0",
|
||||
"graphql": "^15.5.0",
|
||||
"graphql-language-service-interface": "^2.8.4",
|
||||
"json-loader": "^0.5.7",
|
||||
"lodash": "^4.17.21",
|
||||
"mustache": "^4.2.0",
|
||||
"node-interval-tree": "^1.3.3",
|
||||
"nuxt": "^2.15.8",
|
||||
"paho-mqtt": "^1.1.0",
|
||||
"rxjs": "^7.3.0",
|
||||
"socket.io-client": "^4.1.3",
|
||||
"socketio-wildcard": "^2.0.0",
|
||||
"splitpanes": "^2.3.8",
|
||||
"tern": "^0.24.3",
|
||||
"vue-apollo": "^3.0.7",
|
||||
"vue-cli-plugin-apollo": "^0.22.2",
|
||||
"vue-functional-data-merge": "^3.1.0",
|
||||
"vue-github-button": "^1.3.0",
|
||||
"vue-tippy": "^4.10.2",
|
||||
"vuejs-auto-complete": "^0.9.0",
|
||||
"yargs-parser": "^20.2.9"
|
||||
"husky": "^7.0.4",
|
||||
"lint-staged": "^12.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.15.0",
|
||||
"@babel/preset-env": "^7.15.0",
|
||||
"@commitlint/cli": "^13.1.0",
|
||||
"@commitlint/config-conventional": "^13.1.0",
|
||||
"@nuxt/types": "^2.15.8",
|
||||
"@nuxt/typescript-build": "^2.1.0",
|
||||
"@nuxtjs/color-mode": "^2.1.1",
|
||||
"@nuxtjs/dotenv": "^1.4.1",
|
||||
"@nuxtjs/eslint-config-typescript": "^6.0.1",
|
||||
"@nuxtjs/google-analytics": "^2.4.0",
|
||||
"@nuxtjs/google-fonts": "^1.3.0",
|
||||
"@nuxtjs/pwa": "^3.3.5",
|
||||
"@nuxtjs/stylelint-module": "^4.0.0",
|
||||
"@nuxtjs/svg": "^0.1.12",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@types/cookie": "^0.4.1",
|
||||
"@types/lodash": "^4.14.172",
|
||||
"@types/splitpanes": "^2.2.1",
|
||||
"@vue/runtime-dom": "^3.2.6",
|
||||
"@vue/test-utils": "^1.2.2",
|
||||
"babel-core": "^7.0.0-bridge.0",
|
||||
"babel-jest": "^27.0.6",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-prettier": "^8.1.0",
|
||||
"eslint-plugin-nuxt": ">=2.0.0",
|
||||
"eslint-plugin-prettier": "^3.4.1",
|
||||
"eslint-plugin-vue": "^7.16.0",
|
||||
"husky": "^7.0.1",
|
||||
"jest": "^27.0.6",
|
||||
"jest-serializer-vue": "^2.0.2",
|
||||
"lint-staged": "^11.1.2",
|
||||
"nuxt-windicss": "^1.2.3",
|
||||
"prettier": "^2.3.2",
|
||||
"pretty-quick": "^3.1.1",
|
||||
"raw-loader": "^4.0.2",
|
||||
"sass": "^1.38.0",
|
||||
"sass-loader": "^10.2.0",
|
||||
"stylelint": "^13.12.0",
|
||||
"stylelint-config-prettier": "^8.0.2",
|
||||
"stylelint-config-standard": "^22.0.0",
|
||||
"ts-jest": "^27.0.5",
|
||||
"typescript": "^4.2",
|
||||
"unplugin-vue2-script-setup": "^0.4.2",
|
||||
"vue-jest": "^3.0.7",
|
||||
"worker-loader": "^3.0.8"
|
||||
"@commitlint/cli": "^14.1.0",
|
||||
"@commitlint/config-conventional": "^14.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
4
packages/codemirror-lang-graphql/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/node_modules
|
||||
package-lock.json
|
||||
/dist
|
||||
/src/*.d.ts
|
||||
5
packages/codemirror-lang-graphql/.npmignore
Normal file
@@ -0,0 +1,5 @@
|
||||
/src
|
||||
/test
|
||||
/node_modules
|
||||
rollup.config.js
|
||||
tsconfig.json
|
||||
1
packages/codemirror-lang-graphql/README.md
Normal file
@@ -0,0 +1 @@
|
||||
A [CodeMirror 6](https://codemirror.net/6) language plugin for GraphQL
|
||||
31
packages/codemirror-lang-graphql/package.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "@hoppscotch/codemirror-lang-graphql",
|
||||
"version": "0.1.0",
|
||||
"description": "GraphQL language support for CodeMirror",
|
||||
"scripts": {
|
||||
"prepare": "rollup -c"
|
||||
},
|
||||
"type": "module",
|
||||
"main": "dist/index.cjs",
|
||||
"module": "dist/index.js",
|
||||
"exports": {
|
||||
"import": "./dist/index.js",
|
||||
"require": "./dist/index.cjs"
|
||||
},
|
||||
"types": "dist/index.d.ts",
|
||||
"sideEffects": false,
|
||||
"dependencies": {
|
||||
"@codemirror/highlight": "^0.19.0",
|
||||
"@codemirror/language": "^0.19.0",
|
||||
"@lezer/lr": "^0.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@lezer/generator": "^0.15.0",
|
||||
"mocha": "^9.0.1",
|
||||
"rollup": "^2.35.1",
|
||||
"rollup-plugin-dts": "^3.0.2",
|
||||
"rollup-plugin-ts": "^1.4.0",
|
||||
"typescript": "^4.3.4"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
12
packages/codemirror-lang-graphql/rollup.config.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import typescript from "rollup-plugin-ts"
|
||||
import {lezer} from "@lezer/generator/rollup"
|
||||
|
||||
export default {
|
||||
input: "src/index.js",
|
||||
external: id => id != "tslib" && !/^(\.?\/|\w:)/.test(id),
|
||||
output: [
|
||||
{file: "dist/index.cjs", format: "cjs"},
|
||||
{dir: "./dist", format: "es"}
|
||||
],
|
||||
plugins: [lezer(), typescript()]
|
||||
}
|
||||
43
packages/codemirror-lang-graphql/src/index.js
Normal file
@@ -0,0 +1,43 @@
|
||||
import {parser} from "./syntax.grammar"
|
||||
import {LRLanguage, LanguageSupport, indentNodeProp, foldNodeProp, foldInside, delimitedIndent} from "@codemirror/language"
|
||||
import {styleTags, tags as t} from "@codemirror/highlight"
|
||||
|
||||
export const GQLLanguage = LRLanguage.define({
|
||||
parser: parser.configure({
|
||||
props: [
|
||||
indentNodeProp.add({
|
||||
"SelectionSet FieldsDefinition ObjectValue SchemaDefinition RootTypeDef": delimitedIndent({ closing: "}", align: true }),
|
||||
}),
|
||||
foldNodeProp.add({
|
||||
Application: foldInside,
|
||||
"SelectionSet FieldsDefinition ObjectValue RootOperationTypeDefinition RootTypeDef": (node) => {
|
||||
return {
|
||||
from: node.from,
|
||||
to: node.to
|
||||
}
|
||||
|
||||
}
|
||||
}),
|
||||
styleTags({
|
||||
Name: t.definition(t.variableName),
|
||||
"OperationDefinition/Name": t.definition(t.function(t.variableName)),
|
||||
OperationType: t.keyword,
|
||||
BooleanValue: t.bool,
|
||||
StringValue: t.string,
|
||||
IntValue: t.number,
|
||||
FloatValue: t.number,
|
||||
NullValue: t.null,
|
||||
ObjectValue: t.brace,
|
||||
Comment: t.lineComment,
|
||||
})
|
||||
]
|
||||
}),
|
||||
languageData: {
|
||||
commentTokens: { line: "#" },
|
||||
closeBrackets: { brackets: ["(", "[", "{", '"', '"""'] }
|
||||
}
|
||||
})
|
||||
|
||||
export function GQL() {
|
||||
return new LanguageSupport(GQLLanguage)
|
||||
}
|
||||
372
packages/codemirror-lang-graphql/src/syntax.grammar
Normal file
@@ -0,0 +1,372 @@
|
||||
@top SourceFile {
|
||||
Document
|
||||
}
|
||||
|
||||
@precedence {
|
||||
fieldDef @right,
|
||||
typeDef @right
|
||||
}
|
||||
|
||||
Document {
|
||||
Definition+
|
||||
}
|
||||
|
||||
Definition {
|
||||
ExecutableDefinition |
|
||||
TypeSystemDefinition |
|
||||
TypeSystemExtension
|
||||
}
|
||||
|
||||
ExecutableDefinition {
|
||||
OperationDefinition |
|
||||
FragmentDefinition
|
||||
}
|
||||
|
||||
TypeSystemDefinition {
|
||||
SchemaDefinition |
|
||||
TypeDefinition |
|
||||
DirectiveDefinition
|
||||
}
|
||||
|
||||
TypeSystemExtension {
|
||||
SchemaExtension |
|
||||
TypeExtension
|
||||
}
|
||||
|
||||
SchemaDefinition {
|
||||
Description? @specialize<Name, "schema"> Directives? RootTypeDef
|
||||
}
|
||||
|
||||
RootTypeDef {
|
||||
"{" RootOperationTypeDefinition+ "}"
|
||||
}
|
||||
|
||||
SchemaExtension {
|
||||
@specialize<Name, "extend"> @specialize<Name, "schema"> Directives? RootTypeDef
|
||||
}
|
||||
|
||||
TypeExtension {
|
||||
ScalarTypeExtension |
|
||||
ObjectTypeExtension |
|
||||
InterfaceTypeExtension |
|
||||
UnionTypeExtension |
|
||||
EnumTypeExtension |
|
||||
InputObjectTypeExtension
|
||||
}
|
||||
|
||||
ScalarTypeExtension {
|
||||
@specialize<Name, "extend"> @specialize<Name, "scalar"> Name Directives
|
||||
}
|
||||
|
||||
ObjectTypeExtension /* precedence: right 0 */ {
|
||||
@specialize<Name, "extend"> @specialize<Name, "type"> Name ImplementsInterfaces? Directives? !typeDef FieldsDefinition |
|
||||
@specialize<Name, "extend"> @specialize<Name, "type"> Name ImplementsInterfaces? Directives?
|
||||
}
|
||||
|
||||
InterfaceTypeExtension /* precedence: right 0 */ {
|
||||
@specialize<Name, "extend"> @specialize<Name, "interface"> Name ImplementsInterfaces? Directives? FieldsDefinition |
|
||||
@specialize<Name, "extend"> @specialize<Name, "interface"> Name ImplementsInterfaces? Directives?
|
||||
}
|
||||
|
||||
UnionTypeExtension /* precedence: right 0 */ {
|
||||
@specialize<Name, "extend"> @specialize<Name, "union"> Name Directives? UnionMemberTypes |
|
||||
@specialize<Name, "extend"> @specialize<Name, "union"> Name Directives?
|
||||
}
|
||||
|
||||
EnumTypeExtension /* precedence: right 0 */ {
|
||||
@specialize<Name, "extend"> @specialize<Name, "enum"> Name Directives? !typeDef EnumValuesDefinition |
|
||||
@specialize<Name, "extend"> @specialize<Name, "enum"> Name Directives?
|
||||
}
|
||||
|
||||
InputObjectTypeExtension /* precedence: right 0 */ {
|
||||
@specialize<Name, "extend"> @specialize<Name, "input"> Name Directives? InputFieldsDefinition+ |
|
||||
@specialize<Name, "extend"> @specialize<Name, "input"> Name Directives?
|
||||
}
|
||||
|
||||
InputFieldsDefinition {
|
||||
!fieldDef "{" InputValueDefinition+ "}"
|
||||
}
|
||||
|
||||
EnumValuesDefinition {
|
||||
!fieldDef "{" EnumValueDefinition+ "}"
|
||||
}
|
||||
|
||||
EnumValueDefinition {
|
||||
Description? EnumValue Directives?
|
||||
}
|
||||
|
||||
ImplementsInterfaces {
|
||||
ImplementsInterfaces "&" NamedType |
|
||||
@specialize<Name, "implements"> "&"? NamedType
|
||||
}
|
||||
|
||||
FieldsDefinition {
|
||||
!fieldDef "{" FieldDefinition+ "}"
|
||||
}
|
||||
|
||||
FieldDefinition {
|
||||
Description? Name ArgumentsDefinition? ":" Type Directives?
|
||||
}
|
||||
|
||||
ArgumentsDefinition {
|
||||
"(" InputValueDefinition+ ")"
|
||||
}
|
||||
|
||||
InputValueDefinition {
|
||||
Description? Name ":" Type DefaultValue? Directives?
|
||||
}
|
||||
|
||||
DefaultValue {
|
||||
"=" Value
|
||||
}
|
||||
|
||||
UnionMemberTypes {
|
||||
UnionMemberTypes "|" NamedType |
|
||||
"=" "|"? NamedType
|
||||
}
|
||||
|
||||
RootOperationTypeDefinition {
|
||||
OperationType ":" NamedType
|
||||
}
|
||||
|
||||
OperationDefinition {
|
||||
SelectionSet |
|
||||
OperationType Name? VariableDefinitions? Directives? SelectionSet
|
||||
}
|
||||
|
||||
TypeDefinition {
|
||||
ScalarTypeDefinition |
|
||||
ObjectTypeDefinition |
|
||||
InterfaceTypeDefinition |
|
||||
UnionTypeDefinition |
|
||||
EnumTypeDefinition |
|
||||
InputObjectTypeDefinition
|
||||
}
|
||||
|
||||
ScalarTypeDefinition /* precedence: right 0 */ {
|
||||
Description? @specialize<Name, "scalar"> Name Directives?
|
||||
}
|
||||
|
||||
ObjectTypeDefinition /* precedence: right 0 */ {
|
||||
Description? @specialize<Name, "type"> Name ImplementsInterfaces? Directives? FieldsDefinition?
|
||||
}
|
||||
|
||||
InterfaceTypeDefinition /* precedence: right 0 */ {
|
||||
Description? @specialize<Name, "interface"> Name ImplementsInterfaces? Directives? FieldsDefinition?
|
||||
}
|
||||
|
||||
UnionTypeDefinition /* precedence: right 0 */ {
|
||||
Description? @specialize<Name, "union"> Name Directives? UnionMemberTypes?
|
||||
}
|
||||
|
||||
EnumTypeDefinition /* precedence: right 0 */ {
|
||||
Description? @specialize<Name, "enum"> Name Directives? !typeDef EnumValuesDefinition?
|
||||
}
|
||||
|
||||
InputObjectTypeDefinition /* precedence: right 0 */ {
|
||||
Description? @specialize<Name, "input"> Name Directives? !typeDef InputFieldsDefinition?
|
||||
}
|
||||
|
||||
VariableDefinitions {
|
||||
"(" VariableDefinition+ ")"
|
||||
}
|
||||
|
||||
VariableDefinition {
|
||||
Variable ":" Type DefaultValue? Directives? Comma?
|
||||
}
|
||||
|
||||
SelectionSet {
|
||||
"{" Selection* "}"
|
||||
}
|
||||
|
||||
Selection {
|
||||
Field |
|
||||
InlineFragment |
|
||||
FragmentSpread
|
||||
}
|
||||
|
||||
Field {
|
||||
Alias? Name Arguments? Directive? SelectionSet?
|
||||
}
|
||||
|
||||
Alias {
|
||||
Name ":"
|
||||
}
|
||||
|
||||
Arguments {
|
||||
"(" Argument+ ")"
|
||||
}
|
||||
|
||||
Argument {
|
||||
Name ":" Value
|
||||
}
|
||||
|
||||
Value {
|
||||
Variable |
|
||||
StringValue |
|
||||
IntValue |
|
||||
FloatValue |
|
||||
BooleanValue |
|
||||
NullValue |
|
||||
EnumValue |
|
||||
ListValue |
|
||||
ObjectValue
|
||||
}
|
||||
|
||||
Variable {
|
||||
"$" Name
|
||||
}
|
||||
|
||||
EnumValue {
|
||||
Name
|
||||
}
|
||||
|
||||
ListValue {
|
||||
"[" Value* "]"
|
||||
}
|
||||
|
||||
ObjectValue {
|
||||
"{" ObjectField* "}"
|
||||
}
|
||||
|
||||
ObjectField {
|
||||
Name ":" Value Comma?
|
||||
}
|
||||
|
||||
FragmentSpread {
|
||||
"..." FragmentName Directives?
|
||||
}
|
||||
|
||||
FragmentDefinition {
|
||||
@specialize<Name, "fragment"> FragmentName TypeCondition Directives? SelectionSet
|
||||
}
|
||||
|
||||
FragmentName {
|
||||
Name
|
||||
}
|
||||
|
||||
InlineFragment {
|
||||
"..." TypeCondition? Directives? SelectionSet
|
||||
}
|
||||
|
||||
TypeCondition {
|
||||
@specialize<Name, "on"> NamedType
|
||||
}
|
||||
|
||||
Directives {
|
||||
Directive+
|
||||
}
|
||||
|
||||
Directive {
|
||||
"@" Name Arguments?
|
||||
}
|
||||
|
||||
DirectiveDefinition /* precedence: right 1 */ {
|
||||
Description? @specialize<Name, "directive"> "@" Name ArgumentsDefinition? @specialize<Name, "repeatable"> ? @specialize<Name, "on"> DirectiveLocations
|
||||
}
|
||||
|
||||
DirectiveLocations {
|
||||
DirectiveLocations "|" DirectiveLocation |
|
||||
"|"? DirectiveLocation
|
||||
}
|
||||
|
||||
DirectiveLocation {
|
||||
ExecutableDirectiveLocation |
|
||||
TypeSystemDirectiveLocation
|
||||
}
|
||||
|
||||
Type {
|
||||
NamedType |
|
||||
ListType |
|
||||
NonNullType
|
||||
}
|
||||
|
||||
NamedType {
|
||||
Name
|
||||
}
|
||||
|
||||
ListType {
|
||||
"[" Type "]"
|
||||
}
|
||||
|
||||
NonNullType {
|
||||
NamedType "!" |
|
||||
ListType "!"
|
||||
}
|
||||
|
||||
Description {
|
||||
StringValue
|
||||
}
|
||||
|
||||
OperationType {
|
||||
@specialize<Name, "query">
|
||||
| @specialize<Name, "mutation">
|
||||
| @specialize<Name, "subscription">
|
||||
}
|
||||
|
||||
BooleanValue {
|
||||
@specialize<Name, "true">
|
||||
| @specialize<Name, "false">
|
||||
}
|
||||
|
||||
NullValue {
|
||||
@specialize<Name, "null">
|
||||
}
|
||||
|
||||
ExecutableDirectiveLocation {
|
||||
@specialize<Name, "QUERY">
|
||||
| @specialize<Name, "MUTATION">
|
||||
| @specialize<Name, "SUBSCRIPTION">
|
||||
| @specialize<Name, "FIELD">
|
||||
| @specialize<Name, "FRAGMENT_DEFINITION">
|
||||
| @specialize<Name, "FRAGMENT_SPREAD">
|
||||
| @specialize<Name, "INLINE_FRAGMENT">
|
||||
| @specialize<Name, "VARIABLE_DEFINITION">
|
||||
}
|
||||
|
||||
TypeSystemDirectiveLocation {
|
||||
@specialize<Name, "SCHEMA">
|
||||
| @specialize<Name, "SCALAR">
|
||||
| @specialize<Name, "OBJECT">
|
||||
| @specialize<Name, "FIELD_DEFINITION">
|
||||
| @specialize<Name, "ARGUMENT_DEFINITION">
|
||||
| @specialize<Name, "INTERFACE">
|
||||
| @specialize<Name, "UNION">
|
||||
| @specialize<Name, "ENUM">
|
||||
| @specialize<Name, "ENUM_VALUE">
|
||||
| @specialize<Name, "INPUT_OBJECT">
|
||||
| @specialize<Name, "INPUT_FIELD_DEFINITION">
|
||||
}
|
||||
|
||||
@skip { Whitespace | Comment }
|
||||
|
||||
@tokens {
|
||||
Whitespace {
|
||||
std.whitespace+
|
||||
}
|
||||
StringValue {
|
||||
"\"\"\"" (!["] | "\\n" | "\"" "\""? !["])* "\"\"\"" | "\"" !["\\\n]* "\""
|
||||
}
|
||||
IntValue {
|
||||
"-"? "0"
|
||||
| "-"? std.digit+
|
||||
}
|
||||
|
||||
FloatValue {
|
||||
IntValue ("." std.digit+ | ("e" | "E") IntValue+)
|
||||
}
|
||||
|
||||
@precedence { IntValue, FloatValue }
|
||||
|
||||
Name {
|
||||
$[_A-Za-z] $[_0-9A-Za-z]*
|
||||
}
|
||||
Comment {
|
||||
"#" ![\n]*
|
||||
}
|
||||
Comma {
|
||||
","
|
||||
}
|
||||
}
|
||||
|
||||
@detectDelim
|
||||
12
packages/codemirror-lang-graphql/tsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"strict": true,
|
||||
"target": "es6",
|
||||
"module": "es2020",
|
||||
"newLine": "lf",
|
||||
"declaration": true,
|
||||
"moduleResolution": "node",
|
||||
"allowJs": true,
|
||||
},
|
||||
"include": ["src/*"]
|
||||
}
|
||||
@@ -18,29 +18,18 @@ module.exports = {
|
||||
"plugin:prettier/recommended",
|
||||
"plugin:nuxt/recommended",
|
||||
],
|
||||
plugins: ["vue", "prettier"],
|
||||
ignorePatterns: ["helpers/backend/graphql.ts"],
|
||||
plugins: ["vue", "nuxt", "prettier"],
|
||||
// add your custom rules here
|
||||
rules: {
|
||||
semi: [2, "never"],
|
||||
"import/named": "off", // because, named import issue with typescript see: https://github.com/typescript-eslint/typescript-eslint/issues/154
|
||||
"no-console": process.env.NODE_ENV === "production" ? "error" : "off",
|
||||
"no-debugger": process.env.NODE_ENV === "production" ? "error" : "off",
|
||||
"vue/max-attributes-per-line": "off",
|
||||
"vue/component-name-in-template-casing": ["error", "PascalCase"],
|
||||
"vue/html-self-closing": [
|
||||
"error",
|
||||
{
|
||||
html: {
|
||||
normal: "never",
|
||||
void: "always",
|
||||
},
|
||||
},
|
||||
],
|
||||
"vue/singleline-html-element-content-newline": "off",
|
||||
"vue/multiline-html-element-content-newline": "off",
|
||||
"vue/require-default-prop": "warn",
|
||||
"vue/require-prop-types": "warn",
|
||||
"prettier/prettier": ["warn", { semi: false }],
|
||||
"vue/multi-word-component-names": "off",
|
||||
"import/no-named-as-default": "off",
|
||||
"import/no-named-as-default-member": "off",
|
||||
"import/default": "off",
|
||||
"no-undef": "off",
|
||||
// localStorage block
|
||||
"no-restricted-globals": [
|
||||
118
packages/hoppscotch-app/.gitignore
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
# Created by .ignore support plugin (hsz.mobi)
|
||||
|
||||
# Firebase
|
||||
.firebase
|
||||
|
||||
### Node template
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variables file
|
||||
.env
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
|
||||
# next.js build output
|
||||
.next
|
||||
|
||||
# nuxt.js build output
|
||||
.nuxt
|
||||
|
||||
# Nuxt generate
|
||||
dist
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# Serverless directories
|
||||
.serverless
|
||||
|
||||
# IDE / Editor
|
||||
.idea
|
||||
|
||||
# Service worker
|
||||
sw.*
|
||||
|
||||
# Mac OSX
|
||||
.DS_Store
|
||||
|
||||
# Vim swap files
|
||||
*.swp
|
||||
|
||||
# Build data
|
||||
.hoppscotch
|
||||
|
||||
# File explorer
|
||||
.directory
|
||||
|
||||
# Tests screenshots
|
||||
tests/*/screenshots
|
||||
|
||||
# Tests videos
|
||||
tests/*/videos
|
||||
|
||||
# Local Netlify folder
|
||||
.netlify
|
||||
|
||||
# Andrew's crazy Volar shim generator
|
||||
shims-volar.d.ts
|
||||
|
||||
# Hoppscotch Backend Schema Introspection JSON
|
||||
helpers/backend/backend-schema.json
|
||||
|
||||
# GraphQL Type Generation
|
||||
helpers/backend/graphql.ts
|
||||
@@ -1,5 +1,11 @@
|
||||
module.exports = {
|
||||
extends: ["stylelint-config-standard", "stylelint-config-prettier"],
|
||||
ignoreFiles: ["/**/*.vue"],
|
||||
extends: [
|
||||
"stylelint-config-standard",
|
||||
"stylelint-config-prettier",
|
||||
"stylelint-config-standard-scss",
|
||||
],
|
||||
defaultSeverity: "warning",
|
||||
// add your custom config here
|
||||
// https://stylelint.io/user-guide/configuration
|
||||
rules: {
|
||||
@@ -19,5 +25,7 @@ module.exports = {
|
||||
],
|
||||
"declaration-block-trailing-semicolon": null,
|
||||
"no-descending-specificity": null,
|
||||
"selector-class-pattern": null,
|
||||
"selector-id-pattern": null,
|
||||
},
|
||||
}
|
||||
3
packages/hoppscotch-app/assets/icons/archive.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M20 9v9a2 2 0 01-2 2H6a2 2 0 01-2-2V9m16-5H4a2 2 0 00-2 2v1a2 2 0 002 2h16a2 2 0 002-2V6a2 2 0 00-2-2zm-10 9h4"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 319 B |
4
packages/hoppscotch-app/assets/icons/arrow-left.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="19" y1="12" x2="5" y2="12"></line>
|
||||
<polyline points="12 19 5 12 12 5"></polyline>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 284 B |
4
packages/hoppscotch-app/assets/icons/arrow-right.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="5" y1="12" x2="19" y2="12"></line>
|
||||
<polyline points="12 5 19 12 12 19"></polyline>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 285 B |
6
packages/hoppscotch-app/assets/icons/auth/github.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="256px" height="250px" viewBox="0 0 256 250" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
|
||||
<g>
|
||||
<path d="M128.00106,0 C57.3172926,0 0,57.3066942 0,128.00106 C0,184.555281 36.6761997,232.535542 87.534937,249.460899 C93.9320223,250.645779 96.280588,246.684165 96.280588,243.303333 C96.280588,240.251045 96.1618878,230.167899 96.106777,219.472176 C60.4967585,227.215235 52.9826207,204.369712 52.9826207,204.369712 C47.1599584,189.574598 38.770408,185.640538 38.770408,185.640538 C27.1568785,177.696113 39.6458206,177.859325 39.6458206,177.859325 C52.4993419,178.762293 59.267365,191.04987 59.267365,191.04987 C70.6837675,210.618423 89.2115753,204.961093 96.5158685,201.690482 C97.6647155,193.417512 100.981959,187.77078 104.642583,184.574357 C76.211799,181.33766 46.324819,170.362144 46.324819,121.315702 C46.324819,107.340889 51.3250588,95.9223682 59.5132437,86.9583937 C58.1842268,83.7344152 53.8029229,70.715562 60.7532354,53.0843636 C60.7532354,53.0843636 71.5019501,49.6441813 95.9626412,66.2049595 C106.172967,63.368876 117.123047,61.9465949 128.00106,61.8978432 C138.879073,61.9465949 149.837632,63.368876 160.067033,66.2049595 C184.49805,49.6441813 195.231926,53.0843636 195.231926,53.0843636 C202.199197,70.715562 197.815773,83.7344152 196.486756,86.9583937 C204.694018,95.9223682 209.660343,107.340889 209.660343,121.315702 C209.660343,170.478725 179.716133,181.303747 151.213281,184.472614 C155.80443,188.444828 159.895342,196.234518 159.895342,208.176593 C159.895342,225.303317 159.746968,239.087361 159.746968,243.303333 C159.746968,246.709601 162.05102,250.70089 168.53925,249.443941 C219.370432,232.499507 256,184.536204 256,128.00106 C256,57.3066942 198.691187,0 128.00106,0 Z M47.9405593,182.340212 C47.6586465,182.976105 46.6581745,183.166873 45.7467277,182.730227 C44.8183235,182.312656 44.2968914,181.445722 44.5978808,180.80771 C44.8734344,180.152739 45.876026,179.97045 46.8023103,180.409216 C47.7328342,180.826786 48.2627451,181.702199 47.9405593,182.340212 Z M54.2367892,187.958254 C53.6263318,188.524199 52.4329723,188.261363 51.6232682,187.366874 C50.7860088,186.474504 50.6291553,185.281144 51.2480912,184.70672 C51.8776254,184.140775 53.0349512,184.405731 53.8743302,185.298101 C54.7115892,186.201069 54.8748019,187.38595 54.2367892,187.958254 Z M58.5562413,195.146347 C57.7719732,195.691096 56.4895886,195.180261 55.6968417,194.042013 C54.9125733,192.903764 54.9125733,191.538713 55.713799,190.991845 C56.5086651,190.444977 57.7719732,190.936735 58.5753181,192.066505 C59.3574669,193.22383 59.3574669,194.58888 58.5562413,195.146347 Z M65.8613592,203.471174 C65.1597571,204.244846 63.6654083,204.03712 62.5716717,202.981538 C61.4524999,201.94927 61.1409122,200.484596 61.8446341,199.710926 C62.5547146,198.935137 64.0575422,199.15346 65.1597571,200.200564 C66.2704506,201.230712 66.6095936,202.705984 65.8613592,203.471174 Z M75.3025151,206.281542 C74.9930474,207.284134 73.553809,207.739857 72.1039724,207.313809 C70.6562556,206.875043 69.7087748,205.700761 70.0012857,204.687571 C70.302275,203.678621 71.7478721,203.20382 73.2083069,203.659543 C74.6539041,204.09619 75.6035048,205.261994 75.3025151,206.281542 Z M86.046947,207.473627 C86.0829806,208.529209 84.8535871,209.404622 83.3316829,209.4237 C81.8013,209.457614 80.563428,208.603398 80.5464708,207.564772 C80.5464708,206.498591 81.7483088,205.631657 83.2786917,205.606221 C84.8005962,205.576546 86.046947,206.424403 86.046947,207.473627 Z M96.6021471,207.069023 C96.7844366,208.099171 95.7267341,209.156872 94.215428,209.438785 C92.7295577,209.710099 91.3539086,209.074206 91.1652603,208.052538 C90.9808515,206.996955 92.0576306,205.939253 93.5413813,205.66582 C95.054807,205.402984 96.4092596,206.021919 96.6021471,207.069023 Z" fill="currentColor"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
9
packages/hoppscotch-app/assets/icons/auth/google.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg width="256px" height="262px" viewBox="0 0 256 262" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
|
||||
<g>
|
||||
<path d="M255.878,133.451 C255.878,122.717 255.007,114.884 253.122,106.761 L130.55,106.761 L130.55,155.209 L202.497,155.209 C201.047,167.249 193.214,185.381 175.807,197.565 L175.563,199.187 L214.318,229.21 L217.003,229.478 C241.662,206.704 255.878,173.196 255.878,133.451" fill="currentColor"></path>
|
||||
<path d="M130.55,261.1 C165.798,261.1 195.389,249.495 217.003,229.478 L175.807,197.565 C164.783,205.253 149.987,210.62 130.55,210.62 C96.027,210.62 66.726,187.847 56.281,156.37 L54.75,156.5 L14.452,187.687 L13.925,189.152 C35.393,231.798 79.49,261.1 130.55,261.1" fill="currentColor"></path>
|
||||
<path d="M56.281,156.37 C53.525,148.247 51.93,139.543 51.93,130.55 C51.93,121.556 53.525,112.853 56.136,104.73 L56.063,103 L15.26,71.312 L13.925,71.947 C5.077,89.644 0,109.517 0,130.55 C0,151.583 5.077,171.455 13.925,189.152 L56.281,156.37" fill="currentColor"></path>
|
||||
<path d="M130.55,50.479 C155.064,50.479 171.6,61.068 181.029,69.917 L217.873,33.943 C195.245,12.91 165.798,0 130.55,0 C79.49,0 35.393,29.301 13.925,71.947 L56.136,104.73 C66.726,73.253 96.027,50.479 130.55,50.479" fill="currentColor"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
4
packages/hoppscotch-app/assets/icons/book-open.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M2 3h6a4 4 0 014 4v14a3 3 0 00-3-3H2z"></path>
|
||||
<path d="M22 3h-6a4 4 0 00-4 4v14a3 3 0 013-3h7z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 306 B |
4
packages/hoppscotch-app/assets/icons/book.svg
Normal file
@@ -0,0 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M4 19.5A2.5 2.5 0 016.5 17H20"></path>
|
||||
<path d="M6.5 2H20v20H6.5A2.5 2.5 0 014 19.5v-15A2.5 2.5 0 016.5 2z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 317 B |
5
packages/hoppscotch-app/assets/icons/box.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M21 16V8a2 2 0 00-1-1.73l-7-4a2 2 0 00-2 0l-7 4A2 2 0 003 8v8a2 2 0 001 1.73l7 4a2 2 0 002 0l7-4A2 2 0 0021 16z"></path>
|
||||
<polyline points="3.27 6.96 12 12.01 20.73 6.96"></polyline>
|
||||
<line x1="12" y1="22.08" x2="12" y2="12"></line>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 434 B |
19
packages/hoppscotch-app/assets/icons/brands/chrome.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
|
||||
<svg width="256px" height="257px" viewBox="0 0 256 257" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
|
||||
<defs>
|
||||
<linearGradient x1="49.9976632%" y1="0.70578635%" x2="49.9976632%" y2="96.9898739%" id="linearGradient-1">
|
||||
<stop stop-color="#86BBE5" offset="0%"></stop>
|
||||
<stop stop-color="#1072BA" offset="100%"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g>
|
||||
<path d="M127.788721,0.035213205 C127.788721,0.035213205 203.109766,-3.34525447 243.041541,72.3631362 L121.379917,72.3631362 C121.379917,72.3631362 98.4209078,71.6236589 78.8071527,99.477304 C73.1730399,111.168088 67.1163686,123.211004 73.9125172,146.944704 C64.1232462,130.359285 21.9378267,56.9045392 21.9378267,56.9045392 C21.9378267,56.9045392 51.6929849,3.02833563 127.788721,0.035213205 L127.788721,0.035213205 Z" fill="#EF3F36"></path>
|
||||
<path d="M239.132875,192.228886 C239.132875,192.228886 204.377442,259.169188 118.87978,255.859147 C129.443741,237.583494 179.728198,150.501238 179.728198,150.501238 C179.728198,150.501238 191.876754,130.993122 177.544979,100.075928 C170.255846,89.335901 162.82586,78.1028886 138.88088,72.1166437 C158.142503,71.9405777 242.830261,72.1166437 242.830261,72.1166437 C242.830261,72.1166437 274.557359,124.830812 239.132875,192.228886 L239.132875,192.228886 Z" fill="#FCD900"></path>
|
||||
<path d="M16.9727648,192.757084 C16.9727648,192.757084 -23.6280605,129.197249 22.0082531,56.7988996 C32.5370014,75.074553 82.821458,162.156809 82.821458,162.156809 C82.821458,162.156809 93.6671252,182.439615 127.577442,185.46795 C140.500688,184.517194 153.952132,183.70729 171.136176,165.995048 C161.663824,182.756534 119.161486,256 119.161486,256 C119.161486,256 57.6088033,257.126823 16.9727648,192.757084 L16.9727648,192.757084 Z" fill="#61BC5B"></path>
|
||||
<path d="M118.844567,256.492985 L135.958184,185.080605 C135.958184,185.080605 154.762036,183.601651 170.537552,166.311967 C160.748281,183.531224 118.844567,256.492985 118.844567,256.492985 L118.844567,256.492985 Z" fill="#5AB055"></path>
|
||||
<path d="M70.4616231,129.056396 C70.4616231,97.5757909 95.9911967,72.0462173 127.471802,72.0462173 C158.952407,72.0462173 184.481981,97.5757909 184.481981,129.056396 C184.481981,160.537001 158.952407,186.066575 127.471802,186.066575 C95.9911967,186.031362 70.4616231,160.537001 70.4616231,129.056396 L70.4616231,129.056396 Z" fill="#FFFFFF"></path>
|
||||
<path d="M80.0044017,129.056396 C80.0044017,102.857772 101.237964,81.5889959 127.471802,81.5889959 C153.670426,81.5889959 174.939202,102.822558 174.939202,129.056396 C174.939202,155.255021 153.70564,176.523796 127.471802,176.523796 C101.273177,176.523796 80.0044017,155.255021 80.0044017,129.056396 L80.0044017,129.056396 Z" fill="url(#linearGradient-1)"></path>
|
||||
<path d="M242.795048,72.1518569 L172.333425,92.8220083 C172.333425,92.8220083 161.699037,77.2225585 138.845667,72.1518569 C158.670702,72.0462173 242.795048,72.1518569 242.795048,72.1518569 L242.795048,72.1518569 Z" fill="#EACA05"></path>
|
||||
<path d="M72.5392022,144.338927 C62.6442916,127.190096 21.9378267,56.9045392 21.9378267,56.9045392 L74.1237964,108.527098 C74.1237964,108.527098 68.7713893,119.548831 70.778542,135.324347 L72.5392022,144.338927 L72.5392022,144.338927 Z" fill="#DF3A32"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
6
packages/hoppscotch-app/assets/icons/brands/discord.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="256px" height="199px" viewBox="0 0 256 199" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
|
||||
<g>
|
||||
<path d="M216.856339,16.5966031 C200.285002,8.84328665 182.566144,3.2084988 164.041564,0 C161.766523,4.11318106 159.108624,9.64549908 157.276099,14.0464379 C137.583995,11.0849896 118.072967,11.0849896 98.7430163,14.0464379 C96.9108417,9.64549908 94.1925838,4.11318106 91.8971895,0 C73.3526068,3.2084988 55.6133949,8.86399117 39.0420583,16.6376612 C5.61752293,67.146514 -3.4433191,116.400813 1.08711069,164.955721 C23.2560196,181.510915 44.7403634,191.567697 65.8621325,198.148576 C71.0772151,190.971126 75.7283628,183.341335 79.7352139,175.300261 C72.104019,172.400575 64.7949724,168.822202 57.8887866,164.667963 C59.7209612,163.310589 61.5131304,161.891452 63.2445898,160.431257 C105.36741,180.133187 151.134928,180.133187 192.754523,160.431257 C194.506336,161.891452 196.298154,163.310589 198.110326,164.667963 C191.183787,168.842556 183.854737,172.420929 176.223542,175.320965 C180.230393,183.341335 184.861538,190.991831 190.096624,198.16893 C211.238746,191.588051 232.743023,181.531619 254.911949,164.955721 C260.227747,108.668201 245.831087,59.8662432 216.856339,16.5966031 Z M85.4738752,135.09489 C72.8290281,135.09489 62.4592217,123.290155 62.4592217,108.914901 C62.4592217,94.5396472 72.607595,82.7145587 85.4738752,82.7145587 C98.3405064,82.7145587 108.709962,94.5189427 108.488529,108.914901 C108.508531,123.290155 98.3405064,135.09489 85.4738752,135.09489 Z M170.525237,135.09489 C157.88039,135.09489 147.510584,123.290155 147.510584,108.914901 C147.510584,94.5396472 157.658606,82.7145587 170.525237,82.7145587 C183.391518,82.7145587 193.761324,94.5189427 193.539891,108.914901 C193.539891,123.290155 183.391518,135.09489 170.525237,135.09489 Z" fill="#5865F2" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
6
packages/hoppscotch-app/assets/icons/brands/facebook.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
aria-label="Facebook" role="img"
|
||||
viewBox="0 0 512 512"><rect
|
||||
width="512" height="512"
|
||||
rx="15%"
|
||||
fill="#1877f2"/><path d="M355.6 330l11.4-74h-71v-48c0-20.2 9.9-40 41.7-40H370v-63s-29.3-5-57.3-5c-58.5 0-96.7 35.4-96.7 99.6V256h-65v74h65v182h80V330h59.6z" fill="#fff"/></svg>
|
||||
|
After Width: | Height: | Size: 311 B |
201
packages/hoppscotch-app/assets/icons/brands/firefox.svg
Normal file
@@ -0,0 +1,201 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="256px" height="265px" viewBox="0 0 256 265" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
|
||||
<defs>
|
||||
<radialGradient id="SVGID_1_" cx="-14920.2256" cy="-8274.0713" r="124.382" fx="-14928.1611" fy="-8274.0713" gradientTransform="matrix(0.7398 2.920152e-02 -4.866920e-02 1.0902 10886.5264 9537.6963)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.1" style="stop-color:#FFEA00"/>
|
||||
<stop offset="0.17" style="stop-color:#FFDE00"/>
|
||||
<stop offset="0.28" style="stop-color:#FFBF00"/>
|
||||
<stop offset="0.43" style="stop-color:#FF8E00"/>
|
||||
<stop offset="0.77" style="stop-color:#FF272D"/>
|
||||
<stop offset="0.87" style="stop-color:#E0255A"/>
|
||||
<stop offset="0.95" style="stop-color:#CC2477"/>
|
||||
<stop offset="1" style="stop-color:#C42482"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="_Path__1_" cx="-8176.5342" cy="-7746.3013" r="218.2726" gradientTransform="matrix(1.1973 0 0 1.1973 9957.373 9315.5225)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" style="stop-color:#00CCDA"/>
|
||||
<stop offset="0.22" style="stop-color:#0083FF"/>
|
||||
<stop offset="0.26" style="stop-color:#007AF9"/>
|
||||
<stop offset="0.33" style="stop-color:#0060E8"/>
|
||||
<stop offset="0.33" style="stop-color:#005FE7"/>
|
||||
<stop offset="0.44" style="stop-color:#2639AD"/>
|
||||
<stop offset="0.52" style="stop-color:#401E84"/>
|
||||
<stop offset="0.57" style="stop-color:#4A1475"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="145.2425" y1="481.7857" x2="93.8365" y2="313.6288" gradientTransform="matrix(1 0 0 1 0 -286)">
|
||||
<stop offset="0" style="stop-color:#000F43;stop-opacity:0.4"/>
|
||||
<stop offset="0.48" style="stop-color:#001962;stop-opacity:0.17"/>
|
||||
<stop offset="1" style="stop-color:#002079;stop-opacity:0"/>
|
||||
</linearGradient>
|
||||
<radialGradient id="SVGID_3_" cx="-9132.6758" cy="-6696.7002" r="73.6256" gradientTransform="matrix(1.1875 0.1168 -0.1168 1.1875 10322.1836 9194.8447)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" style="stop-color:#FFEA00"/>
|
||||
<stop offset="0.5" style="stop-color:#FF272D"/>
|
||||
<stop offset="1" style="stop-color:#C42482"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="SVGID_4_" cx="-9139.3379" cy="-6767.2969" r="122.9475" gradientTransform="matrix(1.1875 0.1168 -0.1168 1.1875 10322.1836 9194.8447)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" style="stop-color:#FFE900"/>
|
||||
<stop offset="0.16" style="stop-color:#FFAF0E"/>
|
||||
<stop offset="0.32" style="stop-color:#FF7A1B"/>
|
||||
<stop offset="0.47" style="stop-color:#FF4E26"/>
|
||||
<stop offset="0.62" style="stop-color:#FF2C2E"/>
|
||||
<stop offset="0.76" style="stop-color:#FF1434"/>
|
||||
<stop offset="0.89" style="stop-color:#FF0538"/>
|
||||
<stop offset="1" style="stop-color:#FF0039"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="SVGID_5_" cx="-9121.9473" cy="-6653.3262" r="112.8177" gradientTransform="matrix(1.1875 0.1168 -0.1168 1.1875 10322.1836 9194.8447)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0" style="stop-color:#FF272D"/>
|
||||
<stop offset="0.5" style="stop-color:#C42482"/>
|
||||
<stop offset="0.99" style="stop-color:#620700"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="SVGID_6_" cx="227.574" cy="408.42" r="215.776" fx="235.2917" fy="404.6054" gradientTransform="matrix(0.9734 0 0 0.9734 -29.4728 -291.7491)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.16" style="stop-color:#FFEA00"/>
|
||||
<stop offset="0.23" style="stop-color:#FFDE00"/>
|
||||
<stop offset="0.37" style="stop-color:#FFBF00"/>
|
||||
<stop offset="0.54" style="stop-color:#FF8E00"/>
|
||||
<stop offset="0.76" style="stop-color:#FF272D"/>
|
||||
<stop offset="0.8" style="stop-color:#F92433"/>
|
||||
<stop offset="0.84" style="stop-color:#E91C45"/>
|
||||
<stop offset="0.89" style="stop-color:#CF0E62"/>
|
||||
<stop offset="0.94" style="stop-color:#B5007F"/>
|
||||
</radialGradient>
|
||||
<radialGradient id="SVGID_7_" cx="215.2015" cy="308.3499" r="245.9198" gradientTransform="matrix(0.9734 0 0 0.9734 -29.4728 -291.7491)" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0.28" style="stop-color:#FFEA00"/>
|
||||
<stop offset="0.4" style="stop-color:#FFDD00"/>
|
||||
<stop offset="0.63" style="stop-color:#FFBA00"/>
|
||||
<stop offset="0.86" style="stop-color:#FF9100"/>
|
||||
<stop offset="0.93" style="stop-color:#FF6711"/>
|
||||
<stop offset="0.99" style="stop-color:#FF4A1D"/>
|
||||
</radialGradient>
|
||||
<linearGradient id="SVGID_8_" gradientUnits="userSpaceOnUse" x1="-9348.6621" y1="-6754.1943" x2="-9266.2168" y2="-6775.209" gradientTransform="matrix(1.22 0.12 -0.12 1.22 10634.7598 9460)">
|
||||
<stop offset="0" style="stop-color:#C42482;stop-opacity:0.5"/>
|
||||
<stop offset="0.47" style="stop-color:#FF272D;stop-opacity:0.5"/>
|
||||
<stop offset="0.49" style="stop-color:#FF2C2C;stop-opacity:0.51"/>
|
||||
<stop offset="0.68" style="stop-color:#FF7A1A;stop-opacity:0.72"/>
|
||||
<stop offset="0.83" style="stop-color:#FFB20D;stop-opacity:0.87"/>
|
||||
<stop offset="0.94" style="stop-color:#FFD605;stop-opacity:0.96"/>
|
||||
<stop offset="1" style="stop-color:#FFE302"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="SVGID_9_" gradientUnits="userSpaceOnUse" x1="103.0922" y1="-1126.7808" x2="88.439" y2="-1164.3336" gradientTransform="matrix(0.99 0.1 -0.1 0.99 -175.62 1176.2098)">
|
||||
<stop offset="0" style="stop-color:#891551;stop-opacity:0.6"/>
|
||||
<stop offset="1" style="stop-color:#C42482;stop-opacity:0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="SVGID_10_" gradientUnits="userSpaceOnUse" x1="-193.2793" y1="534.1769" x2="-168.0087" y2="562.8766" gradientTransform="matrix(0.99 0.1 -0.1 0.99 303.51 -384.2)">
|
||||
<stop offset="1.000000e-02" style="stop-color:#891551;stop-opacity:0.5"/>
|
||||
<stop offset="0.48" style="stop-color:#FF272D;stop-opacity:0.5"/>
|
||||
<stop offset="1" style="stop-color:#FF272D;stop-opacity:0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="SVGID_11_" gradientUnits="userSpaceOnUse" x1="-144.6839" y1="583.6765" x2="-144.6651" y2="563.5964" gradientTransform="matrix(0.99 0.1 -0.1 0.99 303.51 -384.2)">
|
||||
<stop offset="0" style="stop-color:#C42482"/>
|
||||
<stop offset="8.000000e-02" style="stop-color:#C42482;stop-opacity:0.81"/>
|
||||
<stop offset="0.21" style="stop-color:#C42482;stop-opacity:0.57"/>
|
||||
<stop offset="0.33" style="stop-color:#C42482;stop-opacity:0.36"/>
|
||||
<stop offset="0.45" style="stop-color:#C42482;stop-opacity:0.2"/>
|
||||
<stop offset="0.56" style="stop-color:#C42482;stop-opacity:9.000000e-02"/>
|
||||
<stop offset="0.67" style="stop-color:#C42482;stop-opacity:2.000000e-02"/>
|
||||
<stop offset="0.77" style="stop-color:#C42482;stop-opacity:0"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="SVGID_12_" gradientUnits="userSpaceOnUse" x1="166.5909" y1="295.7137" x2="248.6675" y2="479.2461" gradientTransform="matrix(1 0 0 1 0 -286)">
|
||||
<stop offset="0" style="stop-color:#FFF14F"/>
|
||||
<stop offset="0.27" style="stop-color:#FFEE4C"/>
|
||||
<stop offset="0.45" style="stop-color:#FFE643"/>
|
||||
<stop offset="0.61" style="stop-color:#FFD834"/>
|
||||
<stop offset="0.76" style="stop-color:#FFC41E"/>
|
||||
<stop offset="0.89" style="stop-color:#FFAB02"/>
|
||||
<stop offset="0.9" style="stop-color:#FFA900"/>
|
||||
<stop offset="0.95" style="stop-color:#FFA000"/>
|
||||
<stop offset="1" style="stop-color:#FF9100"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="SVGID_13_" gradientUnits="userSpaceOnUse" x1="182.8878" y1="401.2735" x2="144.0111" y2="505.6616" gradientTransform="matrix(1 0 0 1 0 -286)">
|
||||
<stop offset="0" style="stop-color:#FF8E00"/>
|
||||
<stop offset="4.000000e-02" style="stop-color:#FF8E00;stop-opacity:0.86"/>
|
||||
<stop offset="8.000000e-02" style="stop-color:#FF8E00;stop-opacity:0.73"/>
|
||||
<stop offset="0.13" style="stop-color:#FF8E00;stop-opacity:0.63"/>
|
||||
<stop offset="0.18" style="stop-color:#FF8E00;stop-opacity:0.56"/>
|
||||
<stop offset="0.23" style="stop-color:#FF8E00;stop-opacity:0.51"/>
|
||||
<stop offset="0.28" style="stop-color:#FF8E00;stop-opacity:0.5"/>
|
||||
<stop offset="0.39" style="stop-color:#FF8E00;stop-opacity:0.48"/>
|
||||
<stop offset="0.52" style="stop-color:#FF8E00;stop-opacity:0.42"/>
|
||||
<stop offset="0.68" style="stop-color:#FF8E00;stop-opacity:0.31"/>
|
||||
<stop offset="0.84" style="stop-color:#FF8E00;stop-opacity:0.17"/>
|
||||
<stop offset="1" style="stop-color:#FF8E00;stop-opacity:0"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<g>
|
||||
<path fill="url(#SVGID_1_)" d="M206.8,24.6c-6.4,7.5-9.4,24.3-2.9,41.4s16.5,13.4,22.7,30.8c8.2,23,4.4,53.9,4.4,53.9 s9.9,28.6,16.8-1.8C263.1,91.8,206.8,38.7,206.8,24.6z"/>
|
||||
<path id="_Path_" fill="url(#_Path__1_)" d="M128.4,261.7c65.9,0,119.2-53.6,119.2-119.8S194.2,22.2,128.4,22.2S9.3,75.8 9.3,141.9
|
||||
C9.2,208.1,62.6,261.7,128.4,261.7z"/>
|
||||
<path
|
||||
fill="url(#SVGID_2_)"
|
||||
d="M217.7,215.8c-2.6,1.8-5.3,3.4-8.1,4.9c3.7-5.5,7.2-11.1,10.3-16.9c2.5-2.8,4.9-5.5,6.8-8.5
|
||||
c0.9-1.5,2-3.2,3.1-5.3c6.7-12.1,14.1-31.6,14.3-51.6v-1.5c0-5-0.5-10.1-1.5-15c0.1,0.4,0.1,0.8,0.2,1.2c-0.1-0.3-0.1-0.6-0.2-0.9
|
||||
c0.1,0.5,0.2,1.1,0.3,1.6c1.4,11.6,0.4,22.9-4.5,31.3c-0.1,0.1-0.2,0.2-0.2,0.4c2.5-12.7,3.4-26.7,0.6-40.7c0,0-1.1-6.8-9.5-27.5
|
||||
c-4.8-11.9-13.4-21.7-20.9-28.8c-6.6-8.2-12.7-13.7-16-17.2c-6.9-7.3-9.8-12.8-11-16.3c-1-0.5-14.3-13.4-15.3-13.9
|
||||
c-5.8,9-23.9,37-15.3,63.1c3.9,11.9,13.8,24.2,24.2,31.1c0.5,0.5,6.2,6.7,8.9,20.7c2.8,14.5,1.3,25.7-4.4,42.4
|
||||
c-6.8,14.6-24.2,29.1-40.5,30.6c-34.8,3.2-47.6-17.5-47.6-17.5c12.4,5,26.2,3.9,34.6-1.2c8.4-5.2,13.5-9.1,17.7-7.6
|
||||
c4.1,1.5,7.3-2.9,4.4-7.5c-4.6-7.1-13-10.7-21.3-9.3c-8.4,1.4-16.2,8.1-27.2,1.6c-0.7-0.4-1.4-0.9-2.1-1.4s2.4,0.7,1.6,0.2
|
||||
c-2.1-1.2-6-3.7-6.9-4.6c-0.2-0.2,1.7,0.6,1.5,0.4c-10.3-8.5-9-14.3-8.7-17.9c0.3-2.9,2.1-6.6,5.3-8.1c1.5,0.8,2.5,1.5,2.5,1.5
|
||||
s-0.7-1.3-1-2c0.1-0.1,0.2,0,0.4-0.1c1.3,0.6,4,2.2,5.5,3.1c1.9,1.3,2.5,2.5,2.5,2.5s0.5-0.3,0.1-1.4c-0.1-0.5-0.7-2-2.6-3.5h0.1
|
||||
c1.1,0.6,2.2,1.4,3.2,2.2c0.5-1.9,1.5-3.9,1.3-7.5c-0.1-2.5-0.1-3.2-0.5-4.2c-0.4-0.8,0.2-1.2,0.9-0.3c-0.1-0.7-0.3-1.4-0.6-2v-0.1
|
||||
c0.9-3,18.3-10.9,19.6-11.8c2.1-1.5,3.8-3.4,5.1-5.6c1-1.5,1.7-3.7,1.9-7c0.1-2.4-1-4-18.7-5.8c-4.8-0.5-7.7-4-9.3-7.2
|
||||
c-0.3-0.7-0.6-1.3-0.9-2s-0.5-1.5-0.7-2.3c2.9-8.3,7.7-15.3,14.9-20.6c0.4-0.4-1.6,0.1-1.2-0.3c0.5-0.4,3.4-1.6,4-1.9
|
||||
c0.7-0.3-2.9-1.9-6.1-1.5s-3.9,0.8-5.7,1.5c0.7-0.7,3-1.7,2.5-1.6c-3.5,0.5-7.8,2.6-11.5,4.9c0-0.4,0.1-0.8,0.2-1.2
|
||||
c-1.7,0.7-6,3.7-7.2,6.2c0.1-0.5,0.1-1,0.1-1.5c-1.3,1.1-2.5,2.3-3.5,3.7L85,60.5c-10-4-18.9-4.3-26.3-2.5c-1.6-1.6-2.4-0.4-6.2-8.6
|
||||
c-0.3-0.5,0.2,0.5,0,0c-0.6-1.6,0.4,2.1,0,0c-6.2,5-14.4,10.6-18.4,14.5c0,0.2,4.6-1.3,0,0c-1.6,0.5-1.5,1.4-1.7,10.1
|
||||
c-0.1,0.7,0,1.4-0.1,2c-3.2,4-5.3,7.4-6.1,9.2c-4.1,7-8.6,18-12.9,35.3c1.9-4.7,4.2-9.2,6.9-13.5c-3.6,9.2-7.1,23.7-7.8,45.9
|
||||
c0.9-4.6,2-9.1,3.4-13.6c-0.9,18.5,2.3,36.9,9.3,54c2.5,6.1,6.6,15.4,13.7,25.6c22.2,23.3,53.4,37.8,88,37.8
|
||||
C162.9,256.7,195.4,240.9,217.7,215.8z"/>
|
||||
<path fill="url(#SVGID_3_)" d="M190.9,232.7c43.7-5.1,63.1-50.1,38.2-51C206.7,181,170.2,235.1,190.9,232.7z"/>
|
||||
<path fill="url(#SVGID_4_)" d="M232.3,172.5c30.1-17.5,22.2-55.3,22.2-55.3s-11.6,13.5-19.5,35C227.3,173.5,214.2,183.1,232.3,172.5
|
||||
z"/>
|
||||
<path fill="url(#SVGID_5_)" d="M136.8,255.1c42,13.4,78-19.7,55.8-30.7C172.4,214.5,116.9,248.8,136.8,255.1z"/>
|
||||
|
||||
<path
|
||||
fill="url(#SVGID_6_)"
|
||||
d="M235.5,188.6c1-1.4,2.4-6,3.6-8.1c7.4-12,7.5-21.5,7.5-21.7c4.5-22.3,4.1-31.5,1.3-48.3
|
||||
c-2.2-13.6-11.9-33.1-20.3-42.4c-8.6-9.7-2.6-6.5-10.9-13.6c-7.3-8.1-14.5-16.2-18.3-19.4c-28-23.4-27.4-28.4-26.8-29.2
|
||||
c-0.1,0.1-0.2,0.2-0.4,0.4c-0.3-1.3-0.6-2.4-0.6-2.4S155.2,19.1,152,44.6c-2.1,16.6,4.1,34,13.2,45.1c4.7,5.8,10,11,15.8,15.6l0,0
|
||||
c6.8,9.8,10.6,21.9,10.6,34.9c0,32.6-26.4,58.9-59,58.9c-4.4,0-8.9-0.5-13.2-1.5c-15.4-2.9-24.2-10.7-28.7-16
|
||||
c-2.5-3-3.6-5.2-3.6-5.2c13.8,4.9,29,3.9,38.3-1.2c9.3-5.2,15-9,19.6-7.5c4.5,1.5,8.1-2.9,4.9-7.4c-3.2-4.5-11.4-11-23.6-9.2
|
||||
c-9.3,1.4-17.9,8-30.1,1.6c-0.8-0.4-1.6-0.9-2.3-1.3c-0.8-0.5,2.6,0.7,1.8,0.2c-2.4-1.2-6.6-3.7-7.7-4.6c-0.2-0.2,1.8,0.6,1.7,0.4
|
||||
c-11.4-8.4-10-14.1-9.7-17.7c0.3-2.9,2.4-6.5,5.9-8c1.7,0.8,2.7,1.5,2.7,1.5s-0.7-1.3-1.1-2c0.1-0.1,0.3,0,0.4-0.1
|
||||
c1.4,0.6,4.5,2.1,6.1,3.1c2.1,1.3,2.8,2.5,2.8,2.5s0.6-0.3,0.1-1.4c-0.2-0.5-0.8-2-2.9-3.5h0.1c1.2,0.6,2.4,1.4,3.5,2.2
|
||||
c0.6-1.9,1.6-3.9,1.4-7.5c-0.1-2.5-0.1-3.2-0.6-4.1c-0.4-0.8,0.2-1.2,1-0.3c-0.1-0.7-0.4-1.3-0.7-2V110c1-3,20.3-10.8,21.7-11.7
|
||||
c2.3-1.4,4.2-3.3,5.7-5.5c1.1-1.5,1.9-3.7,2.1-7c0.1-1.5-0.4-2.6-5.5-3.8c-3.1-0.7-7.8-1.3-15.2-2c-5.3-0.5-8.5-3.9-10.3-7.1
|
||||
c-0.3-0.7-0.7-1.3-1-1.9c-0.3-0.7-0.6-1.5-0.8-2.2c3.2-8.4,9-15.5,16.5-20.4c0.4-0.4-1.7,0.1-1.3-0.3c0.5-0.4,3.8-1.6,4.4-1.9
|
||||
c0.8-0.3-3.2-1.8-6.8-1.5c-3.6,0.4-4.3,0.7-6.3,1.5c0.8-0.7,3.3-1.6,2.7-1.6C101,45,96.2,47,92.1,49.3c0-0.4,0.1-0.8,0.2-1.2
|
||||
c-1.9,0.7-6.6,3.7-8,6.2c0.1-0.5,0.1-1,0.1-1.4c-1.4,1.1-2.8,2.3-3.9,3.7l-0.1,0.1c-11.1-4-20.9-4.3-29.1-2.5
|
||||
c-1.8-1.6-4.7-4.1-8.8-12.2c-0.3-0.5-0.4,1-0.6,0.5c-1.6-3.7-2.6-9.8-2.4-14c0,0-3.3,1.5-6,7.8c-0.5,1.1-0.8,1.8-1.2,2.4
|
||||
c-0.2,0.2,0.3-2.1,0.3-1.9c-0.5,0.8-1.7,1.9-2.2,3.4c-0.4,1.1-0.9,1.7-1.2,3l-0.1,0.1c0-0.4,0.1-1.6,0-1.4c-1.3,2.6-2.4,5.3-3.3,8
|
||||
c-1.5,4.8-3.2,11.4-3.5,20c-0.1,0.6,0,1.4-0.1,2c-3.5,4-5.9,7.4-6.8,9.1c-4.5,7-9.5,17.8-14.3,35c2.1-4.7,4.7-9.2,7.7-13.4
|
||||
c-4,9.1-7.9,23.5-8.7,45.5c1-4.6,2.2-9.1,3.7-13.5c-0.7,14.7,1,33,10.3,53.6c5.5,12.1,18.2,36.7,49.3,55.9l0,0
|
||||
c0,0,10.6,7.9,28.7,13.8c1.3,0.5,2.7,1,4.1,1.4c-0.4-0.2-0.9-0.4-1.3-0.5c12.1,3.6,24.6,5.5,37.3,5.5c47,0,60.9-18.9,60.9-18.9
|
||||
l-0.1,0.1c0.7-0.6,1.3-1.3,1.9-1.9c-7.4,7-24.4,7.5-30.7,7c10.8-3.2,17.9-5.9,31.7-11.1c1.6-0.6,3.3-1.3,5-2.1l0.5-0.3
|
||||
c0.3-0.2,0.7-0.3,1-0.5c6.7-3.2,13.1-7.1,18.9-11.8c13.9-11.1,16.9-21.9,18.5-29c-0.2,0.7-0.9,2.3-1.4,3.3
|
||||
c-3.6,7.6-11.5,12.4-20.1,16.4c4.1-5.4,7.9-11,11.4-16.8C232.5,195.9,233.3,191.5,235.5,188.6z"/>
|
||||
<path
|
||||
fill="url(#SVGID_7_)"
|
||||
d="M218.6,215.1c5.7-6.2,10.7-13.4,14.6-21.5c9.9-20.8,25.2-55.5,13.2-91.6
|
||||
c-9.5-28.6-22.6-44.2-39.2-59.5c-27-24.8-34.5-35.9-34.5-42.5c0,0-31.2,34.7-17.7,71s41.2,34.9,59.5,72.7
|
||||
c21.6,44.5-17.4,93.1-49.7,106.7c2-0.4,71.7-16.2,75.4-56.1C240,195,238.4,206.1,218.6,215.1z"/>
|
||||
<path fill="url(#SVGID_8_)" d="M128.2,85.8c0.1-2.4-1.1-3.9-20.6-5.8c-8-0.7-11.1-8.1-12-11.3c-2.8,7.4-4,15.2-3.4,24.6
|
||||
c0.4,6.2,4.6,12.8,6.5,16.6c0,0,0.4-0.6,0.6-0.8c3.7-3.9,19.3-9.8,20.8-10.6C121.8,97.5,128,93,128.2,85.8z"/>
|
||||
<path
|
||||
fill="url(#SVGID_9_)"
|
||||
d="M42.5,42c-0.3-0.5-0.4,1-0.6,0.5c-1.6-3.7-2.6-9.7-2.3-14c0,0-3.3,1.5-6,7.8
|
||||
c-0.5,1.1-0.8,1.8-1.2,2.4c-0.2,0.2,0.3-2.1,0.3-1.9c-0.5,0.8-1.7,1.9-2.2,3.3c-0.4,1.1-0.9,1.8-1.2,3.2c-0.1,0.4,0.1-1.7,0-1.4
|
||||
c-6.4,12.3-7.6,30.9-6.9,30.1C36,57.5,51.5,54.1,51.5,54.1C49.6,52.9,46,49.3,42.5,42L42.5,42z"/>
|
||||
<path fill="url(#SVGID_10_)" d="M93.9,193.4c-18.7-8-40-19.3-39.2-44.9c1.1-33.7,31.5-27.1,31.5-27.1c-1.1,0.3-4.2,2.5-5.3,4.8
|
||||
c-1.1,2.9-3.2,9.5,3.1,16.4c10,10.8-20.5,25.6,26.5,53.6C111.7,196.8,99.5,195.8,93.9,193.4L93.9,193.4z"/>
|
||||
<path
|
||||
fill="url(#SVGID_11_)"
|
||||
d="M87.3,176.6c13.3,4.6,28.7,3.8,38-1.3c6.2-3.5,14.2-9,19-7.6c-4.2-1.7-7.4-2.5-11.3-2.6
|
||||
c-0.7,0-1.4,0-2.1-0.1c-1.4,0-2.8,0.1-4.2,0.2c-2.4,0.2-5,1.7-7.4,1.5c-0.1,0,2.3-1,2.1-1c-1.3,0.3-2.7,0.3-4.1,0.5
|
||||
c-0.9,0.1-1.7,0.2-2.7,0.3c-27.7,2.3-51-15-51-15C61.6,158.1,72.5,171.4,87.3,176.6L87.3,176.6z"/>
|
||||
<path
|
||||
fill="url(#SVGID_12_)"
|
||||
d="M218.5,215.3c28-27.5,42.1-60.8,36.1-98.3c0,0,2.4,19.2-6.7,38.8c4.4-19.2,4.9-43-6.7-67.7
|
||||
c-15.4-32.9-40.8-50.3-50.5-57.5c-14.7-11-20.8-22.1-20.9-24.4c-4.4,9-17.7,39.8-1.4,66.3c15.2,24.9,39.2,32.2,55.9,55
|
||||
C255.3,169.6,218.5,215.3,218.5,215.3z"/>
|
||||
<path fill="url(#SVGID_13_)" d="M214.5,143.8c-9.8-20.2-22-29-33.5-38.5c1.3,1.9,1.7,2.5,2.4,3.8c10.2,10.8,25.1,37.2,14.3,70.4
|
||||
c-20.5,62.4-102.3,33-110.9,24.8c3.5,36.1,63.9,53.4,103.3,30C212.4,212.9,230.5,176.9,214.5,143.8z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 15 KiB |
6
packages/hoppscotch-app/assets/icons/brands/linkedin.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="256px" height="256px" viewBox="0 0 256 256" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
|
||||
<g>
|
||||
<path d="M218.123122,218.127392 L180.191928,218.127392 L180.191928,158.724263 C180.191928,144.559023 179.939053,126.323993 160.463756,126.323993 C140.707926,126.323993 137.685284,141.757585 137.685284,157.692986 L137.685284,218.123441 L99.7540894,218.123441 L99.7540894,95.9665207 L136.168036,95.9665207 L136.168036,112.660562 L136.677736,112.660562 C144.102746,99.9650027 157.908637,92.3824528 172.605689,92.9280076 C211.050535,92.9280076 218.138927,118.216023 218.138927,151.114151 L218.123122,218.127392 Z M56.9550587,79.2685282 C44.7981969,79.2707099 34.9413443,69.4171797 34.9391618,57.260052 C34.93698,45.1029244 44.7902948,35.2458562 56.9471566,35.2436736 C69.1040185,35.2414916 78.9608713,45.0950217 78.963054,57.2521493 C78.9641017,63.090208 76.6459976,68.6895714 72.5186979,72.8184433 C68.3913982,76.9473153 62.7929898,79.26748 56.9550587,79.2685282 M75.9206558,218.127392 L37.94995,218.127392 L37.94995,95.9665207 L75.9206558,95.9665207 L75.9206558,218.127392 Z M237.033403,0.0182577091 L18.8895249,0.0182577091 C8.57959469,-0.0980923971 0.124827038,8.16056231 -0.001,18.4706066 L-0.001,237.524091 C0.120519052,247.839103 8.57460631,256.105934 18.8895249,255.9977 L237.033403,255.9977 C247.368728,256.125818 255.855922,247.859464 255.999,237.524091 L255.999,18.4548016 C255.851624,8.12438979 247.363742,-0.133792868 237.033403,0.000790807055" fill="#0077b5"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |