Compare commits

...

450 Commits

Author SHA1 Message Date
Andrew Bastin
407dad8c7f fix: test breaking for script setup 2021-11-17 21:08:18 +05:30
liyasthomas
9f944506e0 refactor: convert to script setup 2021-11-17 20:12:15 +05:30
liyasthomas
114c37645a refactor: convert to script setup 2021-11-17 19:59:32 +05:30
liyasthomas
8f9bb621b8 fix: parse mqtt pathname - fixed #1959 2021-11-17 17:43:04 +05:30
liyasthomas
48a6c87d9d refactor: convert to script setup 2021-11-17 17:25:18 +05:30
BrainCrumbz
f28b55dd4d chore(i18n): update it translations (#1958)
* i18n(it): review action group

* i18n(it): review 'add' and 'app' groups

* i18n(it): review auth, authorization and collection groups

* i18n(it): review confirm, count, documentation and empty groups

* i18n(it): review environment, error, export, folder, graphql and header groups

* i18n(it): review helpers, hide, import, layout, modal and mqtt groups

* i18n(it): review navigation, preRequest, profile and remove groups

* i18n(it): review request and response groups

* i18n(it): review settings group

* i18n(it): review shortcut and show group

* i18n(it): review socketio, sse, state and support group

* i18n(it): review tab and team groups

* i18n(it): review test and websocket groups
2021-11-17 15:55:39 +05:30
Sobolev Sergey
8a8b4b0245 chore(i18n): update ru translations (#1957) 2021-11-17 13:37:48 +05:30
liyasthomas
0afbc57012 chore: remove absolute packages 2021-11-16 22:09:40 +05:30
Wilson G
c651f2440f feat: add undo when parameters and headers are deleted, resolves #1951 (#1955)
Co-authored-by: liyasthomas <liyascthomas@gmail.com>
2021-11-16 16:34:11 +05:30
Andrew Bastin
8c05084994 fix: issue with the request body from duplication being linked in memory fixes #1954 2021-11-16 15:37:19 +05:30
liyasthomas
6813be47f0 fix: append protocol if empty - resolved #1927 2021-11-16 09:25:25 +05:30
liyasthomas
30327e8d27 feat: scroll logs to the bottom on update 2021-11-16 08:09:44 +05:30
liyasthomas
8096ed300d feat: add authentication to MQTT - resolved #1772 2021-11-16 07:52:26 +05:30
liyasthomas
4a8efbf426 feat: better empty state illustratils in profile and error pages 2021-11-15 20:59:07 +05:30
liyasthomas
7a6d117a76 feat: checkbox component 2021-11-15 20:25:40 +05:30
Andrew Bastin
73568043f1 feat: remove broken test system in codemirror-lang-graphql 2021-11-15 17:03:04 +05:30
liyasthomas
76a3b35e9e Merge branch 'feat/codemirror-6' 2021-11-15 16:45:20 +05:30
liyasthomas
feb1991da3 fix: error on updating readonly editor 2021-11-15 16:43:10 +05:30
liyasthomas
2bee4342b8 chore: cleanup 2021-11-15 16:25:56 +05:30
Andrew Bastin
7e9fc486f2 feat: placeholder implementation 2021-11-15 16:22:54 +05:30
liyasthomas
1d99b79926 feat: style autocomplete tooltips in codemirror 2021-11-15 07:49:07 +05:30
liyasthomas
eb8347f942 chore(deps): bump 2021-11-14 20:16:40 +05:30
liyasthomas
d383b48916 chore: minor fixes and improvements 2021-11-14 20:15:24 +05:30
Andrew Bastin
e88c40db0a feat: graphql language improvements 2021-11-13 20:14:58 +05:30
liyasthomas
f228f37bb8 feat: configure interceptor when request fail 2021-11-13 08:37:43 +05:30
Andrew Bastin
503a54fc5e fix: fix graphql history sync issue 2021-11-12 22:01:42 +05:30
liyasthomas
48b21aa0bf chore(i18n): updated translations 2021-11-12 18:53:56 +05:30
Andrew Bastin
ca40cc5271 feat: fix reactivity issues in graphql 2021-11-12 17:54:34 +05:30
liyasthomas
1c641c6d11 Merge branch 'i18n' 2021-11-12 17:54:02 +05:30
liyasthomas
32b362f9cc chore(ui): minor ui improvements 2021-11-12 17:52:27 +05:30
liyasthomas
103ef8ee0d fix: remove basic-setup and drawSelection on codemirror instance to resolve ::selection property 2021-11-12 07:05:34 +05:30
Tony
4a6239e017 chore(i18n): update tw translations (#1944) 2021-11-12 05:24:26 +05:30
liyasthomas
1f637edd36 feat: better prompts on actions for team members without admin role 2021-11-11 21:43:45 +05:30
Andrew Bastin
25878b9bb1 fix: fix history not writing when formdata is present 2021-11-11 18:36:26 +05:30
liyasthomas
521a96bffb fix: respect url parameters before settings bulk editor value 2021-11-11 15:02:31 +05:30
liyasthomas
ead1f3954f chore: use i18n 'duplicate' instead of 'copy' 2021-11-10 19:53:44 +05:30
0xc0Der
0ac84b58e3 allow environment variables in request body. (#1942)
* feat: allow environment variables in request body

* chore(ui): minor ui improvements

* chore(deps): bump

* fix: track env vars changes

* feat: allow environment variables in request body

* refactor: better implementation

Co-authored-by: liyasthomas <liyascthomas@gmail.com>
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
2021-11-10 19:52:11 +05:30
0xc0Der
a2f1e37ad2 refactor: added space 2021-11-10 19:09:40 +05:30
Andrew Bastin
373343fea1 refactor: adding space between the copy hyphen 2021-11-10 19:09:40 +05:30
0xc0Der
2ef99026e5 fix: added '-Copy' to duplicated request name 2021-11-10 19:09:40 +05:30
0xc0Der
29aff9accc feat: duplicate request 2021-11-10 19:09:40 +05:30
Andrew Bastin
7d7f628f6e fix: compiling issues for codemirror-lang-graphql 2021-11-10 17:09:44 +05:30
liyasthomas
8f6cf07e82 feat: more syntax hightlight colors 2021-11-10 16:27:12 +05:30
Andrew Bastin
245b8a6e3c fix: compiling issues for codemirror-lang-graphql 2021-11-10 14:31:00 +05:30
liyasthomas
a967100be8 feat: more syntax hightlight colors 2021-11-10 13:53:10 +05:30
Andrew Bastin
a9bca8e1f8 feat: initial graphql language definition 2021-11-10 01:16:08 +05:30
liyasthomas
7de8e6be5e refactor: updated editot theme colors 2021-11-09 22:26:53 +05:30
liyasthomas
6b70a39f02 feat: css variable on themes 2021-11-09 22:05:39 +05:30
liyasthomas
d538d722d7 fix: light theme 2021-11-09 20:28:10 +05:30
liyasthomas
13bd831c5f refactor: init codemirror theming support 2021-11-09 18:46:29 +05:30
Andrew Bastin
9b297ba882 feat: implement reactive theming 2021-11-09 14:35:20 +05:30
liyasthomas
be6c802745 feat: init base theme for codemirror 2021-11-08 17:42:34 +05:30
Andrew Bastin
564cce2462 feat: autocomplete cm6 2021-11-08 16:33:36 +05:30
Andrew Bastin
8f166b8b3f feat: add linting cm6 2021-11-08 15:21:09 +05:30
Andrew Bastin
fe7192ae61 feat: add line wrapping 2021-11-08 14:32:17 +05:30
Liyas Thomas
6d54f21c1e docs: update CODE OF CONDUCT 2021-11-08 13:49:12 +05:30
liyasthomas
87f8f61163 chore(deps): bump 2021-11-07 20:38:15 +05:30
liyasthomas
510ba376e5 chore(ui): minor ui improvements 2021-11-07 11:46:21 +05:30
liyasthomas
40c88b3e35 refactor(ui): better spacing and font-weight 2021-11-06 21:15:28 +05:30
Andrew Bastin
7c65da4cf3 feat: cm 6 json mode + readonly + cursor update 2021-11-06 15:12:35 +05:30
Andrew Bastin
0f07c47e9f fix: race condition on gql client subscription client 2021-11-05 20:22:46 +05:30
liyasthomas
17c45fee11 refactor: migrate icons to lucide.dev 2021-11-05 18:24:16 +05:30
liyasthomas
a63c0817cc perf: remove console (log, debug, and warn) messages 2021-11-05 13:41:25 +05:30
Andrew Bastin
68aa54bdb7 fix: json parsing error when using extension (HOPP-110) 2021-11-05 13:32:27 +05:30
Satoshi Jek
41cb6eb190 i18n: Update Chinese translations (#1940) 2021-11-05 10:46:03 +05:30
liyasthomas
61e5a48b02 perf: improve native image tags 2021-11-05 09:13:04 +05:30
Iagor Moraes
9e4d7df7d0 fix: 100% HTML preview area size (#1938) 2021-11-05 06:25:50 +05:30
Andrew Bastin
e83dbc2e5c feat: early codemirror v6 implementation 2021-11-05 00:33:15 +05:30
liyasthomas
03ab6a208d perf: remove absolute files 2021-11-04 23:23:46 +05:30
liyasthomas
dbd39ba0d8 fix: ignore vue files from stylelint 2021-11-04 20:59:01 +05:30
0xc0Der
136b1ff63b fix: bind key-value with bulk editor at startup. (#1936) 2021-11-04 20:43:21 +05:30
Andrew Bastin
69a6207a4d refactor: move firebase initialization to a plugin 2021-11-04 18:59:35 +05:30
liyasthomas
9e74a8c2e7 chore: lint + bump deps 2021-11-04 18:23:50 +05:30
liyasthomas
ad76d100ee feat: bind key-value pairs and bulk editor 2021-11-04 13:36:51 +05:30
0xc0Der
235968073a sync request params with bulk editor. (#1930) 2021-11-04 13:01:56 +05:30
Andrew Bastin
ad7b8da37e fix: windows issues with sandbox not building initially 2021-11-04 12:47:09 +05:30
liyasthomas
7d3e1a700f chore(release): v2.1.0 2021-11-04 11:14:50 +05:30
liyasthomas
d3a1898dad feat: support to move sidebar to left - fixed #1933 2021-11-04 09:58:15 +05:30
Andrew Bastin
45e508fc36 refactor: pwa install prompt (#1932) 2021-11-03 23:44:01 +05:30
0xc0Der
8edad7ded7 persist environment selection. (#1925)
* feat: persist environment selection

* refactor: minor change to selectedEnvIndex calculation

Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
2021-11-03 23:15:52 +05:30
liyasthomas
75e1adb7b3 refactor: empty state illustrations 2021-11-03 16:10:06 +05:30
Andrew Bastin
22ac13f2f0 fix: docker action issues 2021-11-02 21:22:37 +05:30
liyasthomas
7f0246eb47 Merge branch 'refactor/backend' of https://github.com/hoppscotch/hoppscotch into refactor/backend 2021-11-02 19:48:34 +05:30
liyasthomas
5f0800760f chore: clean up 2021-11-02 19:45:41 +05:30
liyasthomas
6db99c9e37 chore(deps): bump 2021-11-02 19:42:44 +05:30
liyasthomas
3b2cabd3f3 fix: typo 2021-11-02 19:39:39 +05:30
Andrew Bastin
b0dd6b0bd6 feat: fix gqlclient race condition 2021-11-02 19:33:09 +05:30
liyasthomas
874b846e60 fix: fix broken edit team modal
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
2021-11-02 19:33:09 +05:30
liyasthomas
dbe2525c6f docs: updated feature list 2021-11-02 19:33:09 +05:30
Andrew Bastin
afd414fa3f feat: introducing gql polling 2021-11-02 19:33:09 +05:30
liyasthomas
94763dcb31 fix: prompt user on actions with no permission 2021-11-02 19:33:09 +05:30
Andrew Bastin
6314740f46 feat: reinit gql client after login 2021-11-02 19:33:09 +05:30
liyasthomas
d7332120e3 fix: hide popover on click on update role 2021-11-02 19:33:09 +05:30
liyasthomas
8c74fe9925 feat: user's roles prompt on invite modal 2021-11-02 19:33:09 +05:30
liyasthomas
c74ddeb530 fix: team invitation error states 2021-11-02 19:33:09 +05:30
Andrew Bastin
e31c0a9d02 feat: fix subscription auth refresh issues 2021-11-02 19:33:09 +05:30
Andrew Bastin
d9e5d4aec5 feat: add subscriptions for team invitations added and removed 2021-11-02 19:33:09 +05:30
liyasthomas
c1ee8f5dd0 feat: subscriptions on edit team modal 2021-11-02 19:33:09 +05:30
Andrew Bastin
dd59de3de0 feat: add subscription support for useGQLQuery 2021-11-02 19:33:07 +05:30
liyasthomas
511a3c55f3 fix: broken edit team modal 2021-11-02 19:32:49 +05:30
liyasthomas
c9021ab3ca fix: broken edit team action 2021-11-02 19:32:49 +05:30
liyasthomas
a765c4a7cc feat: successfully joined team prompt 2021-11-02 19:32:48 +05:30
Andrew Bastin
ea99732474 refactor: use the unpaginated teamMembers field instead of members 2021-11-02 19:32:48 +05:30
liyasthomas
6c64ffe833 feat: invite system error states and user prompts 2021-11-02 19:32:48 +05:30
Andrew Bastin
5fa6c6cdb3 fix: improper parsed error checks 2021-11-02 19:32:48 +05:30
Andrew Bastin
d94759870e refactor: add error info to send invite results 2021-11-02 19:32:48 +05:30
Andrew Bastin
f0a6fc641a feat: team invitation caching 2021-11-02 19:32:48 +05:30
Andrew Bastin
7ba00bee0b feat: update graphcache keys typings 2021-11-02 19:32:48 +05:30
liyasthomas
dc2bdf81b9 fix: invitation acceptance flow 2021-11-02 19:32:48 +05:30
liyasthomas
187a30abac feat: init loading states, minor ui improvements 2021-11-02 19:32:48 +05:30
liyasthomas
5b824ccb17 feat: minor ui improvements on invite flow 2021-11-02 19:32:48 +05:30
liyasthomas
3bdf2baf97 feat: init invitation wiring
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
2021-11-02 19:32:48 +05:30
Andrew Bastin
9af8a24a89 feat: initial team invites implementation
Co-authored-by: Liyas Thomas <hi@liyasthomas.com>
2021-11-02 19:32:48 +05:30
liyasthomas
57c4759bdb chore: minor fixes and improvements 2021-11-02 19:32:48 +05:30
Andrew Bastin
d9d7261bc5 feat: team invitation mutations 2021-11-02 19:32:48 +05:30
Andrew Bastin
a12315d81a refactor: add graphcache codegen and update types 2021-11-02 19:32:47 +05:30
liyasthomas
9f0956556f feat: support updating user's display name 2021-11-02 19:32:13 +05:30
liyasthomas
748318d44e feat: compact mode for team component 2021-11-02 19:32:13 +05:30
liyasthomas
ff3062cdfc chore: clean up 2021-11-02 19:32:13 +05:30
liyasthomas
48d67fe7e1 fix: init local persistence 2021-11-02 19:32:13 +05:30
liyasthomas
2c9918f9a7 feat: init theming in empty layout 2021-11-02 19:32:13 +05:30
liyasthomas
ee03952201 feat: init teams invite page 2021-11-02 19:32:13 +05:30
liyasthomas
43dcd3c443 feat: invite modal mvp 2021-11-02 19:32:13 +05:30
Andrew Bastin
8f8c42a92a feat: gql codegen + caching + optimistic 2021-11-02 19:32:11 +05:30
liyasthomas
6496aded25 feat: teams modal wrapper 2021-11-02 19:25:36 +05:30
Andrew Bastin
eacf8113af feat: initial setup of new backend comms 2021-11-02 19:20:07 +05:30
liyasthomas
c4a1527153 chore(deps): bump 2021-11-02 19:17:30 +05:30
liyasthomas
4a89a6aafc feat: teams invite modal 2021-11-02 19:17:11 +05:30
Andrew Bastin
52539b084d feat: gql codegen + caching + optimistic 2021-11-02 19:17:09 +05:30
Andrew Bastin
8d5bd051a1 refactor: extract cache info to separate files and add more mutations 2021-11-02 19:15:44 +05:30
liyasthomas
3809e9853e chore(ui): minor ui improvements, empty teams state 2021-11-02 19:15:44 +05:30
Andrew Bastin
5f795acd61 feat: probable user is displayed as authenticated 2021-11-02 19:15:44 +05:30
liyasthomas
17c550404f refactor: updated lint script 2021-11-02 19:15:44 +05:30
Andrew Bastin
a840079119 feat: add persistent cache and optimistic updates 2021-11-02 19:15:42 +05:30
Andrew Bastin
2761894164 fix: queries not waiting for authentication 2021-11-02 19:15:20 +05:30
liyasthomas
6b8bc618dc feat: teams modal wrapper 2021-11-02 19:15:02 +05:30
liyasthomas
258f79604f refactor: remove EAInvite flag 2021-11-02 19:15:02 +05:30
Andrew Bastin
81ae70ee04 fix: fix issue with team queries not updating correctly after mutation 2021-11-02 19:15:02 +05:30
Andrew Bastin
6b02d290a5 feat: team mutation 2021-11-02 19:15:02 +05:30
Andrew Bastin
7ab1bbaf62 refactor: refactor create team modal to new system 2021-11-02 19:15:02 +05:30
liyasthomas
079083d0f2 fix: i18n usage 2021-11-02 19:15:02 +05:30
liyasthomas
0504707aab feat: init profile page 2021-11-02 19:14:59 +05:30
Andrew Bastin
fb4aab875d feat: introduce updated mutation system and team.vue rewrite 2021-11-02 19:14:25 +05:30
Andrew Bastin
7bb32ecf7e feat: initial setup of new backend comms 2021-11-02 19:14:21 +05:30
Andrew Bastin
e129a5c179 feat: fix gqlclient race condition 2021-11-02 19:11:16 +05:30
liyasthomas
8045f26c19 fix: fix broken edit team modal
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
2021-11-02 14:25:38 +05:30
liyasthomas
86516421b5 fix(ci): run build command from root 2021-11-02 07:01:48 +05:30
liyasthomas
bce88ccd44 docs: updated feature list 2021-11-02 06:41:18 +05:30
0xc0Der
66d408b7db Allow duplication of global environment (#1924) 2021-11-02 06:16:16 +05:30
Andrew Bastin
297bf3205f feat: introducing gql polling 2021-11-01 22:27:06 +05:30
liyasthomas
7366b32349 fix: prompt user on actions with no permission 2021-11-01 17:51:01 +05:30
Andrew Bastin
b7e0169c9b feat: reinit gql client after login 2021-11-01 17:30:16 +05:30
liyasthomas
6b6f85cc7e fix: hide popover on click on update role 2021-11-01 15:25:19 +05:30
liyasthomas
2c014a2f4b feat: user's roles prompt on invite modal 2021-11-01 15:04:46 +05:30
liyasthomas
d6df675821 fix: team invitation error states 2021-11-01 14:20:35 +05:30
liyasthomas
427baf4c79 chore: bump dep + minor ui fixes 2021-11-01 13:10:48 +05:30
liyasthomas
4f2b682341 chore(deps): bump 2021-10-31 19:53:05 +05:30
Andrew Bastin
e03f888cb2 feat: fix subscription auth refresh issues 2021-10-31 17:40:03 +05:30
Andrew Bastin
513396d498 feat: add subscriptions for team invitations added and removed 2021-10-30 18:56:34 +05:30
liyasthomas
8f04f0758b fix: use hoppscotch's realtime echo servers - resolved #1913 2021-10-29 08:55:31 +05:30
liyasthomas
7a77bfc248 feat: subscriptions on edit team modal 2021-10-28 20:29:49 +05:30
Andrew Bastin
ddd29374ea feat: add subscription support for useGQLQuery 2021-10-27 22:41:32 +05:30
liyasthomas
4b0d7a6c3d fix: broken edit team modal 2021-10-27 18:32:11 +05:30
liyasthomas
20bfc02a4e fix: disable spellcheck on contenteditable - resolved #1908 2021-10-27 18:17:22 +05:30
Darío Hereñú
2511724b73 chore: updated spanish translation (#1907) 2021-10-27 06:10:17 +05:30
liyasthomas
a851ee3fab fix: broken edit team action 2021-10-26 23:16:56 +05:30
liyasthomas
40f6e6f8a8 fix: typo in test result 2021-10-26 15:36:59 +05:30
liyasthomas
e2fd104c2d feat: test results indicator in response section's tests tab 2021-10-26 13:48:09 +05:30
Andrew Bastin
a3eafa54fa fix: test script not parsing json object if json content type 2021-10-26 13:19:15 +05:30
Sergio Guzmán Mayorga
b90b4a1910 chore: update spanish translations (#1904) 2021-10-26 06:47:28 +05:30
liyasthomas
247df4d5b9 chore: bump deps + fix broken links 2021-10-25 21:04:28 +05:30
Burak Saraloglu
4a3889a76e chore (i18n): updated Turkish translations (#1893) 2021-10-25 05:02:30 +05:30
liyasthomas
224a6e069c chore(deps): bump 2021-10-24 20:06:22 +05:30
liyasthomas
2c3097eeb7 feat: successfully joined team prompt 2021-10-24 14:46:32 +05:30
Andrew Bastin
3289ede0e8 refactor: use the unpaginated teamMembers field instead of members 2021-10-22 15:34:20 +05:30
liyasthomas
ddf21c17d2 feat: invite system error states and user prompts 2021-10-21 18:17:32 +05:30
Andrew Bastin
c9a24a0d28 fix: improper parsed error checks 2021-10-20 16:49:58 +05:30
Andrew Bastin
fa4b130b18 refactor: add error info to send invite results 2021-10-20 15:50:21 +05:30
Andrew Bastin
8561a7547f feat: team invitation caching 2021-10-20 15:33:20 +05:30
liyasthomas
e85f7b8232 feat: collapse collections/folders on clicking side border 2021-10-20 13:55:18 +05:30
Chun-Hao Lien
2f91d25ed4 chore: add json file extension (#1889) 2021-10-20 12:06:01 +05:30
Andrew Bastin
ae304b5af7 feat: update graphcache keys typings 2021-10-19 23:12:47 +05:30
hms5232
363d34b5e5 chore(i18n): update tw translations (#1887) 2021-10-19 14:48:50 +05:30
Andrew Bastin
1c51f8b32e fix: bug with search box key listeners still active after close 2021-10-19 14:42:39 +05:30
liyasthomas
aaff07bba2 Merge remote-tracking branch 'origin/keyboard-navigation-on-power-search' 2021-10-19 13:45:48 +05:30
Francisco Emanuel de Sales Pereira
4bc38d5e0f Feature: Adds arrow keys navigation on power search (#1888) 2021-10-19 13:44:14 +05:30
liyasthomas
fdfca00886 feat: set active on hover + minor ui improvements 2021-10-19 13:40:47 +05:30
Liyas Thomas
d872e393f8 Merge branch 'main' into keyboard-navigation-on-power-search 2021-10-19 10:56:00 +05:30
franciscoemanuel
686d8e5be7 feat: adds arrow keys navigation on powersearch 2021-10-19 02:20:30 -03:00
liyasthomas
4e30efd737 fix: invitation acceptance flow 2021-10-18 21:43:13 +05:30
rubjo
aa4935c505 chore(i18n): updated Norwegian translations (#1886) 2021-10-18 12:20:27 +05:30
liyasthomas
0e381ab850 feat: init loading states, minor ui improvements 2021-10-18 09:36:33 +05:30
liyasthomas
4a03ee4518 chore(deps): bump 2021-10-18 07:34:26 +05:30
Geert Wirken
be414d8279 Improve Dutch translations (#1880) 2021-10-17 22:43:18 +05:30
liyasthomas
c47b1f2413 chore(deps): bump 2021-10-17 19:42:53 +05:30
liyasthomas
ba9ee052a6 feat: minor ui improvements on invite flow 2021-10-17 18:53:24 +05:30
liyasthomas
b1c6708762 feat: init invitation wiring
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
2021-10-17 16:45:48 +05:30
Andrew Bastin
0ba31b6c79 feat: initial team invites implementation
Co-authored-by: Liyas Thomas <hi@liyasthomas.com>
2021-10-17 15:05:43 +05:30
liyasthomas
14f402f186 feat: minor ui improvements to power search 2021-10-17 11:37:06 +05:30
liyasthomas
b811e97ea5 feat: set hoppscotch graphql echo server as default 2021-10-17 00:49:36 +05:30
Oscar Dominguez
6d167ce1d6 ci(workflow): add cache to workflows using actions/setup-node (#1872) 2021-10-16 22:50:16 +05:30
liyasthomas
9ac1d23fd9 chore: minor fixes and improvements 2021-10-16 20:54:19 +05:30
Andrew Bastin
dea3a34e3d feat: team invitation mutations 2021-10-16 15:14:07 +05:30
Andrew Bastin
39de34f083 refactor: add graphcache codegen and update types 2021-10-16 15:14:07 +05:30
liyasthomas
02d5f0fdf3 feat: support updating user's display name 2021-10-16 15:01:32 +05:30
liyasthomas
0bb7cbe8d9 feat: compact mode for team component 2021-10-16 11:14:33 +05:30
liyasthomas
3e3b88b8c2 feat: native email input validation on login modal 2021-10-16 00:00:33 +05:30
liyasthomas
1438beb93b chore: clean up 2021-10-15 08:15:43 +05:30
liyasthomas
c1ec5dc60d feat: promote environment aware input to stable 2021-10-14 13:00:32 +05:30
prasanth
3f513f2f4d fix(request): multipart payload not working while using proxy 2021-10-13 21:32:27 +05:30
liyasthomas
d1b573f6f9 fix: init local persistence 2021-10-13 19:42:13 +05:30
liyasthomas
0e08abc46f feat: init theming in empty layout 2021-10-13 17:00:56 +05:30
liyasthomas
49bdf9f203 feat: init teams invite page 2021-10-13 16:12:02 +05:30
liyasthomas
b3e9df4f3d fix: introduce X-Frame-Options header to prevent clickjacking 2021-10-13 08:58:40 +05:30
Andrew Bastin
3b8cf4a60a fix: single character shortcuts not working 2021-10-12 21:43:06 +05:30
Akash Hamirwasia
b7ccb9a34c feat: disable inputs in realtime and graphql connections when active (#1870) 2021-10-12 21:19:29 +05:30
Akash Hamirwasia
b8ffa872c7 feat: add socket.io-client version selector menu (#1867) 2021-10-12 11:22:47 +05:30
liyasthomas
ef866f7851 feat: invite modal mvp 2021-10-11 06:51:43 +05:30
liyasthomas
f27515bf1d Merge branch 'refactor/backend' of https://github.com/hoppscotch/hoppscotch into refactor/backend 2021-10-10 20:49:27 +05:30
liyasthomas
ddf74c5d7c chore(deps): bump 2021-10-10 20:48:16 +05:30
liyasthomas
8ea12695b3 feat: teams invite modal 2021-10-10 20:44:21 +05:30
Andrew Bastin
d6324e6ba6 feat: gql codegen + caching + optimistic 2021-10-10 20:44:18 +05:30
Andrew Bastin
2325982801 refactor: extract cache info to separate files and add more mutations 2021-10-10 20:43:19 +05:30
liyasthomas
b103c45e65 chore(ui): minor ui improvements, empty teams state 2021-10-10 20:43:18 +05:30
Andrew Bastin
409989eddb feat: probable user is displayed as authenticated 2021-10-10 20:43:18 +05:30
liyasthomas
3f5fcae280 refactor: updated lint script 2021-10-10 20:43:18 +05:30
Andrew Bastin
d5123c793a feat: add persistent cache and optimistic updates 2021-10-10 20:43:16 +05:30
Andrew Bastin
e6707c1e4a fix: queries not waiting for authentication 2021-10-10 20:42:46 +05:30
liyasthomas
41be5cc4a8 feat: teams modal wrapper 2021-10-10 20:41:57 +05:30
liyasthomas
e82a4a1d23 refactor: remove EAInvite flag 2021-10-10 20:41:57 +05:30
Andrew Bastin
e30e4edfce fix: fix issue with team queries not updating correctly after mutation 2021-10-10 20:41:57 +05:30
Andrew Bastin
bd72ef7950 feat: team mutation 2021-10-10 20:41:57 +05:30
Andrew Bastin
539034df2a refactor: refactor create team modal to new system 2021-10-10 20:41:57 +05:30
liyasthomas
6da6afc5a1 fix: i18n usage 2021-10-10 20:41:57 +05:30
liyasthomas
fea523972d feat: init profile page 2021-10-10 20:41:57 +05:30
Andrew Bastin
5cfc6c2949 feat: introduce updated mutation system and team.vue rewrite 2021-10-10 20:41:57 +05:30
Andrew Bastin
6dd0c25d49 feat: initial setup of new backend comms 2021-10-10 20:41:54 +05:30
liyasthomas
ab9b3e47b9 chore(deps): bump 2021-10-10 20:24:31 +05:30
James George
33ebdf2831 chore: cleanup .husky directory (#1865) 2021-10-10 15:00:57 +05:30
liyasthomas
ef95939763 feat: teams invite modal 2021-10-09 16:10:51 +05:30
Rishabh kalra
0394deaeef route navigation no longer happening in typable areas (#1862) 2021-10-09 06:45:17 +05:30
Andrew Bastin
337a60c8a4 feat: gql codegen + caching + optimistic 2021-10-08 23:51:06 +05:30
Andrew Bastin
47b341d50e fix: fix conditional tabs not rerendering 2021-10-06 21:55:05 +05:30
Andrew Bastin
a5fd39adf8 refactor: extract cache info to separate files and add more mutations 2021-10-06 19:32:27 +05:30
liyasthomas
5772117dc8 fix: respect localePath on router - fixed #1859 2021-10-06 12:38:50 +05:30
liyasthomas
f73c8a45d9 chore(ui): minor ui improvements, empty teams state 2021-10-05 22:17:29 +05:30
Andrew Bastin
109d4190ae feat: probable user is displayed as authenticated 2021-10-05 22:06:19 +05:30
liyasthomas
317de82be6 refactor: updated lint script 2021-10-05 21:48:10 +05:30
Boris K
3604d69463 chore(i18n): fixed french translations (#1858) 2021-10-05 15:16:36 +05:30
Andrew Bastin
96cf774652 feat: add persistent cache and optimistic updates 2021-10-05 02:09:39 +05:30
liyasthomas
d2b39976ba chore(deps): bump 2021-10-04 08:16:40 +05:30
Stephane
06161bc963 Show graphql error message (#1852)
Co-authored-by: StephaneBischoffSSENSE <stephane.bischoff@ssense.com>
Co-authored-by: Liyas Thomas <liyascthomas@gmail.com>
2021-10-04 08:01:47 +05:30
bblumenwiese
2ab1d3dbfa docs: fix broken link to .env.example in README (#1854)
Refs #1852
2021-10-04 00:29:45 +05:30
Andrew Bastin
ecdc7919ae fix: queries not waiting for authentication 2021-10-03 19:14:32 +05:30
liyasthomas
a24541ac2b chore(deps): bump 2021-10-03 19:13:44 +05:30
liyasthomas
d832690548 refactor: better name for duplicated environment 2021-10-03 18:53:27 +05:30
Stephane
2844710ea8 feat: add ability to copy environments (#1848)
Co-authored-by: StephaneBischoffSSENSE <stephane.bischoff@ssense.com>
2021-10-03 18:38:36 +05:30
liyasthomas
84ad4071ad feat: teams modal wrapper 2021-10-03 18:14:52 +05:30
liyasthomas
7f501241f0 refactor: remove EAInvite flag 2021-10-03 15:49:04 +05:30
Manda Putra
5dcfa66c5d fix: parsing 2021-10-03 12:28:49 +05:30
Manda Putra
f428a21279 feat: parse body from url params 2021-10-03 12:28:49 +05:30
Andrew Bastin
ccdd4963cd fix: fix issue with team queries not updating correctly after mutation 2021-10-03 01:31:06 +05:30
Andrew Bastin
2092a3729c feat: team mutation 2021-10-02 13:50:08 +05:30
Andrew Bastin
a628420adb refactor: refactor create team modal to new system 2021-10-02 13:50:08 +05:30
liyasthomas
23de147ca1 fix: i18n usage 2021-10-02 13:46:51 +05:30
liyasthomas
93f55f5619 feat: init profile page 2021-10-02 13:22:07 +05:30
Liyas Thomas
b10933898f Merge pull request #1845 from YasiOnFire/fix-polish-localization
chore(i18n): fix polish translation
2021-10-02 07:34:24 +05:30
Yasio
1727b754d4 chore(i18n): fix polish translation 2021-10-01 20:36:12 +02:00
Andrew Bastin
ffb0c12c08 feat: introduce updated mutation system and team.vue rewrite 2021-10-01 15:13:48 +05:30
Andrew Bastin
c332808fe4 feat: initial setup of new backend comms 2021-10-01 13:46:22 +05:30
liyasthomas
8f0538c886 chore(i18n): updated translations 2021-10-01 11:24:34 +05:30
Khaleel Gibran
e6bb7e2ca9 Remove random </br>s in locale footers (#1844) 2021-10-01 10:12:10 +05:30
liyasthomas
2a012520d0 fix: proper type for ID 2021-09-30 16:05:40 +05:30
Andrew Bastin
c71333d9cb refactor: update ID based backend queries to proper types 2021-09-30 12:52:31 +05:30
liyasthomas
728515c225 chore: bump deps 2021-09-26 19:16:23 +05:30
Andrew Bastin
e0f88e01f9 Update js-sandbox README 2021-09-26 11:27:46 +05:30
Andrew Bastin
1aa94a12c0 Merge pull request #1836 from AndrewBastin/refactor/js-sandbox
Move to JS code execution sandbox (HOPP-67, HOPP-66)
2021-09-25 23:05:41 +05:30
Andrew Bastin
de2d3361a7 refactor: bump js-sandbox version to 1.0.0 2021-09-25 22:25:33 +05:30
Andrew Bastin
680937e50b feat: fix issue with the pre-request envs 2021-09-25 21:21:01 +05:30
liyasthomas
6751c50514 fix: build 2021-09-25 12:21:32 +05:30
liyasthomas
0c389701fe chore(docs): updated readme 2021-09-25 12:21:19 +05:30
Andrew Bastin
9454d8c100 feat: move testing code to js-sandbox 2021-09-25 01:09:48 +05:30
Andrew Bastin
166f9e817b feat: project level testing + add fp-ts and sandbox as deps 2021-09-25 01:07:24 +05:30
Andrew Bastin
d2865c637c refactor: bring js-sandbox project to the monorepo 2021-09-25 01:01:22 +05:30
liyasthomas
9698932bde refactor(ui): minor ui improvements 2021-09-20 21:33:36 +05:30
liyasthomas
44026fcd41 fix: show scrollbars inside menu 2021-09-20 14:56:25 +05:30
liyasthomas
d938af0c2c fix: better responsiveness on horizontal layout 2021-09-20 14:18:40 +05:30
liyasthomas
fd658400a6 refactor: updated logo 2021-09-20 11:15:09 +05:30
liyasthomas
dcbb17b164 feat: vertical and horizontal layout support 2021-09-20 10:17:31 +05:30
liyasthomas
49741875bd chore: minor ui improvements 2021-09-19 20:27:20 +05:30
liyasthomas
0fcd9733ff chore: bump deps 2021-09-19 20:26:25 +05:30
Andrew Bastin
62d50169d7 Merge pull request #1832 from AndrewBastin/refactor/teams-list
fix: teams list not properly showing (HOPP-61)
2021-09-19 00:48:30 +05:30
Andrew Bastin
1d3d5a1e6a Merge branch 'main' into refactor/teams-list 2021-09-19 00:47:58 +05:30
Liyas Thomas
d309fa745e Update packages/hoppscotch-app/components/teams/index.vue 2021-09-19 00:33:21 +05:30
liyasthomas
f7031992d5 fix: enforce name for SmartTab component 2021-09-19 00:09:35 +05:30
Andrew Bastin
fcdf68ea15 fix: teams list not properly showing 2021-09-18 23:56:19 +05:30
liyasthomas
b0a6692179 feat: vertical tabs for right sidebars 2021-09-18 23:50:42 +05:30
liyasthomas
e1e763575d perf: template literals 2021-09-18 16:09:58 +05:30
Andrew Bastin
4236d1179c fix: save request crashing on teams 2021-09-18 15:11:54 +05:30
Andrew Bastin
09365bcabe fix: environments not being written correctly 2021-09-18 14:39:43 +05:30
Andrew Bastin
be29ddcbd6 fix: volar shim gitignore issue 2021-09-17 18:49:38 +05:30
liyasthomas
522194ca8d chore: remove absolute files 2021-09-17 17:30:55 +05:30
liyasthomas
5af8f584f6 chore(deps): bump 2021-09-17 15:25:52 +05:30
liyasthomas
adc08f8865 perf(ci): remove absolute branch hooks 2021-09-17 15:20:30 +05:30
liyasthomas
0f39d54c3c fix: netlify builds 2021-09-17 14:17:48 +05:30
liyasthomas
9e6659e842 fix: netlify builds 2021-09-17 14:11:30 +05:30
liyasthomas
46a0f6e3f8 refactor: update build docs 2021-09-17 13:57:56 +05:30
Andrew Bastin
e90b26ebed fix: standardized build scripts 2021-09-17 13:46:23 +05:30
Andrew Bastin
4407f260ae fix: updated to better dockerfile 2021-09-17 13:12:49 +05:30
Andrew Bastin
d4392416c8 fix: fix broken tests 2021-09-16 22:59:29 +05:30
liyasthomas
2d3cbd26b8 fix: ci builds 2021-09-16 22:44:34 +05:30
Andrew Bastin
98b9660956 refactor: merge branch 'main' into refactor/monorepo 2021-09-16 22:24:21 +05:30
liyasthomas
4e8a4e8914 refactor: typescript support 2021-09-15 17:30:04 +05:30
liyasthomas
96bcbc80f8 fix(ci): bump deps + fix tests 2021-09-14 23:43:20 +05:30
liyasthomas
1dfc8e2973 perf: remove absolute files 2021-09-14 23:32:43 +05:30
liyasthomas
311886f6c9 Merge remote-tracking branch 'origin/feat/codemirror' 2021-09-14 23:30:04 +05:30
liyasthomas
4a332f40e5 fix: json outline line index 2021-09-14 23:28:39 +05:30
liyasthomas
93a97a2f4c refactor: json outline ui 2021-09-14 23:11:10 +05:30
Andrew Bastin
1dee098ca2 feat: fix outline 2021-09-14 21:33:13 +05:30
liyasthomas
a07cc7e560 refactor: minor ui improvements 2021-09-14 13:19:10 +05:30
Andrew Bastin
c26f7f5ebc fix: autocompletion eating non-identifier tokens 2021-09-13 09:18:37 +05:30
liyasthomas
5d801cf566 fix: missing '?' in query parameter string for code generators 2021-09-13 09:07:06 +05:30
Andrew Bastin
631b2d869e fix: autocompletion position messing up 2021-09-12 21:56:56 +05:30
liyasthomas
c02f54cc18 chore(deps): bump 2021-09-12 19:45:23 +05:30
liyasthomas
827a95515d feat: select suggestion on enter key for autocomplete 2021-09-12 10:51:46 +05:30
liyasthomas
9082152f1a fix: editor width 2021-09-12 02:08:37 +05:30
Liyas Thomas
0efbddeda4 Merge pull request #1822 from hoppscotch/fix/gql-save-req 2021-09-12 00:07:22 +05:30
liyasthomas
b2e186957c fix: repetitive use of () in setup script 2021-09-12 00:02:07 +05:30
Andrew Bastin
d855e5cffb fix: gql request save issue 2021-09-11 23:43:35 +05:30
Andrew Bastin
f3747edaa3 Merge pull request #1820 from hoppscotch/fix/gql-headers
fix: gql headers not passed properly (HOPP-55)
2021-09-11 21:15:17 +05:30
Andrew Bastin
752932ef3d fix: gql headers not passed properly 2021-09-11 21:01:48 +05:30
liyasthomas
948cf9dae3 perf: cleanup 2021-09-11 10:34:06 +05:30
liyasthomas
b2f93aa549 feat: highlight active line in codemirror when focused 2021-09-11 09:19:16 +05:30
liyasthomas
108f228edf refactor: minor ui improvements 2021-09-11 08:26:54 +05:30
liyasthomas
fe6030140f feat: graphql mode for schema editor 2021-09-10 19:57:52 +05:30
Andrew Bastin
003400cfa8 feat: introduce graphql mode for codemirror 2021-09-10 19:01:35 +05:30
liyasthomas
41a02f059d perf: remove ace editor 2021-09-10 18:52:58 +05:30
liyasthomas
b4ed6fd107 feat: codemirror for import curl and codegens 2021-09-10 18:27:31 +05:30
Andrew Bastin
36246da9e1 feat: fix up jest tests 2021-09-10 17:50:22 +05:30
liyasthomas
457b6b982c feat: codemirror for graphql query, scheme and response 2021-09-10 16:12:04 +05:30
liyasthomas
05a07dc4a1 perf: ci 2021-09-10 13:05:31 +05:30
Andrew Bastin
85889c2cb9 fix: fix tests.yml 2021-09-10 12:55:59 +05:30
liyasthomas
be6ceaab04 perf: ci 2021-09-10 12:46:50 +05:30
liyasthomas
f1b18688bb fix: ci 2021-09-10 12:15:08 +05:30
liyasthomas
80c7decb81 feat: husky + commitlint 2021-09-10 12:02:18 +05:30
liyasthomas
3ef5a1e21a feat: codemirror editor for pre-request and test scripts 2021-09-10 11:12:08 +05:30
liyasthomas
2eb0a4c754 feat: reactive syntax + line wrap on raw body 2021-09-10 10:46:58 +05:30
liyasthomas
10f5af5dda feat: codemirror editot for raw body 2021-09-10 01:35:46 +05:30
liyasthomas
8b27ebb96b feat: mixed html syntax, light theme for codemiror 2021-09-10 00:44:14 +05:30
Andrew Bastin
b28f82a881 refactor: monorepo+pnpm (removed husky) 2021-09-10 00:28:28 +05:30
liyasthomas
c921606f3f Merge remote-tracking branch 'origin/exp/volar-types' into feat/codemirror 2021-09-09 20:50:58 +05:30
liyasthomas
c6c08f6c60 feat: reactive line wrap on codemirror
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
2021-09-09 20:50:04 +05:30
liyasthomas
02cf620090 feat: port ace editor to codemirror
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
2021-09-09 17:47:27 +05:30
liyasthomas
917550ff4d feat: tooltips on primary navigation in zenmode 2021-09-09 13:10:48 +05:30
Andrew Bastin
4a12cc76fa feat: improve shim generation for index components 2021-09-09 01:56:43 +05:30
Andrew Bastin
f4f74e223f refactor: a volar types shim generator 2021-09-09 01:03:46 +05:30
liyasthomas
8b4535c131 Merge branch 'feat/codemirror' of https://github.com/hoppscotch/hoppscotch into feat/codemirror 2021-09-08 21:54:00 +05:30
liyasthomas
b15fd6c75a feat: port bulk editor textareas to codemirror 2021-09-08 21:52:26 +05:30
liyasthomas
e1a25fa894 fix: broken conditional rendering of codemirror
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
2021-09-08 21:41:02 +05:30
Andrew Bastin
2bb3b71a70 refactor: map ctrl-space to autocomplete by default in codemirror 2021-09-08 21:41:02 +05:30
Andrew Bastin
4c55b9c304 fix: codemirror theme not changing when color mode is updated 2021-09-08 21:41:02 +05:30
Andrew Bastin
639a629809 feat: gql query autocompletion on codemirror 2021-09-08 21:41:02 +05:30
Andrew Bastin
d6e3bd09b4 refactor: pass current token position to auto completers on codemirror 2021-09-08 21:41:02 +05:30
Andrew Bastin
8d67a0d95f feat: test script auto completion for codemirror 2021-09-08 21:41:02 +05:30
Andrew Bastin
b9fc0175e7 feat: implement base autocomplete implementation for codemirror along with preRequest autocompletion 2021-09-08 21:41:02 +05:30
liyasthomas
dc5f52cc0d refactor: github flavored codemirror light theme 2021-09-08 21:40:55 +05:30
liyasthomas
602aabdeb8 feat: reactive codemirror theme 2021-09-08 21:40:11 +05:30
Andrew Bastin
2f8aa79ec1 feat: json linter support for codemirror 2021-09-08 21:40:11 +05:30
Andrew Bastin
8af90432cf feat: implement gql query linting in codemirror 2021-09-08 21:40:11 +05:30
Andrew Bastin
61da0733c2 feat: codemirror editor options are reactive 2021-09-08 21:40:11 +05:30
liyasthomas
33951482d5 feat: placeholder, auto-close brackets, search, line wrap 2021-09-08 21:40:11 +05:30
Andrew Bastin
4e8484ee7c feat: linter for prerequest and testscripts 2021-09-08 21:40:11 +05:30
Andrew Bastin
071761a61e feat: codemirror linting system 2021-09-08 21:40:11 +05:30
Andrew Bastin
10a11d6725 refactor: add types for esprima 2021-09-08 21:40:11 +05:30
Andrew Bastin
c81178ae26 refactor: extract common codemirror logic out to composable 2021-09-08 21:40:10 +05:30
liyasthomas
2bafae5397 feat: line wrap, auto close brackets, placeholder on codemirror 2021-09-08 21:40:10 +05:30
liyasthomas
6a1d201e0e refactor: ts codemirror 2021-09-08 21:40:10 +05:30
liyasthomas
8de544696d feat: init codemirror 2021-09-08 21:40:06 +05:30
liyasthomas
66c489da8f fix: broken conditional rendering of codemirror
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
2021-09-08 20:27:36 +05:30
Andrew Bastin
26c8f35688 refactor: map ctrl-space to autocomplete by default in codemirror 2021-09-08 19:51:43 +05:30
liyasthomas
c3e881ed77 fix: tooltip position on containers with overflow 2021-09-08 15:22:38 +05:30
liyasthomas
2cf55cbb96 refactor: hide right sidebars by default on mobile devices 2021-09-08 13:56:02 +05:30
liyasthomas
73f22abf56 feat: mobile responsive right sidbars + composable window size 2021-09-08 12:27:46 +05:30
liyasthomas
dbae90a193 refactor: replace lunr with fuse 2021-09-08 09:21:22 +05:30
Andrew Bastin
28aeac4533 fix: codemirror theme not changing when color mode is updated 2021-09-08 06:00:23 +05:30
Andrew Bastin
162b3d6192 feat: gql query autocompletion on codemirror 2021-09-08 05:38:03 +05:30
Andrew Bastin
b016d3fd9d refactor: pass current token position to auto completers on codemirror 2021-09-08 05:36:46 +05:30
Andrew Bastin
f64ff58dbc feat: test script auto completion for codemirror 2021-09-08 04:58:30 +05:30
Andrew Bastin
d4d3d96bbb feat: implement base autocomplete implementation for codemirror along with preRequest autocompletion 2021-09-07 22:15:47 +05:30
liyasthomas
a5197ee544 refactor: github flavored codemirror light theme 2021-09-07 16:26:26 +05:30
liyasthomas
8a5fd4f745 feat: reactive codemirror theme 2021-09-07 12:14:13 +05:30
Andrew Bastin
12cd7940c6 feat: json linter support for codemirror 2021-09-06 23:47:26 +05:30
Andrew Bastin
0c2cec46a7 feat: implement gql query linting in codemirror 2021-09-06 23:30:43 +05:30
liyasthomas
84457ddc86 chore(deps+i18n): updated deps and translations 2021-09-06 20:32:45 +05:30
Liyas Thomas
d6d20e5d49 Merge pull request #1815 from bokub/patch-1
chore(i18n): fixed french translations
2021-09-06 20:26:20 +05:30
Boris K
9acdca1059 chore: french i18n translations 2021-09-06 16:48:10 +02:00
liyasthomas
66c4fd4d2f chore(i18n): updated translations 2021-09-05 20:51:54 +05:30
liyasthomas
f2defb3a31 chore(deps): bump 2021-09-05 20:51:25 +05:30
Andrew Bastin
aa66c10608 fix: improved volar integration 2021-09-03 23:06:23 +05:30
liyasthomas
28d20a9c61 refactor: updated svg icon assets 2021-09-03 11:27:11 +05:30
liyasthomas
94fcc0a6a9 perf: workflows 2021-09-02 20:33:34 +05:30
Andrew Bastin
91abdd5415 Merge pull request #1802 from AndrewBastin/bugfix/1798
fix: auth header doubling in codegen
2021-09-02 20:19:15 +05:30
Andrew Bastin
58e940d193 fix: auth header doubling in codegen 2021-09-02 20:04:56 +05:30
liyasthomas
6991dd48f3 feat: publish docker image 2021-09-02 17:44:36 +05:30
liyasthomas
270a077539 chore: updated social banner image 2021-09-02 09:04:02 +05:30
liyasthomas
ff326acfc8 chore: updated screenshots 2021-09-02 01:46:08 +05:30
liyasthomas
af1446233c chore: i18n translations 2021-09-02 00:28:38 +05:30
liyasthomas
67d66e2b2f Merge remote-tracking branch 'origin/i18n-de' 2021-09-01 23:19:56 +05:30
Liyas Thomas
753ce5bd6e Merge pull request #1800 from JorgeFerrer/patch-1
Improvements for the translation to Spanish #1799
2021-09-01 23:18:37 +05:30
Jorge Ferrer
e7d71ef301 Improvements for the translation to Spanish #1799
I have fixed REST as well as some wrong automatic translations and also applied consistency in terms used across all translations
2021-09-01 19:35:15 +02:00
Andrew Bastin
8430921e4e feat: codemirror editor options are reactive 2021-09-01 20:32:33 +05:30
liyasthomas
c938abf606 feat: placeholder, auto-close brackets, search, line wrap 2021-09-01 17:33:54 +05:30
Andrew Bastin
3addfe8d4b feat: linter for prerequest and testscripts 2021-09-01 16:45:49 +05:30
Andrew Bastin
5276556837 feat: codemirror linting system 2021-09-01 16:41:14 +05:30
Andrew Bastin
e47ad94666 refactor: add types for esprima 2021-09-01 16:34:02 +05:30
liyasthomas
7065763c7c Merge branch 'main' into feat/codemirror 2021-09-01 10:34:52 +05:30
Andrew Bastin
86489d95c2 refactor: extract common codemirror logic out to composable 2021-08-31 22:20:42 +05:30
liyasthomas
f357c4f171 fix: response section's font weight 2021-08-31 22:16:49 +05:30
Liyas Thomas
36e34fe667 Merge pull request #1796 from hoppscotch/feat/bulk-edit
feat: bulk edit
2021-08-31 22:03:39 +05:30
liyasthomas
dcc59f42fa fix: remove // from key on disabled bulk edit entries 2021-08-31 21:54:24 +05:30
Liyas Thomas
f6fbff2b42 Merge pull request #1797 from jenskeiner/jenskeiner-fix-mistranslations-and-typos
Fix some mistranslations an typos.
2021-08-31 18:23:14 +05:30
Jens Keiner
67ce20ef62 Fix some mistranslations an typos.
Fixed up some apparent mistranslated items and typos.
2021-08-31 14:37:19 +02:00
liyasthomas
788e0dc851 chore: lint 2021-08-31 17:38:54 +05:30
liyasthomas
e2b1c83698 feat: line wrap, auto close brackets, placeholder on codemirror 2021-08-31 16:10:35 +05:30
liyasthomas
15373be63e refactor: ts codemirror 2021-08-31 10:47:00 +05:30
liyasthomas
8c9cd079b7 feat: init codemirror 2021-08-31 00:03:07 +05:30
liyasthomas
6f67a97ade feat: textare autoresize 2021-08-30 22:03:59 +05:30
liyasthomas
ada568cb75 feat: bulk edit for graphql headers 2021-08-30 18:49:35 +05:30
liyasthomas
b9fa254ab5 feat: disable bulk edit entries with // 2021-08-30 18:35:33 +05:30
liyasthomas
174ba90fb5 feat: bulk edit transformation 2021-08-30 14:34:43 +05:30
liyasthomas
de8c7c1ca3 Merge branch 'main' into feat/bulk-edit 2021-08-30 13:23:24 +05:30
liyasthomas
407a125533 feat: search
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
2021-08-30 11:27:36 +05:30
liyasthomas
d8881ba6a3 feat: init bulk edit 2021-08-29 23:26:27 +05:30
liyasthomas
755540fb81 Merge branch 'main' into feat/search 2021-08-29 20:59:47 +05:30
liyasthomas
add358c752 chore(deps): bump 2021-08-29 20:53:59 +05:30
liyasthomas
91352ade20 feat: typescript support in auth components
Co-authored-by: Andrew Bastin <andrewbastin.k@gmail.com>
2021-08-29 19:31:23 +05:30
Liyas Thomas
39fbf4ef33 Merge pull request #1790 from AndrewBastin/refactor/firebase-v9
refactor: bump firebase to v9
2021-08-29 18:28:33 +05:30
liyasthomas
d9547c6654 fix: mobile responsiveness 2021-08-29 17:11:37 +05:30
liyasthomas
04f9428267 feat: disable autocomplete on inputs + keyboard navigation focus 2021-08-29 16:44:18 +05:30
liyasthomas
ebd8f43219 refactor: minor ui improvements 2021-08-29 13:25:01 +05:30
liyasthomas
f75b2e26a3 chore: cleanup, typescript 2021-08-29 12:00:19 +05:30
Andrew Bastin
2e654c143f refactor: bump firebase to v9 2021-08-29 10:51:31 +05:30
liyasthomas
5bcee265a6 fix: show empty state for search results, es6 2021-08-29 10:02:19 +05:30
liyasthomas
647599e5aa refactor: full-width modal for search, transparent tabs 2021-08-29 09:44:46 +05:30
liyasthomas
16b9a2b06e feat: search 2021-08-28 23:23:16 +05:30
liyasthomas
7da427c669 feat: share and support modals 2021-08-28 20:48:13 +05:30
liyasthomas
405e6c1e4e chore: minor fixes and improvements 2021-08-28 10:57:21 +05:30
liyasthomas
efbc21826b Merge branch 'refactor/ui' 2021-08-28 05:56:58 +05:30
liyasthomas
7a1f1c9df7 feat: updated screenshots 2021-08-28 05:56:07 +05:30
liyasthomas
476bfbaef0 feat: svg icons 2021-08-28 05:47:33 +05:30
639 changed files with 40205 additions and 73400 deletions

2
.github/FUNDING.yml vendored
View File

@@ -1,4 +1,2 @@
github: hoppscotch
open_collective: hoppscotch
patreon: liyasthomas
custom: https://www.paypal.me/liyascthomas

View File

@@ -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

View File

@@ -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
View 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 }}

View File

@@ -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
View File

@@ -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
View File

@@ -1 +0,0 @@
_

View File

@@ -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.

View File

@@ -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"]

View File

@@ -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**

View File

@@ -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.**

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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 youll 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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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": [
{

View File

@@ -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,
}
}

View File

@@ -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"
}

View File

@@ -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
}

View File

@@ -1,8 +0,0 @@
const rawLens = {
lensName: "response.raw",
isSupportedContentType: () => true,
renderer: "raw",
rendererImport: () => import("~/components/lenses/renderers/RawLensRenderer"),
}
export default rawLens

View File

@@ -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,
}
}

View File

@@ -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,
}
}

View File

@@ -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
}

View File

@@ -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 ""
}

View File

@@ -1,3 +0,0 @@
<template>
<Nuxt />
</template>

View File

@@ -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": "网址"
}
}

View File

@@ -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": "網址"
}
}

View File

@@ -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

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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"
}
}

View File

@@ -0,0 +1,4 @@
/node_modules
package-lock.json
/dist
/src/*.d.ts

View File

@@ -0,0 +1,5 @@
/src
/test
/node_modules
rollup.config.js
tsconfig.json

View File

@@ -0,0 +1 @@
A [CodeMirror 6](https://codemirror.net/6) language plugin for GraphQL

View 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"
}

View 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()]
}

View 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)
}

View 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

View File

@@ -0,0 +1,12 @@
{
"compilerOptions": {
"strict": true,
"target": "es6",
"module": "es2020",
"newLine": "lf",
"declaration": true,
"moduleResolution": "node",
"allowJs": true,
},
"include": ["src/*"]
}

View File

@@ -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
View 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

View File

@@ -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,
},
}

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

Some files were not shown because too many files have changed in this diff Show More