@@ -1,6 +0,0 @@
|
||||
[Dolphin]
|
||||
Timestamp=2019,8,23,13,58,13
|
||||
Version=4
|
||||
|
||||
[Settings]
|
||||
HiddenFilesShown=true
|
||||
104
.dockerignore
Normal file
@@ -0,0 +1,104 @@
|
||||
Dockerfile
|
||||
.vscode
|
||||
.github
|
||||
|
||||
# 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 (http://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
|
||||
|
||||
# Postwoman build data
|
||||
.postwoman
|
||||
|
||||
# File explorer
|
||||
.directory
|
||||
@@ -4,7 +4,7 @@ root = true
|
||||
|
||||
[*]
|
||||
indent_size = 2
|
||||
indent_style = tab
|
||||
indent_style = space
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
|
||||
14
.firebaserc
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"projects": {
|
||||
"default": "postwoman-api"
|
||||
},
|
||||
"targets": {
|
||||
"postwoman-api": {
|
||||
"hosting": {
|
||||
"postwoman": [
|
||||
"postwoman"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
7
.gitignore
vendored
@@ -1,4 +1,8 @@
|
||||
# Created by .ignore support plugin (hsz.mobi)
|
||||
|
||||
# Firebase
|
||||
.firebase
|
||||
|
||||
### Node template
|
||||
# Logs
|
||||
logs
|
||||
@@ -91,3 +95,6 @@ sw.*
|
||||
|
||||
# Postwoman build data
|
||||
.postwoman
|
||||
|
||||
# File explorer
|
||||
.directory
|
||||
|
||||
29
.travis.yml
@@ -15,30 +15,39 @@ language: node_js
|
||||
node_js:
|
||||
- "12"
|
||||
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libgconf-2-4 # cypress binary dependency
|
||||
|
||||
env:
|
||||
- DEPLOY_ENV=GH_PAGES
|
||||
- DEPLOY_ENV=POSTWOMAN_IO
|
||||
|
||||
cache:
|
||||
npm: true
|
||||
directories:
|
||||
- "node_modules"
|
||||
- ~/.cache
|
||||
|
||||
branches:
|
||||
only:
|
||||
- "master"
|
||||
|
||||
install:
|
||||
- "npm install firebase-tools"
|
||||
- "npm install"
|
||||
|
||||
before_script:
|
||||
- "npm run test"
|
||||
|
||||
script:
|
||||
- "cd functions"
|
||||
- "npm install"
|
||||
- "cd .."
|
||||
- "npm run generate"
|
||||
|
||||
notifications:
|
||||
webhooks: https://www.travisbuddy.com
|
||||
|
||||
deploy:
|
||||
provider: pages
|
||||
skip-cleanup: true
|
||||
# Refer to: https://docs.travis-ci.com/user/deployment/pages/#Setting-the-GitHub-token
|
||||
github-token: $GITHUB_ACCESS_TOKEN
|
||||
target-branch: gh-pages
|
||||
local-dir: dist
|
||||
on:
|
||||
branch: master
|
||||
after_success:
|
||||
- firebase deploy --token $FIREBASE_TOKEN
|
||||
|
||||
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"deepcode.review.results.hideInformationIssues": false
|
||||
}
|
||||
37
CHANGELOG.md
@@ -1,38 +1,3 @@
|
||||
# Changelog
|
||||
# Title
|
||||
### Description by [Liyas Thomas](https://github.com/liyasthomas)
|
||||
|
||||
---
|
||||
|
||||
# 1.0.0
|
||||
|
||||
## This is it, Title 1.0.0!
|
||||
We are finally out of beta, therefore many bugs were fixed and camera received a brand new look.
|
||||
|
||||
* **NEW**: Camera redesign
|
||||
* **NEW**: Camera redesign
|
||||
* **NEW**: macOS and iOS support
|
||||
* **IMPROVEMENT**: Major improvements
|
||||
* **IMPROVEMENT**: Updated libraries
|
||||
* **FIX**: Fixed many bugs and crashes
|
||||
* **FIX**: Graphic glitches
|
||||
* **FIX**: Statusbar too dark
|
||||
* **TRANSLATION**: Updated translations
|
||||
* **REVERT**: Brought back the "Help" button
|
||||
* **OTHER**: Removed all analytics
|
||||
|
||||
---
|
||||
|
||||
# 0.9.0
|
||||
|
||||
## I worked a lot on Web apps, WebAR, WebGL & PWAs
|
||||
So I think Lvr is now ready to be released :)
|
||||
|
||||
I will keep the usual branch model.
|
||||
|
||||
* Stable release on `master` branch
|
||||
|
||||
---
|
||||
|
||||
## Thanks
|
||||
* [Google](https://www.google.com) - for [Polymer](https://polymer-project.org)
|
||||
* [v0.1.0](https://github.com/liyasthomas/postwoman/releases/tag/v0.1.0) - Initial 🎉 Initial public release
|
||||
226
CONTRIBUTING.md
@@ -1,180 +1,92 @@
|
||||
# Introduction
|
||||
# Contributing
|
||||
|
||||
### Write something nice here!
|
||||
When contributing to this repository, please first discuss the change you wish to make via issue,
|
||||
email, or any other method with the owners of this repository before making a change.
|
||||
|
||||
>First off, thank you for considering contributing to Active Admin. It's people like you that make Active Admin such a great tool.
|
||||
Please note we have a code of conduct, please follow it in all your interactions with the project.
|
||||
|
||||
[source: [Active Admin](https://github.com/activeadmin/activeadmin/blob/master/CONTRIBUTING.md)] **Need more inspiration?** [1] [Read The Docs](http://read-the-docs.readthedocs.org/en/latest/contribute.html) [2] [Mustache.js](https://github.com/janl/mustache.js/#contributing)
|
||||
## Pull Request Process
|
||||
|
||||
### Tell them why they should read your guidelines.
|
||||
1. Ensure any install or build dependencies are removed before the end of the layer when doing a
|
||||
build.
|
||||
2. Update the README.md with details of changes to the interface, this includes new environment
|
||||
variables, exposed ports, useful file locations and container parameters.
|
||||
3. Increase the version numbers in any examples files and the README.md to the new version that this
|
||||
Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/).
|
||||
4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you
|
||||
do not have permission to do that, you may request the second reviewer to merge it for you.
|
||||
|
||||
>Following these guidelines helps to communicate that you respect the time of the developers managing and developing this open source project. In return, they should reciprocate that respect in addressing your issue, assessing changes, and helping you finalize your pull requests.
|
||||
## Code of Conduct
|
||||
|
||||
[source: [Hoodie](https://github.com/hoodiehq/hoodie/blob/master/CONTRIBUTING.md)]
|
||||
### Our Pledge
|
||||
|
||||
### Explain what kinds of contributions you are looking for.
|
||||
In the interest of fostering an open and welcoming environment, we as
|
||||
contributors and maintainers pledge to making participation in our project and
|
||||
our community a harassment-free experience for everyone, regardless of age, body
|
||||
size, disability, ethnicity, gender identity and expression, level of experience,
|
||||
nationality, personal appearance, race, religion, or sexual identity and
|
||||
orientation.
|
||||
|
||||
Keep an open mind! Improving documentation, bug triaging, or writing tutorials are all examples of helpful contributions that mean less work for you.
|
||||
### Our Standards
|
||||
|
||||
> Elasticsearch is an open source project and we love to receive contributions from our community — you! There are many ways to contribute, from writing tutorials or blog posts, improving the documentation, submitting bug reports and feature requests or writing code which can be incorporated into Elasticsearch itself.
|
||||
Examples of behavior that contributes to creating a positive environment
|
||||
include:
|
||||
|
||||
[source: [Elasticsearch](https://github.com/elastic/elasticsearch/blob/master/CONTRIBUTING.md)] **Need more inspiration?** [1] [Devise](https://github.com/plataformatec/devise/wiki/Contributing) [2] [Geocoder](https://github.com/alexreisner/geocoder#known-issue) (“known issue”)
|
||||
* Using welcoming and inclusive language
|
||||
* Being respectful of differing viewpoints and experiences
|
||||
* Gracefully accepting constructive criticism
|
||||
* Focusing on what is best for the community
|
||||
* Showing empathy towards other community members
|
||||
|
||||
### Explain contributions you are NOT looking for (if any).
|
||||
Examples of unacceptable behavior by participants include:
|
||||
|
||||
Again, defining this up front means less work for you. If someone ignores your guide and submits something you don’t want, you can simply close it and point to your policy.
|
||||
* The use of sexualized language or imagery and unwelcome sexual attention or
|
||||
advances
|
||||
* Trolling, insulting/derogatory comments, and personal or political attacks
|
||||
* Public or private harassment
|
||||
* Publishing others' private information, such as a physical or electronic
|
||||
address, without explicit permission
|
||||
* Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
> Please, don't use the issue tracker for [support questions]. Check whether the #pocoo IRC channel on Freenode can help with your issue. If your problem is not strictly Werkzeug or Flask specific, #python is generally more active. Stack Overflow is also worth considering.
|
||||
### Our Responsibilities
|
||||
|
||||
[source: [Flask](https://github.com/pallets/flask/blob/master/CONTRIBUTING.rst)] **Need more inspiration?** [1] [cucumber-ruby](https://github.com/cucumber/cucumber-ruby/blob/master/CONTRIBUTING.md#about-to-create-a-new-github-issue) [2] [Read the Docs](http://read-the-docs.readthedocs.org/en/latest/open-source-philosophy.html#unsupported)
|
||||
Project maintainers are responsible for clarifying the standards of acceptable
|
||||
behavior and are expected to take appropriate and fair corrective action in
|
||||
response to any instances of unacceptable behavior.
|
||||
|
||||
# Ground Rules
|
||||
### Set expectations for behavior (yours, and theirs).
|
||||
This includes not just how to communicate with others (being respectful, considerate, etc) but also technical responsibilities (importance of testing, project dependencies, etc). Mention and link to your code of conduct, if you have one.
|
||||
Project maintainers have the right and responsibility to remove, edit, or
|
||||
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||
permanently any contributor for other behaviors that they deem inappropriate,
|
||||
threatening, offensive, or harmful.
|
||||
|
||||
> Responsibilities
|
||||
> * Ensure cross-platform compatibility for every change that's accepted. Windows, Mac, Debian & Ubuntu Linux.
|
||||
> * Ensure that code that goes into core meets all requirements in this checklist: https://gist.github.com/audreyr/4feef90445b9680475f2
|
||||
> * Create issues for any major changes and enhancements that you wish to make. Discuss things transparently and get community feedback.
|
||||
> * Don't add any classes to the codebase unless absolutely needed. Err on the side of using functions.
|
||||
> * Keep feature versions as small as possible, preferably one new feature per version.
|
||||
> * Be welcoming to newcomers and encourage diverse new contributors from all backgrounds. See the [Python Community Code of Conduct](https://www.python.org/psf/codeofconduct/).
|
||||
### Scope
|
||||
|
||||
[source: [cookiecutter](https://github.com/audreyr/cookiecutter/blob/master/CONTRIBUTING.rst)] **Need more inspiration?** [1] [Celery](https://github.com/celery/celery/blob/master/CONTRIBUTING.rst#community-code-of-conduct) [2] [geocoder](https://github.com/alexreisner/geocoder#contributing)
|
||||
This Code of Conduct applies both within project spaces and in public spaces
|
||||
when an individual is representing the project or its community. Examples of
|
||||
representing a project or community include using an official project e-mail
|
||||
address, posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event. Representation of a project may be
|
||||
further defined and clarified by project maintainers.
|
||||
|
||||
# Your First Contribution
|
||||
Help people who are new to your project understand where they can be most helpful. This is also a good time to let people know if you follow a label convention for flagging beginner issues.
|
||||
### Enforcement
|
||||
|
||||
> Unsure where to begin contributing to Atom? You can start by looking through these beginner and help-wanted issues:
|
||||
> Beginner issues - issues which should only require a few lines of code, and a test or two.
|
||||
> Help wanted issues - issues which should be a bit more involved than beginner issues.
|
||||
> Both issue lists are sorted by total number of comments. While not perfect, number of comments is a reasonable proxy for impact a given change will have.
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported by contacting the project team at [INSERT EMAIL ADDRESS]. All
|
||||
complaints will be reviewed and investigated and will result in a response that
|
||||
is deemed necessary and appropriate to the circumstances. The project team is
|
||||
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||
Further details of specific enforcement policies may be posted separately.
|
||||
|
||||
[source: [Atom](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#your-first-code-contribution)] **Need more inspiration?** [1] [Read the Docs](http://docs.readthedocs.org/en/latest/contribute.html#contributing-to-development) [2] [Django](https://docs.djangoproject.com/en/dev/internals/contributing/new-contributors/#first-steps) (scroll down to "Guidelines" as well)
|
||||
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||
faith may face temporary or permanent repercussions as determined by other
|
||||
members of the project's leadership.
|
||||
|
||||
### Bonus points: Add a link to a resource for people who have never contributed to open source before.
|
||||
Here are a couple of friendly tutorials you can include: http://makeapullrequest.com/ and http://www.firsttimersonly.com/
|
||||
### Attribution
|
||||
|
||||
> Working on your first Pull Request? You can learn how from this *free* series, [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github).
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||
available at [http://contributor-covenant.org/version/1/4][version]
|
||||
|
||||
[source: [React](https://github.com/facebook/react/blob/master/CONTRIBUTING.md#pull-requests)]
|
||||
|
||||
As a side note, it helps to use newcomer-friendly language throughout the rest of your document. Here are a couple of examples from [Active Admin](https://github.com/activeadmin/activeadmin/blob/master/CONTRIBUTING.md):
|
||||
|
||||
>At this point, you're ready to make your changes! Feel free to ask for help; everyone is a beginner at first :smile_cat:
|
||||
>
|
||||
>If a maintainer asks you to "rebase" your PR, they're saying that a lot of code has changed, and that you need to update your branch so it's easier to merge.
|
||||
|
||||
# Getting started
|
||||
### Give them a quick walkthrough of how to submit a contribution.
|
||||
How you write this is up to you, but some things you may want to include:
|
||||
|
||||
* Let them know if they need to sign a CLA, agree to a DCO, or get any other legal stuff out of the way
|
||||
* If tests are required for contributions, let them know, and explain how to run the tests
|
||||
* If you use anything other than GitHub to manage issues (ex. JIRA or Trac), let them know which tools they’ll need to contribute
|
||||
|
||||
>For something that is bigger than a one or two line fix:
|
||||
|
||||
>1. Create your own fork of the code
|
||||
>2. Do the changes in your fork
|
||||
>3. If you like the change and think the project could use it:
|
||||
* Be sure you have followed the code style for the project.
|
||||
* Sign the Contributor License Agreement, CLA, with the jQuery Foundation.
|
||||
* Note the jQuery Foundation Code of Conduct.
|
||||
* Send a pull request indicating that you have a CLA on file.
|
||||
|
||||
[source: [Requirejs](http://requirejs.org/docs/contributing.html)] **Need more inspiration?** [1] [Active Admin](https://github.com/activeadmin/activeadmin/blob/master/CONTRIBUTING.md#1-where-do-i-go-from-here) [2] [Node.js](https://github.com/nodejs/node/blob/master/CONTRIBUTING.md#code-contributions) [3] [Ember.js](https://github.com/emberjs/ember.js/blob/master/CONTRIBUTING.md#pull-requests)
|
||||
|
||||
### If you have a different process for small or "obvious" fixes, let them know.
|
||||
|
||||
> Small contributions such as fixing spelling errors, where the content is small enough to not be considered intellectual property, can be submitted by a contributor as a patch, without a CLA.
|
||||
>
|
||||
>As a rule of thumb, changes are obvious fixes if they do not introduce any new functionality or creative thinking. As long as the change does not affect functionality, some likely examples include the following:
|
||||
>* Spelling / grammar fixes
|
||||
>* Typo correction, white space and formatting changes
|
||||
>* Comment clean up
|
||||
>* Bug fixes that change default return values or error codes stored in constants
|
||||
>* Adding logging messages or debugging output
|
||||
>* Changes to ‘metadata’ files like Gemfile, .gitignore, build scripts, etc.
|
||||
>* Moving source files from one directory or package to another
|
||||
|
||||
[source: [Chef](https://github.com/chef/chef/blob/master/CONTRIBUTING.md#chef-obvious-fix-policy)] **Need more inspiration?** [1] [Puppet](https://github.com/puppetlabs/puppet/blob/master/CONTRIBUTING.md#making-trivial-changes)
|
||||
|
||||
# How to report a bug
|
||||
### Explain security disclosures first!
|
||||
At bare minimum, include this sentence:
|
||||
> If you find a security vulnerability, do NOT open an issue. Email XXXX instead.
|
||||
|
||||
If you don’t want to use your personal contact information, set up a “security@” email address. Larger projects might have more formal processes for disclosing security, including encrypted communication. (Disclosure: I am not a security expert.)
|
||||
|
||||
> Any security issues should be submitted directly to security@travis-ci.org
|
||||
> In order to determine whether you are dealing with a security issue, ask yourself these two questions:
|
||||
> * Can I access something that's not mine, or something I shouldn't have access to?
|
||||
> * Can I disable something for other people?
|
||||
>
|
||||
> If the answer to either of those two questions are "yes", then you're probably dealing with a security issue. Note that even if you answer "no" to both questions, you may still be dealing with a security issue, so if you're unsure, just email us at security@travis-ci.org.
|
||||
|
||||
[source: [Travis CI](https://github.com/travis-ci/travis-ci/blob/master/CONTRIBUTING.md)] **Need more inspiration?** [1] [Celery](https://github.com/celery/celery/blob/master/CONTRIBUTING.rst#security) [2] [Express.js](https://github.com/expressjs/express/blob/master/Security.md)
|
||||
|
||||
### Tell your contributors how to file a bug report.
|
||||
You can even include a template so people can just copy-paste (again, less work for you).
|
||||
|
||||
> When filing an issue, make sure to answer these five questions:
|
||||
>
|
||||
> 1. What version of Go are you using (go version)?
|
||||
> 2. What operating system and processor architecture are you using?
|
||||
> 3. What did you do?
|
||||
> 4. What did you expect to see?
|
||||
> 5. What did you see instead?
|
||||
> General questions should go to the golang-nuts mailing list instead of the issue tracker. The gophers there will answer or ask you to file an issue if you've tripped over a bug.
|
||||
|
||||
[source: [Go](https://github.com/golang/go/blob/master/CONTRIBUTING.md#filing-issues)] **Need more inspiration?** [1] [Celery](https://github.com/celery/celery/blob/master/CONTRIBUTING.rst#other-bugs ) [2] [Atom](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#reporting-bugs) (includes template)
|
||||
|
||||
# How to suggest a feature or enhancement
|
||||
### If you have a particular roadmap, goals, or philosophy for development, share it here.
|
||||
This information will give contributors context before they make suggestions that may not align with the project’s needs.
|
||||
|
||||
> The Express philosophy is to provide small, robust tooling for HTTP servers, making it a great solution for single page applications, web sites, hybrids, or public HTTP APIs.
|
||||
>
|
||||
> Express does not force you to use any specific ORM or template engine. With support for over 14 template engines via Consolidate.js, you can quickly craft your perfect framework.
|
||||
|
||||
[source: [Express](https://github.com/expressjs/express#philosophy)] **Need more inspiration?** [Active Admin](https://github.com/activeadmin/activeadmin#goals)
|
||||
|
||||
### Explain your desired process for suggesting a feature.
|
||||
If there is back-and-forth or signoff required, say so. Ask them to scope the feature, thinking through why it’s needed and how it might work.
|
||||
|
||||
> If you find yourself wishing for a feature that doesn't exist in Elasticsearch, you are probably not alone. There are bound to be others out there with similar needs. Many of the features that Elasticsearch has today have been added because our users saw the need. Open an issue on our issues list on GitHub which describes the feature you would like to see, why you need it, and how it should work.
|
||||
|
||||
[source: [Elasticsearch](https://github.com/elastic/elasticsearch/blob/master/CONTRIBUTING.md#feature-requests)] **Need more inspiration?** [1] [Hoodie](https://github.com/hoodiehq/hoodie/blob/master/CONTRIBUTING.md#feature-requests) [2] [Ember.js](https://github.com/emberjs/ember.js/blob/master/CONTRIBUTING.md#requesting-a-feature)
|
||||
|
||||
# Code review process
|
||||
### Explain how a contribution gets accepted after it’s been submitted.
|
||||
Who reviews it? Who needs to sign off before it’s accepted? When should a contributor expect to hear from you? How can contributors get commit access, if at all?
|
||||
|
||||
> The core team looks at Pull Requests on a regular basis in a weekly triage meeting that we hold in a public Google Hangout. The hangout is announced in the weekly status updates that are sent to the puppet-dev list. Notes are posted to the Puppet Community community-triage repo and include a link to a YouTube recording of the hangout.
|
||||
> After feedback has been given we expect responses within two weeks. After two weeks we may close the pull request if it isn't showing any activity.
|
||||
|
||||
[source: [Puppet](https://github.com/puppetlabs/puppet/blob/master/CONTRIBUTING.md#submitting-changes)] **Need more inspiration?** [1] [Meteor](https://meteor.hackpad.com/Responding-to-GitHub-Issues-SKE2u3tkSiH ) [2] [Express.js](https://github.com/expressjs/express/blob/master/Contributing.md#becoming-a-committer)
|
||||
|
||||
# Community
|
||||
If there are other channels you use besides GitHub to discuss contributions, mention them here. You can also list the author, maintainers, and/or contributors here, or set expectations for response time.
|
||||
|
||||
> You can chat with the core team on https://gitter.im/cucumber/cucumber. We try to have office hours on Fridays.
|
||||
|
||||
[source: [cucumber-ruby](https://github.com/cucumber/cucumber-ruby/blob/master/CONTRIBUTING.md#talking-with-other-devs)] **Need more inspiration?**
|
||||
[1] [Chef](https://github.com/chef/chef/blob/master/CONTRIBUTING.md#-developer-office-hours) [2] [Cookiecutter](https://github.com/audreyr/cookiecutter#community)
|
||||
|
||||
# BONUS: Code, commit message and labeling conventions
|
||||
These sections are not necessary, but can help streamline the contributions you receive.
|
||||
|
||||
### Explain your preferred style for code, if you have any.
|
||||
|
||||
**Need inspiration?** [1] [Requirejs](http://requirejs.org/docs/contributing.html#codestyle) [2] [Elasticsearch](https://github.com/elastic/elasticsearch/blob/master/CONTRIBUTING.md#contributing-to-the-elasticsearch-codebase)
|
||||
|
||||
### Explain if you use any commit message conventions.
|
||||
|
||||
**Need inspiration?** [1] [Angular](https://github.com/angular/material/blob/master/.github/CONTRIBUTING.md#submit) [2] [Node.js](https://github.com/nodejs/node/blob/master/CONTRIBUTING.md#step-3-commit)
|
||||
|
||||
### Explain if you use any labeling conventions for issues.
|
||||
|
||||
**Need inspiration?** [1] [StandardIssueLabels](https://github.com/wagenet/StandardIssueLabels#standardissuelabels) [2] [Atom](https://github.com/atom/atom/blob/master/CONTRIBUTING.md#issue-and-pull-request-labels)
|
||||
[homepage]: http://contributor-covenant.org
|
||||
[version]: http://contributor-covenant.org/version/1/4/
|
||||
|
||||
14
Dockerfile
Normal file
@@ -0,0 +1,14 @@
|
||||
FROM node:12.10.0-buster
|
||||
|
||||
LABEL maintainer="Liyas Thomas (liyascthomas@gmail.com)"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN npm install
|
||||
RUN npm run build
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["npm", "run", "start"]
|
||||
236
README.md
@@ -1,40 +1,46 @@
|
||||
```
|
||||
When I wrote this, only God and I understood what I was doing. Now, only God knows.
|
||||
```
|
||||
|
||||
<div align="center">
|
||||
<a href="https://liyas-thomas.firebaseapp.com"><img src="https://raw.githubusercontent.com/liyasthomas/templates/master/assets/logo.gif" alt="Liyas Thomas" width="200"></a>
|
||||
<br>
|
||||
<h1>Liyas Thomas</h1>
|
||||
<sub>Built with ❤︎ by
|
||||
<a href="https://github.com/liyasthomas">liyasthomas</a> and
|
||||
<a href="https://github.com/liyasthomas/postwoman/graphs/contributors">contributors</a>
|
||||
</sub>
|
||||
<a href="https://liyas-thomas.firebaseapp.com"><img src="static/icons/logo.svg" alt="Liyas Thomas" height="128"></a>
|
||||
<br>
|
||||
<h1><a href="https://postwoman.io">Postwoman.io</a></h1>
|
||||
<p>
|
||||
API request builder - Helps you create your requests faster, saving you precious time on your development - <a href="https://postwoman.launchaco.com">Subscribe</a>
|
||||
</p>
|
||||
<p>
|
||||
|
||||
[](https://travis-ci.com/liyasthomas/postwoman) [](https://github.com/liyasthomas/postwoman/releases/latest) [](CONTRIBUTING.md) [](https://postwoman.io) [](https://opencollective.com/postwoman) [](https://www.paypal.me/liyascthomas) [](https://t.me/postwoman_app) [](https://discord.gg/GAMWxmR)
|
||||
|
||||
</p>
|
||||
<sub>Built with ❤︎ by
|
||||
<a href="https://github.com/liyasthomas">liyasthomas</a> and
|
||||
<a href="https://github.com/liyasthomas/postwoman/graphs/contributors">contributors</a>
|
||||
</sub>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
[](https://travis-ci.com/liyasthomas/postwoman) [](https://github.com/liyasthomas/postwoman/releases/latest) [](https://github.com/liyasthomas/postwoman/archive/master.zip) [](https://github.com/liyasthomas/postwoman/blob/master/LICENSE) [](https://github.com/liyasthomas/postwoman/issues) [](https://liyas-thomas.firebaseapp.com) [](https://www.paypal.me/liyascthomas) [](https://www.buymeacoffee.com/liyasthomas)
|
||||
|
||||
# <img src="static/icon.png" alt="postwoman" width="32"> Postwoman
|
||||
|
||||
### API request builder
|
||||
|
||||
**Start here: _[Story behind Postwoman](https://dev.to/liyasthomas/i-created-postwoman-an-online-open-source-api-request-builder-41md)_**
|
||||
|
||||
**Chat here: _[Telegram](https://t.me/postwoman_app), [Discord](https://discord.gg/GAMWxmR)_**
|
||||
|
||||
**Donate here: _[PayPal](https://www.paypal.me/liyascthomas), [Open Collective](https://opencollective.com/postwoman), [Patreon](https://www.patreon.com/liyasthomas)_**
|
||||
|
||||
<div align="center">
|
||||
<br>
|
||||
<img src="static/screely.png" alt="postwoman" width="100%">
|
||||
<img src="static/screely2.png" alt="postwoman" width="100%">
|
||||
<img src="static/screenshot1.png" alt="postwoman" width="100%">
|
||||
<br>
|
||||
</div>
|
||||
|
||||
### Features :sparkles:
|
||||
### Features ✨
|
||||
|
||||
:heart: **Lightweight**: Crafted with minimalistic UI design
|
||||
❤️ **Lightweight**: Crafted with minimalistic UI design. Simple design is the best design.
|
||||
|
||||
- Faster, lighter, cleaner, minimal & responsive
|
||||
|
||||
:zap: **Real-time**: Send requests and get/copy responses right away!
|
||||
⚡️ **Fast**: Send requests and get/copy responses in real-time! Fast software is the best software.
|
||||
|
||||
**Methods:**
|
||||
- `GET` - Retrieve information about the REST API resource
|
||||
@@ -47,63 +53,88 @@ When I wrote this, only God and I understood what I was doing. Now, only God kno
|
||||
|
||||
_History entries are synced with local session storage_
|
||||
|
||||
:rainbow: **VIBGYOR**: Neon combination background, foreground & accent colors - because customization === freedom :sparkles:
|
||||
🌈 **Make it yours**: Customizable combinations for background, foreground and accent colors: because customization === freedom. [Customize now ✨](https://postwoman.io/settings).
|
||||
|
||||
**Customizations:**
|
||||
- Dark and Light background themes
|
||||
- Choose accent color
|
||||
- Choose theme: Kinda Dark (default), Clearly White, Just Black and System theme
|
||||
- Choose accent color: Green (default), Yellow, Pink, Red, Purple, Orange, Cyan and Blue
|
||||
- Toggle multi-colored frames
|
||||
|
||||
_Customized themes are also synced with local session storage_
|
||||
|
||||
:fire: **PWA**: Install as a **[PWA](https://developers.google.com/web/progressive-web-apps)** on your device
|
||||
🔥 **PWA**: Install as a **[PWA](https://developers.google.com/web/progressive-web-apps)** on your device.
|
||||
|
||||
**Features:**
|
||||
- Instant loading with Service Workers
|
||||
- Instant loading with [Service Workers](https://developers.google.com/web/fundamentals/primers/service-workers)
|
||||
- Offline support
|
||||
- Low RAM/memory and CPU usage
|
||||
- [Add to Home Screen](https://developers.google.com/web/fundamentals/app-install-banners) (button in footer)
|
||||
- [Desktop PWA](https://developers.google.com/web/progressive-web-apps/desktop) support (button in footer)
|
||||
- [Full features](https://developers.google.com/web/progressive-web-apps)
|
||||
|
||||
:electric_plug: **Web Socket**: Establish full-duplex communication channels over a single TCP connection
|
||||
🚀 **Request**: Retrieve data from a URL without having to do a full page refresh.
|
||||
|
||||
- Choose `method`
|
||||
- Enter `URL`
|
||||
- Enter `Path`
|
||||
|
||||
**Features:**
|
||||
- Copy/share public "Share URL"
|
||||
- Generate request code for JavaScript XHR, Fetch and cURL
|
||||
- Copy generated request code to clipboard
|
||||
- Import cURL
|
||||
- Label requests
|
||||
|
||||
🔌 **Web Socket**: Establish full-duplex communication channels over a single TCP connection.
|
||||
|
||||
- Send and receive data
|
||||
|
||||
:closed_lock_with_key: **Authentication**: Allows to identity the end user
|
||||
🔐 **Authentication**: Allows to identity the end user.
|
||||
|
||||
**Types:**
|
||||
- None
|
||||
- Basic authentication using username and password
|
||||
- Token based authentication
|
||||
|
||||
:loudspeaker: **Headers**: Describes the format the body of your request is being sent as
|
||||
📢 **Headers**: Describes the format the body of your request is being sent as.
|
||||
|
||||
:mailbox: **Parameters**: Use request parameters to set varying parts in simulated requests
|
||||
- Add or remove Header list
|
||||
|
||||
:page_with_curl: **Request Body**: Used to send and receive data via the REST API
|
||||
📫 **Parameters**: Use request parameters to set varying parts in simulated requests.
|
||||
|
||||
📃 **Request Body**: Used to send and receive data via the REST API.
|
||||
|
||||
**Options:**
|
||||
- Set content Type
|
||||
- Toggle between RAW input and parameter list
|
||||
- Set Content Type
|
||||
- Add or remove Parameter list
|
||||
- Toggle between key-value and RAW input Parameter list
|
||||
|
||||
:wave: **Responses**: Contains the status line, headers and the message/response body
|
||||
👋 **Responses**: Contains the status line, headers and the message/response body.
|
||||
|
||||
- Copy response to clipboard
|
||||
- View preview for HTML responses
|
||||
|
||||
_HTML responses have "Preview HTML" feature_
|
||||
|
||||
:alarm_clock: **History**: Request entries are synced with local session storage to reuse with a single click
|
||||
⏰ **History**: Request entries are synced with local session storage to reuse with a single click.
|
||||
|
||||
**Fields**
|
||||
- Label
|
||||
- Timestamp
|
||||
- Method
|
||||
- Status code
|
||||
- URL
|
||||
- Path
|
||||
|
||||
_History entries can be deleted one-by-one or all together_
|
||||
_History entries can be sorted by any fields_
|
||||
|
||||
---
|
||||
_Histories can deleted one-by-one or all together_
|
||||
|
||||
## Demo
|
||||
## Demo 🚀
|
||||
|
||||
[https://liyasthomas.github.io/postwoman](https://liyasthomas.github.io/postwoman)
|
||||
[https://postwoman.io](https://postwoman.io)
|
||||
|
||||
## Usage 💡
|
||||
|
||||
1. Specify your request method
|
||||
2. Type in your API URL
|
||||
@@ -113,94 +144,143 @@ _History entries can be deleted one-by-one or all together_
|
||||
|
||||
You're done!
|
||||
|
||||
---
|
||||
## Built with 🔧
|
||||
|
||||
## Built with
|
||||
|
||||
* **[Chromium](https://github.com/chromium/chromium)** - Thanks for being so fast!
|
||||
* [Chromium](https://github.com/chromium/chromium) - Thanks for being so fast!
|
||||
* HTML - For the web framework
|
||||
* CSS - For styling components
|
||||
* JavaScript - For magic!
|
||||
* [Vue](https://vuejs.org/) - To add to the JavaScript magic!
|
||||
* [Nuxt](https://nuxtjs.org/) - To add to the Vue magic! <!-- (Nuxt helps create the PWA and single page application.) -->
|
||||
* [Nuxt](https://nuxtjs.org/) - To add to the Vue magic!
|
||||
|
||||
---
|
||||
|
||||
## Developing
|
||||
## Developing 👷
|
||||
|
||||
1. [Clone this repo](https://help.github.com/en/articles/cloning-a-repository) with git.
|
||||
1. Install dependencies by running `npm install` within the directory that you cloned (probably `postwoman`).
|
||||
1. Start the development server with `npm run dev`.
|
||||
1. Open development site by going to [http://localhost:3000](http://localhost:3000) in your browser.
|
||||
|
||||
---
|
||||
#### Or, with docker-compose:
|
||||
|
||||
1. [Clone this repo](https://help.github.com/en/articles/cloning-a-repository) with git.
|
||||
1. Run `docker-compose up`
|
||||
1. Open development site by going to [http://localhost:3000](http://localhost:3000) in your browser.
|
||||
|
||||
## Docker 🐳
|
||||
|
||||
```bash
|
||||
#pull
|
||||
docker pull liyasthomas/postwoman
|
||||
|
||||
#run
|
||||
docker run -p 3000:3000 liyasthomas/postwoman:latest
|
||||
|
||||
#build
|
||||
docker build -t postwoman:latest
|
||||
```
|
||||
|
||||
## Releasing 🏷️
|
||||
|
||||
## Releasing
|
||||
1. [Clone this repo](https://help.github.com/en/articles/cloning-a-repository) with git.
|
||||
1. Install dependencies by running `npm install` within the directory that you cloned (probably `postwoman`).
|
||||
1. Build the release files with `npm run build`.
|
||||
1. Find the built project in `./dist`.
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
## Contributing 🍰
|
||||
|
||||
Please read [CONTRIBUTING](CONTRIBUTING.md) for details on our [CODE OF CONDUCT](CODE_OF_CONDUCT.md), and the process for submitting pull requests to us.
|
||||
|
||||
---
|
||||
|
||||
## Continuous Integration
|
||||
## Continuous Integration 💚
|
||||
|
||||
We use [Travis CI](https://travis-ci.com) for continuous integration. Check out our [Travis CI Status](https://travis-ci.com/liyasthomas/postwoman).
|
||||
|
||||
---
|
||||
|
||||
## Versioning
|
||||
## Versioning 🔖
|
||||
|
||||
This project is developed by [Liyas Thomas](https://github.com/liyasthomas) using the [Semantic Versioning specification](https://semver.org). For the versions available, see the [releases on this repository](https://github.com/liyasthomas/postwoman/releases).
|
||||
|
||||
---
|
||||
|
||||
## Change log
|
||||
## Change log 📝
|
||||
|
||||
See the [CHANGELOG](CHANGELOG.md) file for details.
|
||||
|
||||
---
|
||||
|
||||
## Authors
|
||||
## Authors 🔮
|
||||
|
||||
### Lead Developers
|
||||
* [**Liyas Thomas**](https://github.com/liyasthomas) - *Author*
|
||||
|
||||
* **[Liyas Thomas](https://github.com/liyasthomas)** - *Author*
|
||||
|
||||
### Testing and Debugging
|
||||
* [Liyas Thomas](https://github.com/liyasthomas)
|
||||
|
||||
* ([contributors](https://github.com/liyasthomas/postwoman/graphs/contributors))
|
||||
|
||||
### Contributors
|
||||
* [John Harker](https://github.com/NBTX)
|
||||
* [Andrew Bastin](https://github.com/AndrewBastin)
|
||||
* [Nick Palenchar](https://github.com/nickpalenchar)
|
||||
* [Abraham Williams](https://github.com/abraham)
|
||||
* [Nicholas La Roux](https://github.com/larouxn)
|
||||
* [RifqiAlAbqary](https://github.com/reefqi037)
|
||||
* [izerozlu](https://github.com/izerozlu)
|
||||
* [Thomas Yuba](https://github.com/yubathom)
|
||||
### Collaborators
|
||||
|
||||
### Thanks
|
||||
* [Dribbble](https://dribbble.com)
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/NBTX"><img src="https://github.com/NBTX.png?size=100" width="100px;" alt="John Harker"/><br /><sub><b>John Harker</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=NBTX" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/izerozlu"><img src="https://github.com/izerozlu.png?size=100" width="100px;" alt="izerozlu"/><br /><sub><b>izerozlu</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=izerozlu" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/AndrewBastin"><img src="https://github.com/AndrewBastin.png?size=100" width="100px;" alt="Andrew Bastin"/><br /><sub><b>Andrew Bastin</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=AndrewBastin" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/nickpalenchar"><img src="https://github.com/nickpalenchar.png?size=100" width="100px;" alt="Nick Palenchar"/><br /><sub><b>Nick Palenchar</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=nickpalenchar" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/yubathom"><img src="https://github.com/yubathom.png?size=100" width="100px;" alt="Thomas Yuba"/><br /><sub><b>Thomas Yuba</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=yubathom" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/larouxn"><img src="https://github.com/larouxn.png?size=100" width="100px;" alt="Nicholas La Roux"/><br /><sub><b>Nicholas La Roux</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=larouxn" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/JacobAnavisca"><img src="https://github.com/JacobAnavisca.png?size=100" width="100px;" alt="Jacob Anavisca"/><br /><sub><b>Jacob Anavisca</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=JacobAnavisca" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/nityanandagohain"><img src="https://github.com/nityanandagohain.png?size=100" width="100px;" alt="Nityananda Gohain"/><br /><sub><b>Nityananda Gohain</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=nityanandagohain" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/terranblake"><img src="https://github.com/terranblake.png?size=100" width="100px;" alt="Terran Blake"/><br /><sub><b>Terran Blake</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=terranblake" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/hosseinnedaee"><img src="https://github.com/hosseinnedaee.png?size=100" width="100px;" alt="Hossein Nedaee"/><br /><sub><b>Hossein Nedaee</b></sub></a><br /><a href="https://github.com/liyasthomas/postwoman/commits?author=hosseinnedaee" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
See the list of [contributors](https://github.com/liyasthomas/postwoman/graphs/contributors) who participated in this project.
|
||||
|
||||
---
|
||||
### Thanks
|
||||
|
||||
## License
|
||||
* [dev.to](https://dev.to)
|
||||
|
||||
## Contributors 🚸
|
||||
|
||||
### Code Contributors
|
||||
|
||||
This project exists thanks to all the people who contribute. [[Contribute](CONTRIBUTING.md)].
|
||||
|
||||
<a href="https://github.com/liyasthomas/postwoman/graphs/contributors"><img src="https://opencollective.com/postwoman/contributors.svg?width=890&button=false" /></a>
|
||||
|
||||
### Financial Contributors
|
||||
|
||||
Become a financial contributor and help us sustain our community. [[Contribute](https://opencollective.com/postwoman/contribute)]
|
||||
|
||||
#### Individuals
|
||||
|
||||
<a href="https://opencollective.com/postwoman"><img src="https://opencollective.com/postwoman/individuals.svg"></a>
|
||||
|
||||
#### Organizations
|
||||
|
||||
Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/postwoman/contribute)]
|
||||
|
||||
<a href="https://opencollective.com/postwoman/organization/0/website"><img src="https://opencollective.com/postwoman/organization/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/postwoman/organization/1/website"><img src="https://opencollective.com/postwoman/organization/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/postwoman/organization/2/website"><img src="https://opencollective.com/postwoman/organization/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/postwoman/organization/3/website"><img src="https://opencollective.com/postwoman/organization/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/postwoman/organization/4/website"><img src="https://opencollective.com/postwoman/organization/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/postwoman/organization/5/website"><img src="https://opencollective.com/postwoman/organization/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/postwoman/organization/6/website"><img src="https://opencollective.com/postwoman/organization/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/postwoman/organization/7/website"><img src="https://opencollective.com/postwoman/organization/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/postwoman/organization/8/website"><img src="https://opencollective.com/postwoman/organization/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/postwoman/organization/9/website"><img src="https://opencollective.com/postwoman/organization/9/avatar.svg"></a>
|
||||
|
||||
## License 📄
|
||||
|
||||
This project is licensed under the [MIT License](https://opensource.org/licenses/MIT) - see the [LICENSE](LICENSE) file for details.
|
||||
|
||||
---
|
||||
|
||||
## Acknowledgments
|
||||
## Acknowledgments 🙏
|
||||
|
||||
* Hat tip to anyone who's code was used
|
||||
* Inspirations:
|
||||
* [Dribbble](https://dribbble.com)
|
||||
|
||||
<div align="center">
|
||||
<a href="https://liyas-thomas.firebaseapp.com"><img src="https://raw.githubusercontent.com/liyasthomas/templates/master/assets/logo.gif" alt="Liyas Thomas" width="200"></a>
|
||||
<br>
|
||||
<h3>Happy Coding ❤︎</h3>
|
||||
</div>
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
// Poppins (Google Fonts)
|
||||
@import url("https://fonts.googleapis.com/css?family=Poppins:500,700&display=swap");
|
||||
@import url('https://fonts.googleapis.com/css?family=Material+Icons|Poppins:500,700|Roboto+Mono:400&display=swap');
|
||||
|
||||
@@ -7,17 +7,27 @@ $responsiveWidth: 720px;
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #4a4a4a;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
::placeholder {
|
||||
color: var(--fg-light-color);
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
outline: 0;
|
||||
border: 0;
|
||||
font-family: "Poppins", "Roboto", "Noto", sans-serif;
|
||||
}
|
||||
|
||||
a {
|
||||
@@ -31,10 +41,11 @@ body {
|
||||
background-color: var(--bg-color);
|
||||
color: var(--fg-color);
|
||||
font-weight: 500;
|
||||
font-size: 16px;
|
||||
font-family: "Poppins", "Roboto", "Noto", sans-serif;
|
||||
line-height: 1.5;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@@ -45,6 +56,116 @@ h3 {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
display: block !important;
|
||||
z-index: 10000;
|
||||
|
||||
.tooltip-inner {
|
||||
background: black;
|
||||
color: white;
|
||||
border-radius: 16px;
|
||||
padding: 5px 10px 4px;
|
||||
}
|
||||
|
||||
.tooltip-arrow {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
position: absolute;
|
||||
margin: 5px;
|
||||
border-color: black;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&[x-placement^="top"] {
|
||||
margin-bottom: 5px;
|
||||
|
||||
.tooltip-arrow {
|
||||
border-width: 5px 5px 0 5px;
|
||||
border-left-color: transparent !important;
|
||||
border-right-color: transparent !important;
|
||||
border-bottom-color: transparent !important;
|
||||
bottom: -5px;
|
||||
left: calc(50% - 5px);
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&[x-placement^="bottom"] {
|
||||
margin-top: 5px;
|
||||
|
||||
.tooltip-arrow {
|
||||
border-width: 0 5px 5px 5px;
|
||||
border-left-color: transparent !important;
|
||||
border-right-color: transparent !important;
|
||||
border-top-color: transparent !important;
|
||||
top: -5px;
|
||||
left: calc(50% - 5px);
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&[x-placement^="right"] {
|
||||
margin-left: 5px;
|
||||
|
||||
.tooltip-arrow {
|
||||
border-width: 5px 5px 5px 0;
|
||||
border-left-color: transparent !important;
|
||||
border-top-color: transparent !important;
|
||||
border-bottom-color: transparent !important;
|
||||
left: -5px;
|
||||
top: calc(50% - 5px);
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&[x-placement^="left"] {
|
||||
margin-right: 5px;
|
||||
|
||||
.tooltip-arrow {
|
||||
border-width: 5px 0 5px 5px;
|
||||
border-top-color: transparent !important;
|
||||
border-right-color: transparent !important;
|
||||
border-bottom-color: transparent !important;
|
||||
right: -5px;
|
||||
top: calc(50% - 5px);
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.popover {
|
||||
$color: #f9f9f9;
|
||||
|
||||
.popover-inner {
|
||||
background: $color;
|
||||
color: black;
|
||||
padding: 24px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 5px 30px rgba(black, .1);
|
||||
}
|
||||
|
||||
.popover-arrow {
|
||||
border-color: $color;
|
||||
}
|
||||
}
|
||||
|
||||
&[aria-hidden='true'] {
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: opacity .15s, visibility .15s;
|
||||
}
|
||||
|
||||
&[aria-hidden='false'] {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
transition: opacity .15s;
|
||||
}
|
||||
}
|
||||
|
||||
h3.title {
|
||||
margin: 4px;
|
||||
}
|
||||
@@ -67,10 +188,7 @@ nav {
|
||||
}
|
||||
|
||||
body.sticky-footer footer {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
opacity: .25;
|
||||
}
|
||||
|
||||
.logo {
|
||||
@@ -78,101 +196,99 @@ body.sticky-footer footer {
|
||||
}
|
||||
|
||||
button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 4px;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
padding: 0 16px;
|
||||
border-radius: 20px;
|
||||
background-color: var(--ac-color);
|
||||
color: var(--act-color);
|
||||
font-weight: 700;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
font-family: "Poppins", "Roboto", "Noto", sans-serif;
|
||||
transition: all 0.2s ease-in-out;
|
||||
fill: var(--act-color);
|
||||
height: 40px;
|
||||
box-shadow: 0px 1px 0px rgba(0, 0, 0, 0.02);
|
||||
cursor: pointer;
|
||||
|
||||
&[disabled], &.disabled {
|
||||
opacity: 0.7;
|
||||
cursor: default;
|
||||
span {
|
||||
display: inline-flex;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
// Only show hover and focus if the button is *not*
|
||||
// disabled.
|
||||
&:not([disabled]):hover, &:not(.disabled):focus {
|
||||
background-color: transparent;
|
||||
box-shadow: inset 0 0 0 2px var(--ac-color);
|
||||
color: var(--ac-color);
|
||||
}
|
||||
&.icon {
|
||||
background-color: var(--bg-color);
|
||||
color: var(--fg-light-color);
|
||||
fill: var(--fg-light-color);
|
||||
|
||||
&:not([disabled]):hover {
|
||||
color: var(--fg-color);
|
||||
fill: var(--fg-color);
|
||||
}
|
||||
}
|
||||
|
||||
&:not([disabled]):hover {
|
||||
color: var(--act-color);
|
||||
fill: var(--act-color);
|
||||
}
|
||||
}
|
||||
|
||||
fieldset {
|
||||
margin: 16px 0;
|
||||
border: 2px solid var(--fg-color);
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--brd-color);
|
||||
border-radius: 8px;
|
||||
background-color: var(--bg-color);
|
||||
}
|
||||
|
||||
legend {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--fg-color);
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
fieldset textarea,
|
||||
fieldset pre {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
fieldset.blue {
|
||||
border-color: #57b5f9;
|
||||
i {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
fieldset.blue legend {
|
||||
color: #57b5f9;
|
||||
}
|
||||
|
||||
fieldset.gray {
|
||||
border-color: #9B9B9B;
|
||||
}
|
||||
|
||||
fieldset.gray legend {
|
||||
color: #9B9B9B;
|
||||
}
|
||||
|
||||
fieldset.green {
|
||||
border-color: #B8E986;
|
||||
color: #bcc2cd;
|
||||
}
|
||||
|
||||
fieldset.green legend {
|
||||
color: #B8E986;
|
||||
}
|
||||
|
||||
fieldset.cyan {
|
||||
border-color: #50E3C2;
|
||||
color: #50fa7b;
|
||||
}
|
||||
|
||||
fieldset.cyan legend {
|
||||
color: #50E3C2;
|
||||
}
|
||||
|
||||
fieldset.blue-dark {
|
||||
border-color: #4A90E2;
|
||||
}
|
||||
|
||||
fieldset.blue-dark legend {
|
||||
color: #4A90E2;
|
||||
}
|
||||
|
||||
fieldset.purple {
|
||||
border-color: #C198FB;
|
||||
color: #8be9fd;
|
||||
}
|
||||
|
||||
fieldset.purple legend {
|
||||
color: #C198FB;
|
||||
}
|
||||
|
||||
fieldset.orange {
|
||||
border-color: #F5A623;
|
||||
color: #bd93f9;
|
||||
}
|
||||
|
||||
fieldset.orange legend {
|
||||
color: #F5A623;
|
||||
color: #ffb86c;
|
||||
}
|
||||
|
||||
fieldset.pink legend {
|
||||
color: #ff79c6;
|
||||
}
|
||||
|
||||
fieldset.red legend {
|
||||
color: #ff5555;
|
||||
}
|
||||
|
||||
fieldset.yellow legend {
|
||||
color: #f1fa8c;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
@@ -185,21 +301,44 @@ option,
|
||||
textarea,
|
||||
pre {
|
||||
margin: 4px;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
width: calc(100% - 8px);
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
background-color: var(--bg-dark-color);
|
||||
color: var(--fg-color);
|
||||
font-weight: 700;
|
||||
font-size: 18px;
|
||||
font-family: monospace;
|
||||
font-size: 16px;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
transition: all 0.2s ease-in-out;
|
||||
user-select: text;
|
||||
width: calc(100% - 8px);
|
||||
min-height: 40px;
|
||||
resize: vertical;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
&:not([readonly]):hover {
|
||||
background-color: var(--bg-dark-color);
|
||||
}
|
||||
}
|
||||
|
||||
pre {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
code {
|
||||
height: 336px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.hljs,
|
||||
.hljs-subst {
|
||||
background-color: var(--bg-dark-color) !important;
|
||||
color: var(--fg-color) !important;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
}
|
||||
|
||||
// Force the same height, for dropdowns and regular input boxes.
|
||||
select,
|
||||
input,
|
||||
option {
|
||||
height: 41px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
@@ -212,8 +351,8 @@ input[type="checkbox"] {
|
||||
|
||||
&:before {
|
||||
content: "\2714";
|
||||
border: 2px solid var(--fg-color);
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--fg-color);
|
||||
border-radius: 8px;
|
||||
display: inline-flex;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
@@ -221,7 +360,7 @@ input[type="checkbox"] {
|
||||
justify-content: center;
|
||||
margin: 8px 8px 8px 0;
|
||||
color: transparent;
|
||||
transition: .2s;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,25 +371,29 @@ input[type="checkbox"] {
|
||||
}
|
||||
}
|
||||
|
||||
.error {
|
||||
background-color: var(--err-color);
|
||||
}
|
||||
|
||||
.error,
|
||||
.disabled,
|
||||
input[disabled],
|
||||
button[disabled] {
|
||||
[disabled] {
|
||||
background-color: var(--err-color);
|
||||
color: #b2b2b2;
|
||||
color: var(--fg-light-color);
|
||||
fill: var(--fg-light-color);
|
||||
cursor: default;
|
||||
|
||||
&.icon {
|
||||
color: var(--bg-color);
|
||||
fill: var(--bg-color);
|
||||
}
|
||||
}
|
||||
|
||||
label {
|
||||
padding: 4px;
|
||||
color: var(--fg-light-color);
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
display: flex;
|
||||
margin: 8px 0 0;
|
||||
margin: 4px 0 4px;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
@@ -264,8 +407,12 @@ ol li {
|
||||
|
||||
.flex-wrap {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.show-on-small-screen {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@media (max-width: $responsiveWidth) {
|
||||
@@ -293,6 +440,10 @@ ol li {
|
||||
.hide-on-small-screen {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.show-on-small-screen {
|
||||
display: inline-flex;
|
||||
}
|
||||
}
|
||||
|
||||
#installPWA {
|
||||
@@ -300,27 +451,27 @@ ol li {
|
||||
}
|
||||
|
||||
.info-response {
|
||||
background-color: #FFEB3B;
|
||||
color: #ffeb3b;
|
||||
}
|
||||
|
||||
.success-response {
|
||||
background-color: #4BB543;
|
||||
color: #4bb543;
|
||||
}
|
||||
|
||||
.redir-response {
|
||||
background-color: #FF5722;
|
||||
color: #ff5722;
|
||||
}
|
||||
|
||||
.cl-error-response {
|
||||
background-color: #A63232;
|
||||
color: #a63232;
|
||||
}
|
||||
|
||||
.sv-error-response {
|
||||
background-color: #B71C1C;
|
||||
color: #b71c1c;
|
||||
}
|
||||
|
||||
.missing-data-response {
|
||||
background-color: #676767;
|
||||
background-color: var(--err-color);
|
||||
}
|
||||
|
||||
.virtual-list::-webkit-scrollbar {
|
||||
@@ -333,9 +484,9 @@ fieldset#history {
|
||||
|
||||
span {
|
||||
position: absolute;
|
||||
top: 44px;
|
||||
right: 20px;
|
||||
font-family: monospace, monospace;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -355,15 +506,11 @@ fieldset#history {
|
||||
#response-details-wrapper {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
border-radius: 4px;
|
||||
margin: 4px;
|
||||
border-radius: 8px;
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#response-details {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.covers-response {
|
||||
@@ -378,7 +525,7 @@ fieldset#history {
|
||||
}
|
||||
}
|
||||
|
||||
#action {
|
||||
#send {
|
||||
#hidden-message {
|
||||
display: none;
|
||||
}
|
||||
@@ -396,3 +543,37 @@ fieldset#history {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
section {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
div.tab {
|
||||
width: 100%;
|
||||
order: 1;
|
||||
}
|
||||
|
||||
input[type="radio"],
|
||||
div.tab {
|
||||
display: none;
|
||||
}
|
||||
|
||||
input[type="radio"] + label {
|
||||
padding: 8px 16px;
|
||||
border-bottom: 2px solid transparent;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--brd-color);
|
||||
}
|
||||
}
|
||||
|
||||
input[type="radio"]:checked + label {
|
||||
border-color: var(--fg-color);
|
||||
}
|
||||
|
||||
input[type="radio"]:checked + label + div.tab {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -3,41 +3,116 @@
|
||||
|
||||
- dark (default)
|
||||
- light
|
||||
- black
|
||||
- auto
|
||||
*/
|
||||
|
||||
// Dark is the default theme variant.
|
||||
:root {
|
||||
--bg-dark-color: #000000;
|
||||
// Dark Background color
|
||||
--bg-dark-color: rgb(41, 42, 45);
|
||||
// Background color
|
||||
--bg-color: #121212;
|
||||
// Auto-complete color
|
||||
--atc-color: #212121;
|
||||
--bg-color: rgb(37, 38, 40);
|
||||
// Auto-complete color
|
||||
--atc-color: rgb(49, 49, 55);
|
||||
// Text color
|
||||
--fg-color: #FFF;
|
||||
|
||||
--fg-color: rgb(247, 248, 248);
|
||||
// Light Text color
|
||||
--fg-light-color: rgb(150, 155, 160);
|
||||
// Border color
|
||||
--brd-color: rgb(48, 47, 55);
|
||||
// Error color
|
||||
--err-color: #393939;
|
||||
|
||||
// Active color
|
||||
--ac-color: #51FF0D;
|
||||
--err-color: rgb(41, 42, 45);
|
||||
// Acent color
|
||||
--ac-color: #50fa7b;
|
||||
// Active text color
|
||||
--act-color: #121212;
|
||||
--act-color: rgb(37, 38, 40);
|
||||
}
|
||||
|
||||
:root.light {
|
||||
--bg-dark-color: #ffffff;
|
||||
// Dark Background color
|
||||
--bg-dark-color: #f6f6f6;
|
||||
// Background color
|
||||
--bg-color: #F6F8FA;
|
||||
// Auto-complete color
|
||||
--atc-color: #F1F1F1;
|
||||
--bg-color: #ffffff;
|
||||
// Auto-complete color
|
||||
--atc-color: #ebebeb;
|
||||
// Text color
|
||||
--fg-color: #121212;
|
||||
|
||||
--fg-color: #525252;
|
||||
// Light Text color
|
||||
--fg-light-color: rgb(150, 155, 160);
|
||||
// Border color
|
||||
--brd-color: #eeeeed;
|
||||
// Error color
|
||||
--err-color: invert(#393939, 1);
|
||||
|
||||
// Active color
|
||||
--ac-color: #51FF0D;
|
||||
--err-color: #f6f6f6;
|
||||
// Acent color
|
||||
--ac-color: #57b5f9;
|
||||
// Active text color
|
||||
--act-color: #121212;
|
||||
--act-color: #ffffff;
|
||||
}
|
||||
|
||||
:root.black {
|
||||
// Dark Background color
|
||||
--bg-dark-color: rgb(8, 8, 8);
|
||||
// Background color
|
||||
--bg-color: #000000;
|
||||
// Auto-complete color
|
||||
--atc-color: rgb(18, 18, 18);
|
||||
// Text color
|
||||
--fg-color: rgb(250, 250, 250);
|
||||
// Light Text color
|
||||
--fg-light-color: rgb(100, 100, 100);
|
||||
// Border color
|
||||
--brd-color: rgb(16, 16, 16);
|
||||
// Error color
|
||||
--err-color: rgb(8, 8, 8);
|
||||
// Acent color
|
||||
--ac-color: #50fa7b;
|
||||
// Active text color
|
||||
--act-color: #000000;
|
||||
}
|
||||
|
||||
@media(prefers-color-scheme: light) {
|
||||
:root.auto {
|
||||
// Dark Background color
|
||||
--bg-dark-color: #f6f6f6;
|
||||
// Background color
|
||||
--bg-color: #ffffff;
|
||||
// Auto-complete color
|
||||
--atc-color: #ebebeb;
|
||||
// Text color
|
||||
--fg-color: #525252;
|
||||
// Light Text color
|
||||
--fg-light-color: rgb(150, 155, 160);
|
||||
// Border color
|
||||
--brd-color: #eeeeed;
|
||||
// Error color
|
||||
--err-color: #f6f6f6;
|
||||
// Acent color
|
||||
--ac-color: #57b5f9;
|
||||
// Active text color
|
||||
--act-color: #ffffff;
|
||||
}
|
||||
}
|
||||
|
||||
@media(prefers-color-scheme: dark) {
|
||||
:root.auto {
|
||||
// Dark Background color
|
||||
--bg-dark-color: rgb(41, 42, 45);
|
||||
// Background color
|
||||
--bg-color: rgb(37, 38, 40);
|
||||
// Auto-complete color
|
||||
--atc-color: rgb(49, 49, 55);
|
||||
// Text color
|
||||
--fg-color: rgb(247, 248, 248);
|
||||
// Light Text color
|
||||
--fg-light-color: rgb(150, 155, 160);
|
||||
// Border color
|
||||
--brd-color: rgb(48, 47, 55);
|
||||
// Error color
|
||||
--err-color: rgb(41, 42, 45);
|
||||
// Acent color
|
||||
--ac-color: #50fa7b;
|
||||
// Active text color
|
||||
--act-color: rgb(37, 38, 40);
|
||||
}
|
||||
}
|
||||
|
||||
223
assets/js/curlparser.js
Normal file
@@ -0,0 +1,223 @@
|
||||
import * as cookie from "cookie";
|
||||
import * as URL from "url";
|
||||
import * as querystring from "querystring";
|
||||
|
||||
|
||||
/**
|
||||
* given this: [ 'msg1=value1', 'msg2=value2' ]
|
||||
* output this: 'msg1=value1&msg2=value2'
|
||||
* @param dataArguments
|
||||
*/
|
||||
function joinDataArguments(dataArguments) {
|
||||
let data = '';
|
||||
dataArguments.forEach(function(argument, i) {
|
||||
if (i === 0) {
|
||||
data += argument;
|
||||
} else {
|
||||
data += '&' + argument;
|
||||
}
|
||||
})
|
||||
return data;
|
||||
}
|
||||
|
||||
function parseCurlCommand(curlCommand) {
|
||||
let newlineFound = /\r|\n/.exec(curlCommand);
|
||||
if (newlineFound) {
|
||||
// remove newlines
|
||||
curlCommand = curlCommand.replace(/\\\r|\\\n/g, '');
|
||||
}
|
||||
// yargs parses -XPOST as separate arguments. just prescreen for it.
|
||||
curlCommand = curlCommand.replace(/ -XPOST/, ' -X POST');
|
||||
curlCommand = curlCommand.replace(/ -XGET/, ' -X GET');
|
||||
curlCommand = curlCommand.replace(/ -XPUT/, ' -X PUT');
|
||||
curlCommand = curlCommand.replace(/ -XPATCH/, ' -X PATCH');
|
||||
curlCommand = curlCommand.replace(/ -XDELETE/, ' -X DELETE');
|
||||
curlCommand = curlCommand.trim();
|
||||
let parsedArguments = require("yargs-parser")(curlCommand);
|
||||
let cookieString;
|
||||
let cookies;
|
||||
let url = parsedArguments._[1];
|
||||
if (!url) {
|
||||
for (let argName in parsedArguments) {
|
||||
if (typeof parsedArguments[argName] === 'string') {
|
||||
if (['http', 'www.'].includes(parsedArguments[argName])) {
|
||||
url = parsedArguments[argName];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let headers;
|
||||
|
||||
let parseHeaders = function(headerFieldName) {
|
||||
if (parsedArguments[headerFieldName]) {
|
||||
if (!headers) {
|
||||
headers = {};
|
||||
}
|
||||
if (!Array.isArray(parsedArguments[headerFieldName])) {
|
||||
parsedArguments[headerFieldName] = [parsedArguments[headerFieldName]];
|
||||
}
|
||||
parsedArguments[headerFieldName].forEach(function(header) {
|
||||
if (header.includes('Cookie')) {
|
||||
// stupid javascript tricks: closure
|
||||
cookieString = header;
|
||||
} else {
|
||||
let colonIndex = header.indexOf(':');
|
||||
let headerName = header.substring(0, colonIndex);
|
||||
let headerValue = header.substring(colonIndex + 1).trim();
|
||||
headers[headerName] = headerValue;
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
parseHeaders('H');
|
||||
parseHeaders('header');
|
||||
if (parsedArguments.A) {
|
||||
if (!headers) {
|
||||
headers = [];
|
||||
}
|
||||
headers['User-Agent'] = parsedArguments.A;
|
||||
} else if (parsedArguments['user-agent']) {
|
||||
if (!headers) {
|
||||
headers = [];
|
||||
}
|
||||
headers['User-Agent'] = parsedArguments['user-agent'];
|
||||
}
|
||||
|
||||
if (parsedArguments.b) {
|
||||
cookieString = parsedArguments.b;
|
||||
}
|
||||
if (parsedArguments.cookie) {
|
||||
cookieString = parsedArguments.cookie;
|
||||
}
|
||||
let multipartUploads;
|
||||
if (parsedArguments.F) {
|
||||
multipartUploads = {};
|
||||
if (!Array.isArray(parsedArguments.F)) {
|
||||
parsedArguments.F = [parsedArguments.F];
|
||||
}
|
||||
parsedArguments.F.forEach(function(multipartArgument) {
|
||||
// input looks like key=value. value could be json or a file path prepended with an @
|
||||
let splitArguments = multipartArgument.split('=', 2);
|
||||
let key = splitArguments[0];
|
||||
let value = splitArguments[1];
|
||||
multipartUploads[key] = value;
|
||||
})
|
||||
}
|
||||
if (cookieString) {
|
||||
let cookieParseOptions = {
|
||||
decode: function(s) { return s }
|
||||
}
|
||||
// separate out cookie headers into separate data structure
|
||||
// note: cookie is case insensitive
|
||||
cookies = cookie.parse(cookieString.replace(/^Cookie: /gi, ''), cookieParseOptions);
|
||||
}
|
||||
let method;
|
||||
if (parsedArguments.X === 'POST') {
|
||||
method = 'post';
|
||||
} else if (parsedArguments.X === 'PUT' ||
|
||||
parsedArguments['T']) {
|
||||
method = 'put';
|
||||
} else if (parsedArguments.X === 'PATCH') {
|
||||
method = 'patch';
|
||||
} else if (parsedArguments.X === 'DELETE') {
|
||||
method = 'delete';
|
||||
} else if (parsedArguments.X === 'OPTIONS') {
|
||||
method = 'options';
|
||||
} else if ((parsedArguments['d'] ||
|
||||
parsedArguments['data'] ||
|
||||
parsedArguments['data-ascii'] ||
|
||||
parsedArguments['data-binary'] ||
|
||||
parsedArguments['F'] ||
|
||||
parsedArguments['form']) && !((parsedArguments['G'] || parsedArguments['get']))) {
|
||||
method = 'post';
|
||||
} else if (parsedArguments['I'] ||
|
||||
parsedArguments['head']) {
|
||||
method = 'head';
|
||||
} else {
|
||||
method = 'get';
|
||||
}
|
||||
|
||||
let compressed = !!parsedArguments.compressed;
|
||||
let urlObject = URL.parse(url); // eslint-disable-line
|
||||
|
||||
// if GET request with data, convert data to query string
|
||||
// NB: the -G flag does not change the http verb. It just moves the data into the url.
|
||||
if (parsedArguments['G'] || parsedArguments['get']) {
|
||||
urlObject.query = urlObject.query ? urlObject.query : '';
|
||||
let option = 'd' in parsedArguments ? 'd' : 'data' in parsedArguments ? 'data' : null;
|
||||
if (option) {
|
||||
let urlQueryString = '';
|
||||
|
||||
if (!url.includes('?')) {
|
||||
url += '?';
|
||||
} else {
|
||||
urlQueryString += '&';
|
||||
}
|
||||
|
||||
if (typeof(parsedArguments[option]) === 'object') {
|
||||
urlQueryString += parsedArguments[option].join('&');
|
||||
} else {
|
||||
urlQueryString += parsedArguments[option];
|
||||
}
|
||||
urlObject.query += urlQueryString;
|
||||
url += urlQueryString;
|
||||
delete parsedArguments[option];
|
||||
}
|
||||
}
|
||||
let query = querystring.parse(urlObject.query, null, null, { maxKeys: 10000 });
|
||||
|
||||
urlObject.search = null // Clean out the search/query portion.
|
||||
let request = {
|
||||
url: url,
|
||||
urlWithoutQuery: URL.format(urlObject)
|
||||
}
|
||||
if (compressed) {
|
||||
request['compressed'] = true;
|
||||
}
|
||||
|
||||
if (Object.keys(query).length > 0) {
|
||||
request.query = query;
|
||||
}
|
||||
if (headers) {
|
||||
request.headers = headers;
|
||||
}
|
||||
request['method'] = method;
|
||||
|
||||
if (cookies) {
|
||||
request.cookies = cookies;
|
||||
request.cookieString = cookieString.replace('Cookie: ', '');
|
||||
}
|
||||
if (multipartUploads) {
|
||||
request.multipartUploads = multipartUploads;
|
||||
}
|
||||
if (parsedArguments.data) {
|
||||
request.data = parsedArguments.data;
|
||||
} else if (parsedArguments['data-binary']) {
|
||||
request.data = parsedArguments['data-binary']
|
||||
request.isDataBinary = true;
|
||||
} else if (parsedArguments['d']) {
|
||||
request.data = parsedArguments['d'];
|
||||
} else if (parsedArguments['data-ascii']) {
|
||||
request.data = parsedArguments['data-ascii'];
|
||||
}
|
||||
|
||||
if (parsedArguments['u']) {
|
||||
request.auth = parsedArguments['u'];
|
||||
}
|
||||
if (parsedArguments['user']) {
|
||||
request.auth = parsedArguments['user'];
|
||||
}
|
||||
if (Array.isArray(request.data)) {
|
||||
request.dataArray = request.data
|
||||
request.data = joinDataArguments(request.data);
|
||||
}
|
||||
|
||||
if (parsedArguments['k'] || parsedArguments['insecure']) {
|
||||
request.insecure = true;
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
|
||||
export default parseCurlCommand;
|
||||
@@ -23,7 +23,7 @@ export default () => {
|
||||
|
||||
// Show the install button if the prompt appeared.
|
||||
if (!pwaInstalled) {
|
||||
document.querySelector('#installPWA').style.display = 'block';
|
||||
document.querySelector('#installPWA').style.display = 'inline-flex';
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
78
build.js
@@ -1,46 +1,54 @@
|
||||
const axios = require('axios');
|
||||
const fs = require('fs');
|
||||
const { spawnSync } = require('child_process');
|
||||
const runCommand = (command, args) => spawnSync(command, args).stdout.toString().replace(/\n/g, "");
|
||||
const axios = require("axios");
|
||||
const fs = require("fs");
|
||||
const { spawnSync } = require("child_process");
|
||||
const runCommand = (command, args) =>
|
||||
spawnSync(command, args)
|
||||
.stdout.toString()
|
||||
.replace(/\n/g, "");
|
||||
|
||||
const FAIL_ON_ERROR = false;
|
||||
const PW_BUILD_DATA_DIR = "./.postwoman";
|
||||
const IS_DEV_MODE = process.argv.includes("--dev");
|
||||
|
||||
try {
|
||||
(async () => {
|
||||
// Create the build data directory if it does not exist.
|
||||
if (!fs.existsSync(PW_BUILD_DATA_DIR)) {
|
||||
fs.mkdirSync(PW_BUILD_DATA_DIR);
|
||||
}
|
||||
|
||||
(async () => {
|
||||
// Create the build data directory if it does not exist.
|
||||
if (!fs.existsSync(PW_BUILD_DATA_DIR)) {
|
||||
fs.mkdirSync(PW_BUILD_DATA_DIR);
|
||||
}
|
||||
let version = {};
|
||||
// Get the current version name as the tag from Git.
|
||||
version.name = process.env.TRAVIS_TAG || runCommand("git", ["tag"]);
|
||||
|
||||
let version = {};
|
||||
// Get the current version name as the tag from Git.
|
||||
version.name = (process.env.TRAVIS_TAG || runCommand('git', ['tag']));
|
||||
// FALLBACK: If version.name was unset, let's grab it from GitHub.
|
||||
if (!version.name) {
|
||||
version.name = (await axios
|
||||
.get("https://api.github.com/repos/liyasthomas/postwoman/releases")
|
||||
// If we can't get it from GitHub, we'll resort to getting it from package.json
|
||||
.catch(ex => ({
|
||||
data: [{ tag_name: require("./package.json").version }]
|
||||
}))).data[0]["tag_name"];
|
||||
}
|
||||
|
||||
// FALLBACK: If version.name was unset, let's grab it from GitHub.
|
||||
if(!version.name){
|
||||
version.name = (
|
||||
await axios.get("https://api.github.com/repos/liyasthomas/postwoman/releases")
|
||||
// If we can't get it from GitHub, we'll resort to getting it from package.json
|
||||
.catch(
|
||||
(ex) => ({ data: [{ 'tag_name': require('./package.json').version }] })
|
||||
)
|
||||
).data[0]['tag_name'];
|
||||
}
|
||||
// Get the current version hash as the short hash from Git.
|
||||
version.hash = runCommand("git", ["rev-parse", "--short", "HEAD"]);
|
||||
// Get the 'variant' name as the branch, if it's not master.
|
||||
version.variant =
|
||||
process.env.TRAVIS_BRANCH ||
|
||||
runCommand("git", ["branch"])
|
||||
.split("* ")[1]
|
||||
.split(" ")[0] + (IS_DEV_MODE ? " - DEV MODE" : "");
|
||||
if (["", "master"].includes(version.variant))
|
||||
delete version.variant;
|
||||
|
||||
// Get the current version hash as the short hash from Git.
|
||||
version.hash = runCommand('git', ['rev-parse', '--short', 'HEAD']);
|
||||
// Get the 'variant' name as the branch, if it's not master.
|
||||
version.variant = (process.env.TRAVIS_BRANCH || runCommand('git', ['branch']).split("* ")[1].split(" ")[0] + (IS_DEV_MODE ? " - DEV MODE" : ""));
|
||||
if(version.variant === "" || version.variant === "master") delete version.variant;
|
||||
|
||||
// Write version data into a file
|
||||
fs.writeFileSync(PW_BUILD_DATA_DIR + "/version.json", JSON.stringify(version));
|
||||
})();
|
||||
|
||||
}catch(ex){
|
||||
console.error(ex);
|
||||
process.exit(FAIL_ON_ERROR ? 1 : 0);
|
||||
// Write version data into a file
|
||||
fs.writeFileSync(
|
||||
PW_BUILD_DATA_DIR + "/version.json",
|
||||
JSON.stringify(version)
|
||||
);
|
||||
})();
|
||||
} catch (ex) {
|
||||
console.error(ex);
|
||||
process.exit(FAIL_ON_ERROR ? 1 : 0);
|
||||
}
|
||||
|
||||
@@ -1,190 +1,204 @@
|
||||
<template>
|
||||
<div class="autocomplete-wrapper">
|
||||
<label>
|
||||
<slot />
|
||||
<input type="text"
|
||||
:placeholder="placeholder"
|
||||
v-model="value"
|
||||
@input="updateSuggestions"
|
||||
@keyup="updateSuggestions"
|
||||
@click="updateSuggestions"
|
||||
@keydown="handleKeystroke"
|
||||
ref="acInput"
|
||||
:spellcheck="spellcheck"
|
||||
:autocapitalize="spellcheck"
|
||||
:autocorrect="spellcheck">
|
||||
|
||||
<ul class="suggestions" v-if="suggestions.length > 0 && suggestionsVisible" :style="{ transform: `translate(${suggestionsOffsetLeft}px, 0)` }">
|
||||
<li v-for="(suggestion, index) in suggestions" @click.prevent="forceSuggestion(suggestion)" :class="{ active: currentSuggestionIndex === index }">{{ suggestion }}</li>
|
||||
</ul>
|
||||
</label>
|
||||
</div>
|
||||
<div class="autocomplete-wrapper">
|
||||
<input
|
||||
type="text"
|
||||
:placeholder="placeholder"
|
||||
v-model="value"
|
||||
@input="updateSuggestions"
|
||||
@keyup="updateSuggestions"
|
||||
@click="updateSuggestions"
|
||||
@keydown="handleKeystroke"
|
||||
ref="acInput"
|
||||
:spellcheck="spellcheck"
|
||||
:autocapitalize="spellcheck"
|
||||
:autocorrect="spellcheck"
|
||||
/>
|
||||
<ul
|
||||
class="suggestions"
|
||||
v-if="suggestions.length > 0 && suggestionsVisible"
|
||||
:style="{ transform: `translate(${suggestionsOffsetLeft}px, 0)` }"
|
||||
>
|
||||
<li
|
||||
v-for="(suggestion, index) in suggestions"
|
||||
@click.prevent="forceSuggestion(suggestion)"
|
||||
:class="{ active: currentSuggestionIndex === index }"
|
||||
:key="index"
|
||||
>{{ suggestion }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.autocomplete-wrapper {
|
||||
position: relative;
|
||||
|
||||
position: relative;
|
||||
input:focus + ul.suggestions,
|
||||
ul.suggestions:hover {
|
||||
display: block;
|
||||
}
|
||||
|
||||
input:focus + ul.suggestions, ul.suggestions:hover {
|
||||
display: block;
|
||||
}
|
||||
ul.suggestions {
|
||||
display: none;
|
||||
background-color: var(--atc-color);
|
||||
position: absolute;
|
||||
top: calc(100% - 4px);
|
||||
margin: 0 4px;
|
||||
left: 0;
|
||||
padding: 0;
|
||||
border-radius: 0 0 4px 4px;
|
||||
z-index: 9999;
|
||||
transition: transform 200ms ease-out;
|
||||
|
||||
ul.suggestions {
|
||||
display: none;
|
||||
background-color: var(--atc-color);
|
||||
position: absolute;
|
||||
top: 90%;
|
||||
margin: 0 4px;
|
||||
left: 0;
|
||||
li {
|
||||
width: 100%;
|
||||
display: block;
|
||||
padding: 8px 16px;
|
||||
font-size: 18px;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
white-space: pre-wrap;
|
||||
|
||||
padding: 0;
|
||||
border-radius: 0 0 4px 4px;
|
||||
z-index: 9999;
|
||||
transition: transform 200ms ease-out;
|
||||
|
||||
li {
|
||||
width: 100%;
|
||||
display: block;
|
||||
margin: 5px 0;
|
||||
padding: 10px 10px;
|
||||
font-weight: 700;
|
||||
font-size: 18px;
|
||||
font-family: monospace;
|
||||
white-space: pre-wrap;
|
||||
|
||||
&:hover, &.active {
|
||||
background-color: var(--ac-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
&:last-child {
|
||||
border-radius: 0 0 4px 4px;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&.active {
|
||||
background-color: var(--ac-color);
|
||||
color: var(--act-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
const KEY_TAB = 9;
|
||||
const KEY_ESC = 27;
|
||||
const KEY_TAB = 9;
|
||||
const KEY_ESC = 27;
|
||||
|
||||
const KEY_ARROW_UP = 38;
|
||||
const KEY_ARROW_DOWN = 40;
|
||||
const KEY_ARROW_UP = 38;
|
||||
const KEY_ARROW_DOWN = 40;
|
||||
|
||||
export default {
|
||||
props: {
|
||||
spellcheck: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
required: false
|
||||
},
|
||||
export default {
|
||||
props: {
|
||||
spellcheck: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
required: false
|
||||
},
|
||||
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: 'Start typing...',
|
||||
required: false
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "Start typing...",
|
||||
required: false
|
||||
},
|
||||
|
||||
source: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
source: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
value: {}
|
||||
},
|
||||
watch: {
|
||||
value() {
|
||||
this.$emit("input", this.value);
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
value () {
|
||||
this.$emit('input', this.value);
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
value: "application/json",
|
||||
selectionStart: 0,
|
||||
suggestionsOffsetLeft: 0,
|
||||
currentSuggestionIndex: -1,
|
||||
suggestionsVisible: false
|
||||
};
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
value: "",
|
||||
|
||||
selectionStart: 0,
|
||||
suggestionsOffsetLeft: 0,
|
||||
currentSuggestionIndex: -1,
|
||||
suggestionsVisible: false
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
updateSuggestions (event) {
|
||||
// Hide suggestions if ESC pressed.
|
||||
if(event.which && event.which === KEY_ESC){
|
||||
event.preventDefault();
|
||||
this.suggestionsVisible = false;
|
||||
this.currentSuggestionIndex = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
// As suggestions is a reactive property, this implicitly
|
||||
// causes suggestions to update.
|
||||
this.selectionStart = this.$refs.acInput.selectionStart;
|
||||
this.suggestionsOffsetLeft = (12 * this.selectionStart);
|
||||
this.suggestionsVisible = true;
|
||||
},
|
||||
|
||||
forceSuggestion (text) {
|
||||
let input = this.value.substring(0, this.selectionStart);
|
||||
this.value = input + text;
|
||||
|
||||
this.selectionStart = this.value.length;
|
||||
this.suggestionsVisible = true;
|
||||
this.currentSuggestionIndex = -1;
|
||||
},
|
||||
|
||||
handleKeystroke (event) {
|
||||
if(event.which === KEY_ARROW_UP){
|
||||
event.preventDefault();
|
||||
|
||||
this.currentSuggestionIndex = this.currentSuggestionIndex - 1 >= 0
|
||||
? this.currentSuggestionIndex - 1
|
||||
: 0;
|
||||
}else if(event.which === KEY_ARROW_DOWN){
|
||||
event.preventDefault();
|
||||
|
||||
this.currentSuggestionIndex = this.currentSuggestionIndex < this.suggestions.length - 1
|
||||
? this.currentSuggestionIndex + 1
|
||||
: this.suggestions.length - 1;
|
||||
}
|
||||
|
||||
if(event.which === KEY_TAB){
|
||||
event.preventDefault();
|
||||
|
||||
let activeSuggestion = this.suggestions[this.currentSuggestionIndex >= 0 ? this.currentSuggestionIndex : 0];
|
||||
if(activeSuggestion){
|
||||
let input = this.value.substring(0, this.selectionStart);
|
||||
this.value = input + activeSuggestion;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* Gets the suggestions list to be displayed under the input box.
|
||||
*
|
||||
* @returns {default.props.source|{type, required}}
|
||||
*/
|
||||
suggestions () {
|
||||
let input = this.value.substring(0, this.selectionStart);
|
||||
|
||||
return this.source.filter((entry) => {
|
||||
return entry.toLowerCase().startsWith(input.toLowerCase())
|
||||
&& input.toLowerCase() !== entry.toLowerCase();
|
||||
})
|
||||
// Cut off the part that's already been typed.
|
||||
.map((entry) => entry.substring(this.selectionStart))
|
||||
// We only want the top 3 suggestions.
|
||||
.slice(0, 3);
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
this.updateSuggestions({
|
||||
target: this.$refs.acInput
|
||||
});
|
||||
methods: {
|
||||
updateSuggestions(event) {
|
||||
// Hide suggestions if ESC pressed.
|
||||
if (event.which && event.which === KEY_ESC) {
|
||||
event.preventDefault();
|
||||
this.suggestionsVisible = false;
|
||||
this.currentSuggestionIndex = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
// As suggestions is a reactive property, this implicitly
|
||||
// causes suggestions to update.
|
||||
this.selectionStart = this.$refs.acInput.selectionStart;
|
||||
this.suggestionsOffsetLeft = 12 * this.selectionStart;
|
||||
this.suggestionsVisible = true;
|
||||
},
|
||||
|
||||
forceSuggestion(text) {
|
||||
let input = this.value.substring(0, this.selectionStart);
|
||||
this.value = input + text;
|
||||
|
||||
this.selectionStart = this.value.length;
|
||||
this.suggestionsVisible = true;
|
||||
this.currentSuggestionIndex = -1;
|
||||
},
|
||||
|
||||
handleKeystroke(event) {
|
||||
switch (event.which) {
|
||||
|
||||
case KEY_ARROW_UP:
|
||||
event.preventDefault();
|
||||
this.currentSuggestionIndex =this.currentSuggestionIndex - 1 >= 0 ? this.currentSuggestionIndex - 1 : 0;
|
||||
break;
|
||||
|
||||
case KEY_ARROW_DOWN:
|
||||
event.preventDefault();
|
||||
this.currentSuggestionIndex = this.currentSuggestionIndex < this.suggestions.length - 1 ? this.currentSuggestionIndex + 1
|
||||
: this.suggestions.length - 1;
|
||||
break;
|
||||
|
||||
case KEY_TAB:
|
||||
event.preventDefault();
|
||||
let activeSuggestion = this.suggestions[this.currentSuggestionIndex >= 0 ? this.currentSuggestionIndex : 0];
|
||||
if (activeSuggestion) {
|
||||
let input = this.value.substring(0, this.selectionStart);
|
||||
this.value = input + activeSuggestion;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* Gets the suggestions list to be displayed under the input box.
|
||||
*
|
||||
* @returns {default.props.source|{type, required}}
|
||||
*/
|
||||
suggestions() {
|
||||
let input = this.value.substring(0, this.selectionStart);
|
||||
|
||||
return (
|
||||
this.source
|
||||
.filter(entry => {
|
||||
return (
|
||||
entry.toLowerCase().startsWith(input.toLowerCase()) &&
|
||||
input.toLowerCase() !== entry.toLowerCase()
|
||||
);
|
||||
})
|
||||
// Cut off the part that's already been typed.
|
||||
.map(entry => entry.substring(this.selectionStart))
|
||||
// We only want the top 3 suggestions.
|
||||
.slice(0, 3)
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.updateSuggestions({
|
||||
target: this.$refs.acInput
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -2,41 +2,56 @@
|
||||
<pw-section class="gray" label="History">
|
||||
<ul>
|
||||
<li id="filter-history">
|
||||
<label for="filter-history-input">Search History</label>
|
||||
<input id="filter-history-input" type="text" :disabled="history.length === 0 || isClearingHistory" v-model="filterText">
|
||||
<input aria-label="Search" type="text" placeholder="search history" :readonly="history.length === 0" v-model="filterText">
|
||||
</li>
|
||||
</ul>
|
||||
<virtual-list class="virtual-list" :class="{filled: filteredHistory.length}" :size="89" :remain="Math.min(5, filteredHistory.length)">
|
||||
<ul v-for="entry in filteredHistory" :key="entry.time" class="entry">
|
||||
<ul>
|
||||
<li @click="sort_by_label()">
|
||||
<label for="" class="flex-wrap">Label<i class="material-icons">sort</i></label>
|
||||
</li>
|
||||
<li @click="sort_by_time()">
|
||||
<label for="" class="flex-wrap">Time<i class="material-icons">sort</i></label>
|
||||
</li>
|
||||
<li @click="sort_by_status_code()">
|
||||
<label for="" class="flex-wrap">Status<i class="material-icons">sort</i></label>
|
||||
</li>
|
||||
<li @click="sort_by_url()">
|
||||
<label for="" class="flex-wrap">URL<i class="material-icons">sort</i></label>
|
||||
</li>
|
||||
<li @click="sort_by_path()">
|
||||
<label for="" class="flex-wrap">Path<i class="material-icons">sort</i></label>
|
||||
</li>
|
||||
</ul>
|
||||
<virtual-list class="virtual-list" :class="{filled: filteredHistory.length}" :size="54" :remain="Math.min(5, filteredHistory.length)">
|
||||
<ul v-for="(entry, index) in filteredHistory" :key="index" class="entry">
|
||||
<li>
|
||||
<label :for="'time#' + entry.time">Time</label>
|
||||
<input :id="'time#' + entry.time" type="text" readonly :value="entry.time" :title="entry.date">
|
||||
<input aria-label="Label" type="text" readonly :value="entry.label" placeholder="No label">
|
||||
</li>
|
||||
<li>
|
||||
<input aria-label="Time" type="text" readonly :value="entry.time" :title="entry.date">
|
||||
</li>
|
||||
<li class="method-list-item">
|
||||
<label :for="'time#' + entry.time">Method</label>
|
||||
<input :id="'method#' + entry.time" type="text" readonly :value="entry.method" :class="findEntryStatus(entry).className" :style="{'--status-code': entry.status}">
|
||||
<span class="entry-status-code">{{entry.status}}</span>
|
||||
<input aria-label="Method" type="text" readonly :value="entry.method" :class="findEntryStatus(entry).className" :style="{'--status-code': entry.status}">
|
||||
<span class="entry-status-code" :class="findEntryStatus(entry).className" :style="{'--status-code': entry.status}">{{entry.status}}</span>
|
||||
</li>
|
||||
<li>
|
||||
<label :for="'url#' + entry.time">URL</label>
|
||||
<input :id="'url#' + entry.time" type="text" readonly :value="entry.url">
|
||||
<input aria-label="URL" type="text" readonly :value="entry.url">
|
||||
</li>
|
||||
<li>
|
||||
<label :for="'path#' + entry.time">Path</label>
|
||||
<input :id="'path#' + entry.time" type="text" readonly :value="entry.path">
|
||||
</li>
|
||||
<li>
|
||||
<label :for="'delete-button#' + entry.time" class="hide-on-small-screen"> </label>
|
||||
<button :id="'delete-button#' + entry.time" :disabled="isClearingHistory" @click="deleteHistory(entry)">
|
||||
Delete
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<label :for="'use-button#' + entry.time" class="hide-on-small-screen"> </label>
|
||||
<button :id="'use-button#' + entry.time" :disabled="isClearingHistory" @click="useHistory(entry)">
|
||||
Use
|
||||
</button>
|
||||
<input aria-label="Path" type="text" readonly :value="entry.path" placeholder="No path">
|
||||
</li>
|
||||
<div class="show-on-small-screen">
|
||||
<li>
|
||||
<button v-tooltip="'Delete'" class="icon" :id="'delete-button#'+index" @click="deleteHistory(entry)" aria-label="Delete">
|
||||
<i class="material-icons">delete</i>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<button v-tooltip="'Edit'" class="icon" :id="'use-button#'+index" @click="useHistory(entry)" aria-label="Edit">
|
||||
<i class="material-icons">edit</i>
|
||||
</button>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
</virtual-list>
|
||||
<ul :class="{hidden: filteredHistory.length != 0 || history.length === 0 }">
|
||||
@@ -44,20 +59,26 @@
|
||||
<label>Nothing found for "{{filterText}}"</label>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<ul v-if="history.length === 0">
|
||||
<li>
|
||||
<label>History is empty</label>
|
||||
</li>
|
||||
</ul>
|
||||
<ul v-if="history.length !== 0">
|
||||
<li v-if="!isClearingHistory">
|
||||
<button id="clear-history-button" :disabled="history.length === 0" @click="enableHistoryClearing">
|
||||
Clear History
|
||||
<button class="icon" id="clear-history-button" :disabled="history.length === 0" @click="enableHistoryClearing">
|
||||
<i class="material-icons">clear_all</i>
|
||||
<span>Clear All</span>
|
||||
</button>
|
||||
</li>
|
||||
<li v-else>
|
||||
<div class="flex-wrap">
|
||||
<label for="clear-history-button">Are you sure?</label>
|
||||
<div>
|
||||
<button id="confirm-clear-history-button" @click="clearHistory">
|
||||
<button class="icon" id="confirm-clear-history-button" @click="clearHistory">
|
||||
Yes
|
||||
</button>
|
||||
<button id="reject-clear-history-button" @click="disableHistoryClearing">
|
||||
<button class="icon" id="reject-clear-history-button" @click="disableHistoryClearing">
|
||||
No
|
||||
</button>
|
||||
</div>
|
||||
@@ -85,7 +106,12 @@
|
||||
history: localStorageHistory || [],
|
||||
filterText: '',
|
||||
showFilter: false,
|
||||
isClearingHistory: false
|
||||
isClearingHistory: false,
|
||||
reverse_sort_label: false,
|
||||
reverse_sort_time: false,
|
||||
reverse_sort_status_code: false,
|
||||
reverse_sort_url: false,
|
||||
reverse_sort_path: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -106,6 +132,9 @@
|
||||
this.filterText = '';
|
||||
this.disableHistoryClearing();
|
||||
updateOnLocalStorage('history', this.history);
|
||||
this.$toast.error('History Deleted', {
|
||||
icon: 'delete'
|
||||
});
|
||||
},
|
||||
useHistory(entry) {
|
||||
this.$emit('useHistory', entry);
|
||||
@@ -122,6 +151,9 @@
|
||||
this.filterText = '';
|
||||
}
|
||||
updateOnLocalStorage('history', this.history);
|
||||
this.$toast.error('Deleted', {
|
||||
icon: 'delete'
|
||||
});
|
||||
},
|
||||
addEntry(entry) {
|
||||
this.history.push(entry);
|
||||
@@ -133,6 +165,67 @@
|
||||
},
|
||||
disableHistoryClearing() {
|
||||
this.isClearingHistory = false;
|
||||
},
|
||||
sort_by_time() {
|
||||
let byDate = this.history.slice(0);
|
||||
byDate.sort((a,b) =>{
|
||||
let date_a = a.date.split("/");
|
||||
let date_b = b.date.split("/");
|
||||
let time_a = a.time.split(":")
|
||||
let time_b = b.time.split(":")
|
||||
let final_a = new Date(date_a[2], date_a[1], date_a[0], time_a[0], time_a[1], time_a[2]);
|
||||
let final_b = new Date(date_b[2], date_b[1], date_b[0], time_b[0], time_b[1], time_b[2]);
|
||||
if(this.reverse_sort_time)
|
||||
return final_b - final_a;
|
||||
else
|
||||
return final_a - final_b;
|
||||
})
|
||||
this.history = byDate;
|
||||
this.reverse_sort_time = !this.reverse_sort_time;
|
||||
},
|
||||
sort_by_status_code() {
|
||||
let byCode = this.history.slice(0);
|
||||
byCode.sort((a,b) =>{
|
||||
if(this.reverse_sort_status_code)
|
||||
return b.status - a.status;
|
||||
else
|
||||
return a.status - b.status;
|
||||
})
|
||||
this.history = byCode;
|
||||
this.reverse_sort_status_code = !this.reverse_sort_status_code;
|
||||
},
|
||||
sort_by_url() {
|
||||
let byUrl = this.history.slice(0);
|
||||
byUrl.sort((a, b)=>{
|
||||
if(this.reverse_sort_url)
|
||||
return a.url == b.url ? 0 : +(a.url < b.url) || -1;
|
||||
else
|
||||
return a.url == b.url ? 0 : +(a.url > b.url) || -1;
|
||||
});
|
||||
this.history = byUrl;
|
||||
this.reverse_sort_url = !this.reverse_sort_url;
|
||||
},
|
||||
sort_by_label() {
|
||||
let byLabel = this.history.slice(0);
|
||||
byLabel.sort((a, b)=>{
|
||||
if(this.reverse_sort_label)
|
||||
return a.label == b.label ? 0 : +(a.label < b.label) || -1;
|
||||
else
|
||||
return a.label == b.label ? 0 : +(a.label > b.label) || -1;
|
||||
});
|
||||
this.history = byLabel;
|
||||
this.reverse_sort_label = !this.reverse_sort_label;
|
||||
},
|
||||
sort_by_path() {
|
||||
let byPath = this.history.slice(0);
|
||||
byPath.sort((a, b)=>{
|
||||
if(this.reverse_sort_path)
|
||||
return a.path == b.path ? 0 : +(a.path < b.path) || -1;
|
||||
else
|
||||
return a.path == b.path ? 0 : +(a.path > b.path) || -1;
|
||||
});
|
||||
this.history = byPath;
|
||||
this.reverse_sort_path = !this.reverse_sort_path;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -147,7 +240,7 @@
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.virtual-list.filled {
|
||||
min-height: 430px;
|
||||
min-height: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,29 +1,32 @@
|
||||
<template>
|
||||
<svg version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 612.001 612.001" style="enable-background:new 0 0 612.001 612.001;" xml:space="preserve">
|
||||
<defs id="defs11" />
|
||||
<g id="g3826" transform="translate(-516.40798,-163.88978)">
|
||||
<circle transform="scale(1,-1)" style="stroke-width:1.19531453" r="178.70923" cy="-501.55591" cx="822.40845" id="circle3814" />
|
||||
<g id="g3820" transform="translate(516.40798,163.89028)">
|
||||
<g id="g3818">
|
||||
<path :fill="color" id="path3816" data-old_color="#121212" class="active-path" data-original="#121212" d="M 64.601,236.822 C 64.601,394.256 192.786,612 306.001,612 412.582,612 547.4,394.256 547.4,236.822 547.4,79.388 439.322,0 306,0 172.678,0 64.601,79.388 64.601,236.822 Z m 304.12,116.415 c 29.475,-29.475 70.598,-40.195 108.552,-32.173 8.021,37.954 -2.698,79.077 -32.173,108.552 -29.475,29.475 -70.598,40.195 -108.552,32.173 -8.022,-37.955 2.698,-79.078 32.173,-108.552 z M 134.727,321.063 c 37.954,-8.021 79.077,2.698 108.552,32.173 29.475,29.475 40.195,70.598 32.173,108.552 -37.954,8.021 -79.077,-2.698 -108.552,-32.173 -29.475,-29.476 -40.194,-70.598 -32.173,-108.552 z" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
<svg version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 612.001 612.001" style="enable-background:new 0 0 612.001 612.001;" xml:space="preserve">
|
||||
<defs id="defs11" />
|
||||
<g id="g3826" transform="translate(-516.40798,-163.88978)">
|
||||
<circle :fill="color" transform="scale(1,-1)" style="stroke-width:1.19531453" r="178.70923" cy="-501.55591" cx="822.40845" id="circle3814" />
|
||||
<g id="g3820" transform="translate(516.40798,163.89028)">
|
||||
<g id="g3818">
|
||||
<path :fill="color" id="path3816" data-old_color="#121212" class="active-path" data-original="#121212" d="M 64.601,236.822 C 64.601,394.256 192.786,612 306.001,612 412.582,612 547.4,394.256 547.4,236.822 547.4,79.388 439.322,0 306,0 172.678,0 64.601,79.388 64.601,236.822 Z m 304.12,116.415 c 29.475,-29.475 70.598,-40.195 108.552,-32.173 8.021,37.954 -2.698,79.077 -32.173,108.552 -29.475,29.475 -70.598,40.195 -108.552,32.173 -8.022,-37.955 2.698,-79.078 32.173,-108.552 z M 134.727,321.063 c 37.954,-8.021 79.077,2.698 108.552,32.173 29.475,29.475 40.195,70.598 32.173,108.552 -37.954,8.021 -79.077,-2.698 -108.552,-32.173 -29.475,-29.476 -40.194,-70.598 -32.173,-108.552 z" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
#path3816 {
|
||||
fill: var(--ac-color);
|
||||
}
|
||||
#circle3814 {
|
||||
fill: var(--bg-color);
|
||||
}
|
||||
#path3816 {
|
||||
fill: var(--ac-color);
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
'color': {
|
||||
type: String
|
||||
}
|
||||
}
|
||||
export default {
|
||||
props: {
|
||||
'color': {
|
||||
type: String
|
||||
}
|
||||
}
|
||||
</script>
|
||||
}
|
||||
</script>
|
||||
|
||||
74
components/modal.vue
Normal file
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<transition name="modal-fade">
|
||||
<div class="modal-backdrop">
|
||||
<div class="modal-wrapper">
|
||||
<div class="modal-container">
|
||||
<div class="modal-header">
|
||||
<slot name="header"></slot>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<slot name="body"></slot>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<slot name="footer"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
<style scoped>
|
||||
.modal-backdrop {
|
||||
position: fixed;
|
||||
z-index: 998;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.modal-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-grow: 1;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.modal-container {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
flex-direction: column;
|
||||
margin: 8px;
|
||||
padding: 12px;
|
||||
transition: all 0.2s ease;
|
||||
background-color: var(--bg-color);
|
||||
border-radius: 8px;
|
||||
box-shadow: rgba(0, 0, 0, 0.5) 0px 16px 70px;
|
||||
}
|
||||
|
||||
/*
|
||||
* The following styles are auto-applied to elements with
|
||||
* transition="modal" when their visibility is toggled
|
||||
* by Vue.js.
|
||||
*
|
||||
* You can easily play with the modal transition by editing
|
||||
* these styles.
|
||||
*/
|
||||
|
||||
.modal-fade-enter,
|
||||
.modal-fade-leave-active {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.modal-fade-enter .modal-container,
|
||||
.modal-fade-leave-active .modal-container {
|
||||
transform: scale(0.9);
|
||||
transition: all 0.1s ease-in-out;
|
||||
}
|
||||
</style>
|
||||
@@ -1,47 +1,48 @@
|
||||
<template>
|
||||
<fieldset :id="label.toLowerCase()" :class="{ 'no-colored-frames': noFrameColors }">
|
||||
<legend @click.prevent="collapse">{{ label }} ↕</legend>
|
||||
<div class="collapsible" :class="{ hidden: collapsed }">
|
||||
<slot />
|
||||
</div>
|
||||
</fieldset>
|
||||
<fieldset :id="label.toLowerCase()" :class="{ 'no-colored-frames': noFrameColors }">
|
||||
<legend @click.prevent="collapse"><span>{{ label }}</span><i class="material-icons" v-if="isCollapsed">expand_more</i><i class="material-icons" v-if="!isCollapsed">expand_less</i></legend>
|
||||
<div class="collapsible" :class="{ hidden: collapsed }">
|
||||
<slot />
|
||||
</div>
|
||||
</fieldset>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
fieldset.no-colored-frames {
|
||||
border-color: #afafaf !important;
|
||||
}
|
||||
|
||||
fieldset.no-colored-frames legend {
|
||||
color: var(--ac-color);
|
||||
}
|
||||
fieldset.no-colored-frames legend {
|
||||
color: var(--fg-color);
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
|
||||
computed: {
|
||||
noFrameColors () {
|
||||
return this.$store.state.postwoman.settings.DISABLE_FRAME_COLORS || false;
|
||||
}
|
||||
},
|
||||
|
||||
props: {
|
||||
"label": {
|
||||
type: String,
|
||||
default: "Section"
|
||||
},
|
||||
"collapsed": {
|
||||
type: Boolean
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
collapse({ target }) {
|
||||
const parent = target.parentNode;
|
||||
parent.querySelector(".collapsible").classList.toggle('hidden');
|
||||
},
|
||||
}
|
||||
export default {
|
||||
computed: {
|
||||
noFrameColors() {
|
||||
return this.$store.state.postwoman.settings.DISABLE_FRAME_COLORS || false;
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
isCollapsed: false
|
||||
}
|
||||
},
|
||||
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
default: "Section"
|
||||
},
|
||||
collapsed: {
|
||||
type: Boolean
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
collapse({ target }) {
|
||||
const parent = target.parentNode.parentNode;
|
||||
parent.querySelector(".collapsible").classList.toggle("hidden");
|
||||
this.isCollapsed = !this.isCollapsed;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,68 +1,68 @@
|
||||
<template>
|
||||
<div class="color" :data-color="color">
|
||||
<span :style="{backgroundColor: color}" class="preview">
|
||||
<svg v-if="active" class="activeTick" width="24" height="24" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd"><path d="M21 6.285l-11.16 12.733-6.84-6.018 1.319-1.49 5.341 4.686 9.865-11.196 1.475 1.285z"/></svg>
|
||||
</span>
|
||||
{{ name || color }}
|
||||
</div>
|
||||
<div class="color" :data-color="color">
|
||||
<span :style="{backgroundColor: color}" class="preview">
|
||||
<i v-if="active" class="material-icons activeTick">done</i>
|
||||
</span>
|
||||
{{ name || color }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.color {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
.color {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 16px 0 4px;
|
||||
margin: 4px;
|
||||
background-color: var(--bg-dark-color);
|
||||
color: var(--fg-color);
|
||||
border-radius: 20px;
|
||||
cursor: pointer;
|
||||
height: 40px;
|
||||
|
||||
padding: 8px 16px;
|
||||
margin: 4px;
|
||||
background-color: rgba(93, 93, 93, 0.2);
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
|
||||
&.active {
|
||||
background-color: rgba(93, 93, 93, 0.3);
|
||||
}
|
||||
|
||||
.preview {
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 100%;
|
||||
margin-right: 8px;
|
||||
|
||||
position: relative;
|
||||
.activeTick {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
fill: white;
|
||||
}
|
||||
}
|
||||
&.active {
|
||||
background-color: var(--bg-dark-color);
|
||||
}
|
||||
|
||||
.color.vibrant {
|
||||
.preview .activeTick {
|
||||
fill: black;
|
||||
}
|
||||
.preview {
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
border-radius: 100%;
|
||||
margin-right: 8px;
|
||||
padding: 16px;
|
||||
position: relative;
|
||||
|
||||
.activeTick {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.color.vibrant {
|
||||
.preview .activeTick {
|
||||
color: var(--act-color);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
'color': {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
'name': {
|
||||
type: String
|
||||
},
|
||||
|
||||
'active': {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
export default {
|
||||
props: {
|
||||
'color': {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
'name': {
|
||||
type: String
|
||||
},
|
||||
'active': {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -4,32 +4,34 @@
|
||||
<span class="handle"></span>
|
||||
</label>
|
||||
<label class="caption">
|
||||
<slot /></label>
|
||||
<slot/>
|
||||
</label>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$useBorder: true;
|
||||
$borderColor: var(--fg-color);
|
||||
$useBorder: false;
|
||||
$borderColor: var(--fg-light-color);
|
||||
$activeColor: var(--ac-color);
|
||||
$inactiveColor: var(--fg-color);
|
||||
$inactiveColor: var(--fg-light-color);
|
||||
|
||||
$inactiveHandleColor: $inactiveColor;
|
||||
$inactiveHandleColor: var(--bg-color);
|
||||
$activeHandleColor: var(--act-color);
|
||||
|
||||
$width: 50px;
|
||||
$height: 20px;
|
||||
$width: 32px;
|
||||
$height: 16px;
|
||||
$handleSpacing: 4px;
|
||||
|
||||
$transition: all 0.2s ease-in-out;
|
||||
|
||||
div {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
label.caption {
|
||||
margin-left: 4px;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
label.toggle {
|
||||
@@ -41,11 +43,12 @@
|
||||
background-color: if($useBorder, transparent, $inactiveColor);
|
||||
vertical-align: middle;
|
||||
|
||||
border-radius: 100px;
|
||||
border-radius: 32px;
|
||||
transition: $transition;
|
||||
box-sizing: initial;
|
||||
padding: 0;
|
||||
margin: 10px 5px;
|
||||
margin: 8px 4px;
|
||||
cursor: pointer;
|
||||
|
||||
.handle {
|
||||
position: absolute;
|
||||
@@ -62,8 +65,6 @@
|
||||
|
||||
pointer-events: none;
|
||||
transition: $transition;
|
||||
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
|
||||
}
|
||||
|
||||
&.on {
|
||||
|
||||
9
cypress.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"baseUrl": "http://localhost:3000",
|
||||
"integrationFolder": "tests/e2e/integration",
|
||||
"screenshotsFolder": "tests/e2e/screenshots",
|
||||
"fixturesFolder": "tests/e2e/fixtures",
|
||||
"supportFile": "tests/e2e/support",
|
||||
"pluginsFile": false,
|
||||
"video": false
|
||||
}
|
||||
7
database.rules.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
/* Visit https://firebase.google.com/docs/database/security to learn more about security rules. */
|
||||
"rules": {
|
||||
".read": false,
|
||||
".write": false
|
||||
}
|
||||
}
|
||||
19
docker-compose.yml
Normal file
@@ -0,0 +1,19 @@
|
||||
version: "3.7"
|
||||
|
||||
services:
|
||||
web:
|
||||
build:
|
||||
context: .
|
||||
volumes:
|
||||
- "./.postwoman:/app/.postwoman"
|
||||
- "./assets:/app/assets"
|
||||
- "./directives:/app/directives"
|
||||
- "./layouts:/app/layouts"
|
||||
- "./middleware:/app/middleware"
|
||||
- "./pages:/app/pages"
|
||||
- "./plugins:/app/plugins"
|
||||
- "./static:/app/static"
|
||||
- "./store:/app/store"
|
||||
ports:
|
||||
- "3000:3000"
|
||||
command: "npm run dev"
|
||||
22
firebase.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"database": {
|
||||
"rules": "database.rules.json"
|
||||
},
|
||||
"firestore": {
|
||||
"rules": "firestore.rules",
|
||||
"indexes": "firestore.indexes.json"
|
||||
},
|
||||
"hosting": {
|
||||
"target": "postwoman",
|
||||
"public": "dist",
|
||||
"cleanUrls": true,
|
||||
"ignore": [
|
||||
"firebase.json",
|
||||
"**/.*",
|
||||
"**/node_modules/**"
|
||||
]
|
||||
},
|
||||
"storage": {
|
||||
"rules": "storage.rules"
|
||||
}
|
||||
}
|
||||
26
firestore.indexes.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
// Example:
|
||||
//
|
||||
// "indexes": [
|
||||
// {
|
||||
// "collectionGroup": "widgets",
|
||||
// "queryScope": "COLLECTION",
|
||||
// "fields": [
|
||||
// { "fieldPath": "foo", "arrayConfig": "CONTAINS" },
|
||||
// { "fieldPath": "bar", "mode": "DESCENDING" }
|
||||
// ]
|
||||
// },
|
||||
//
|
||||
// "fieldOverrides": [
|
||||
// {
|
||||
// "collectionGroup": "widgets",
|
||||
// "fieldPath": "baz",
|
||||
// "indexes": [
|
||||
// { "order": "ASCENDING", "queryScope": "COLLECTION" }
|
||||
// ]
|
||||
// },
|
||||
// ]
|
||||
// ]
|
||||
"indexes": [],
|
||||
"fieldOverrides": []
|
||||
}
|
||||
7
firestore.rules
Normal file
@@ -0,0 +1,7 @@
|
||||
service cloud.firestore {
|
||||
match /databases/{database}/documents {
|
||||
match /{document=**} {
|
||||
allow read, write;
|
||||
}
|
||||
}
|
||||
}
|
||||
1
functions/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
8
functions/index.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const functions = require('firebase-functions');
|
||||
|
||||
// // Create and Deploy Your First Cloud Functions
|
||||
// // https://firebase.google.com/docs/functions/write-firebase-functions
|
||||
//
|
||||
// exports.helloWorld = functions.https.onRequest((request, response) => {
|
||||
// response.send("Hello from Firebase!");
|
||||
// });
|
||||
1915
functions/package-lock.json
generated
Normal file
22
functions/package.json
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "functions",
|
||||
"description": "Cloud Functions for Firebase",
|
||||
"scripts": {
|
||||
"serve": "firebase serve --only functions",
|
||||
"shell": "firebase functions:shell",
|
||||
"start": "npm run shell",
|
||||
"deploy": "firebase deploy --only functions",
|
||||
"logs": "firebase functions:log"
|
||||
},
|
||||
"engines": {
|
||||
"node": "8"
|
||||
},
|
||||
"dependencies": {
|
||||
"firebase-admin": "^8.0.0",
|
||||
"firebase-functions": "^3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"firebase-functions-test": "^0.1.6"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
@@ -4,18 +4,17 @@
|
||||
<div>
|
||||
<div class="slide-in">
|
||||
<nuxt-link to="/">
|
||||
<h1 class="logo">
|
||||
<logo alt="" style="height: 24px; margin-right: 16px" />Postwoman</h1>
|
||||
<h1 class="logo"><logo alt="" style="height: 24px; margin-right: 16px"></logo>Postwoman</h1>
|
||||
</nuxt-link>
|
||||
<h3>API request builder</h3>
|
||||
</div>
|
||||
<nav>
|
||||
<nuxt-link to="/">HTTP</nuxt-link>
|
||||
<nuxt-link to="/websocket">WebSocket</nuxt-link>
|
||||
<nuxt-link to="/settings">
|
||||
<nuxt-link v-tooltip="'Settings'" to="/settings" aria-label="Settings">
|
||||
<!-- Settings cog -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24">
|
||||
<path d="M24 14.187v-4.374c-2.148-.766-2.726-.802-3.027-1.529-.303-.729.083-1.169 1.059-3.223l-3.093-3.093c-2.026.963-2.488 1.364-3.224 1.059-.727-.302-.768-.889-1.527-3.027h-4.375c-.764 2.144-.8 2.725-1.529 3.027-.752.313-1.203-.1-3.223-1.059l-3.093 3.093c.977 2.055 1.362 2.493 1.059 3.224-.302.727-.881.764-3.027 1.528v4.375c2.139.76 2.725.8 3.027 1.528.304.734-.081 1.167-1.059 3.223l3.093 3.093c1.999-.95 2.47-1.373 3.223-1.059.728.302.764.88 1.529 3.027h4.374c.758-2.131.799-2.723 1.537-3.031.745-.308 1.186.099 3.215 1.062l3.093-3.093c-.975-2.05-1.362-2.492-1.059-3.223.3-.726.88-.763 3.027-1.528zm-4.875.764c-.577 1.394-.068 2.458.488 3.578l-1.084 1.084c-1.093-.543-2.161-1.076-3.573-.49-1.396.581-1.79 1.693-2.188 2.877h-1.534c-.398-1.185-.791-2.297-2.183-2.875-1.419-.588-2.507-.045-3.579.488l-1.083-1.084c.557-1.118 1.066-2.18.487-3.58-.579-1.391-1.691-1.784-2.876-2.182v-1.533c1.185-.398 2.297-.791 2.875-2.184.578-1.394.068-2.459-.488-3.579l1.084-1.084c1.082.538 2.162 1.077 3.58.488 1.392-.577 1.785-1.69 2.183-2.875h1.534c.398 1.185.792 2.297 2.184 2.875 1.419.588 2.506.045 3.579-.488l1.084 1.084c-.556 1.121-1.065 2.187-.488 3.58.577 1.391 1.689 1.784 2.875 2.183v1.534c-1.188.398-2.302.791-2.877 2.183zm-7.125-5.951c1.654 0 3 1.346 3 3s-1.346 3-3 3-3-1.346-3-3 1.346-3 3-3zm0-2c-2.762 0-5 2.238-5 5s2.238 5 5 5 5-2.238 5-5-2.238-5-5-5z" />
|
||||
<path d="M24 13.616v-3.232c-1.651-.587-2.694-.752-3.219-2.019v-.001c-.527-1.271.1-2.134.847-3.707l-2.285-2.285c-1.561.742-2.433 1.375-3.707.847h-.001c-1.269-.526-1.435-1.576-2.019-3.219h-3.232c-.582 1.635-.749 2.692-2.019 3.219h-.001c-1.271.528-2.132-.098-3.707-.847l-2.285 2.285c.745 1.568 1.375 2.434.847 3.707-.527 1.271-1.584 1.438-3.219 2.02v3.232c1.632.58 2.692.749 3.219 2.019.53 1.282-.114 2.166-.847 3.707l2.285 2.286c1.562-.743 2.434-1.375 3.707-.847h.001c1.27.526 1.436 1.579 2.019 3.219h3.232c.582-1.636.75-2.69 2.027-3.222h.001c1.262-.524 2.12.101 3.698.851l2.285-2.286c-.744-1.563-1.375-2.433-.848-3.706.527-1.271 1.588-1.44 3.221-2.021zm-12 2.384c-2.209 0-4-1.791-4-4s1.791-4 4-4 4 1.791 4 4-1.791 4-4 4z"/>
|
||||
</svg>
|
||||
</nuxt-link>
|
||||
</nav>
|
||||
@@ -24,21 +23,34 @@
|
||||
<nuxt id="main" />
|
||||
<footer>
|
||||
<!-- Top section of footer: GitHub/install links -->
|
||||
<div>
|
||||
<div>
|
||||
<a href="https://github.com/liyasthomas/postwoman" target="_blank"><img id="imgGitHub" src="~static/icons/github.svg" alt="" :style="logoStyle()">GitHub</a>
|
||||
</div>
|
||||
<button id="installPWA" @click.prevent="showInstallPrompt()">
|
||||
Install PWA
|
||||
<div class="flex-wrap">
|
||||
<a href="https://github.com/liyasthomas/postwoman" target="_blank" rel="noopener">
|
||||
<button class="icon">
|
||||
<img id="imgGitHub" src="~static/icons/github.svg" alt="GitHub" :style="logoStyle()">
|
||||
<span>GitHub</span>
|
||||
</button>
|
||||
</a>
|
||||
<button class="icon" id="installPWA" @click.prevent="showInstallPrompt()">
|
||||
<i class="material-icons">add_to_home_screen</i>
|
||||
<span>Install PWA</span>
|
||||
</button>
|
||||
<button class="icon" onClick="window.open('https://twitter.com/share?text=👽 Postwoman • API request builder - Helps you create your requests faster, saving you precious time on your development&url=https://postwoman.io&hashtags=postwoman&via=liyasthomas');">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24">
|
||||
<path d="M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"/>
|
||||
</svg>
|
||||
<span>Tweet</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Bottom section of footer: version/author information -->
|
||||
<p class="align-center">
|
||||
<span v-if="version.name">{{ version.name }}
|
||||
<span v-if="version.hash">- {{ version.hash }}</span>
|
||||
<span v-if="version.variant"> ({{ version.variant }})</span>
|
||||
• </span>by <a href="https://liyasthomas.web.app" target="_blank">Liyas Thomas 🦄</a>
|
||||
<span v-if="version.name">
|
||||
<a v-bind:href="'https://github.com/liyasthomas/postwoman/releases/tag/' + version.name" target="_blank" rel="noopener">{{version.name}}</a>
|
||||
<span v-if="version.hash">
|
||||
- <a v-bind:href="'https://github.com/liyasthomas/postwoman/commit/' + version.hash" target="_blank" rel="noopener">{{version.hash}}</a>
|
||||
</span>
|
||||
<span v-if="version.variant"> ({{version.variant}})</span>
|
||||
•
|
||||
</span> by <a href="https://liyasthomas.web.app" target="_blank" rel="noopener">Liyas Thomas 🦄</a> • <a href="https://postwoman.launchaco.com" target="_blank" rel="noopener">Subscribe</a>
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
@@ -69,7 +81,7 @@
|
||||
}
|
||||
|
||||
footer {
|
||||
margin: 40px auto;
|
||||
margin: 32px auto;
|
||||
}
|
||||
|
||||
nav {
|
||||
@@ -102,12 +114,12 @@
|
||||
right: 0;
|
||||
z-index: -1;
|
||||
background-color: var(--ac-color);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
&:not(.nuxt-link-exact-active):hover:before {
|
||||
animation: linkHover 0.3s forwards ease-in-out;
|
||||
animation: linkHover 0.2s forwards ease-in-out;
|
||||
}
|
||||
|
||||
@keyframes linkHover {
|
||||
@@ -142,7 +154,7 @@
|
||||
// prompt.
|
||||
showInstallPrompt: null,
|
||||
logoStyle() {
|
||||
return "margin-right: 16px;" + (((this.$store.state.postwoman.settings.THEME_CLASS || '').includes("light")) ? " filter: invert(100%); -webkit-filter: invert(100%);" : '')
|
||||
return (((this.$store.state.postwoman.settings.THEME_CLASS || '').includes("light")) ? " filter: invert(100%); -webkit-filter: invert(100%);" : '')
|
||||
},
|
||||
|
||||
version: {}
|
||||
@@ -158,11 +170,11 @@
|
||||
// Apply theme from settings.
|
||||
document.documentElement.className = this.$store.state.postwoman.settings.THEME_CLASS || '';
|
||||
// Load theme color data from settings, or use default color.
|
||||
let color = this.$store.state.postwoman.settings.THEME_COLOR || '#51FF0D';
|
||||
let color = this.$store.state.postwoman.settings.THEME_COLOR || '#50fa7b';
|
||||
let vibrant = this.$store.state.postwoman.settings.THEME_COLOR_VIBRANT;
|
||||
if (vibrant == null) vibrant = true;
|
||||
document.documentElement.style.setProperty('--ac-color', color);
|
||||
document.documentElement.style.setProperty('--act-color', vibrant ? '#121212' : '#fff');
|
||||
document.documentElement.style.setProperty('--act-color', vibrant ? 'rgb(37, 38, 40)' : '#ffffff');
|
||||
})();
|
||||
},
|
||||
|
||||
@@ -171,6 +183,23 @@
|
||||
// etc.
|
||||
(async () => {
|
||||
this.showInstallPrompt = await intializePwa();
|
||||
let cookiesAllowed = localStorage.getItem('cookiesAllowed') === 'yes';
|
||||
if(!cookiesAllowed) {
|
||||
this.$toast.show('We use cookies', {
|
||||
icon: 'info',
|
||||
duration: 5000,
|
||||
theme: 'toasted-primary',
|
||||
action: [
|
||||
{
|
||||
text: 'Dismiss',
|
||||
onClick: (e, toastObject) => {
|
||||
localStorage.setItem('cookiesAllowed', 'yes');
|
||||
toastObject.goAway(0);
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
})();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="page page-error">
|
||||
<h1>{{ error.statusCode }}</h1>
|
||||
<h2>{{ error.message }}</h2>
|
||||
<br>
|
||||
<img src="~static/icons/error.svg" alt="Error" class="error_banner">
|
||||
<h2>{{ error.statusCode }}</h2>
|
||||
<h3>{{ error.message }}</h3>
|
||||
<p><nuxt-link to="/"><button>Go Home</button></nuxt-link></p>
|
||||
<p><a href="" @click.prevent="reloadApplication">Reload</a></p>
|
||||
</div>
|
||||
@@ -11,14 +11,16 @@
|
||||
<style lang="scss">
|
||||
// Center the error page in the viewport.
|
||||
.page-error {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 100%;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.error_banner {
|
||||
width: 256px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
|
||||
326
nuxt.config.js
@@ -1,4 +1,3 @@
|
||||
|
||||
// Some helpful application constants.
|
||||
// TODO: Use these when rendering the pages (rather than just for head/meta tags...)
|
||||
export const meta = {
|
||||
@@ -6,10 +5,8 @@ export const meta = {
|
||||
shortDescription: "API request builder",
|
||||
description: "The Postwoman API request builder helps you create your requests faster, saving you precious time on your development."
|
||||
};
|
||||
|
||||
// Sets the base path for the router.
|
||||
// Important for deploying to GitHub pages.
|
||||
|
||||
// -- Travis includes the author in the repo slug,
|
||||
// so if there's a /, we need to get everything after it.
|
||||
let repoName = (process.env.TRAVIS_REPO_SLUG || '').split('/').pop();
|
||||
@@ -22,143 +19,276 @@ export const routerBase = process.env.DEPLOY_ENV === 'GH_PAGES' ? {
|
||||
base: '/'
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
mode: 'spa',
|
||||
/*
|
||||
** Headers of the page
|
||||
*/
|
||||
** Headers of the page
|
||||
*/
|
||||
server: {
|
||||
host: '0.0.0.0', // default: localhost
|
||||
},
|
||||
serverMiddleware: [
|
||||
'~/proxy/index.js'
|
||||
],
|
||||
head: {
|
||||
title: `${meta.name} \u2022 ${meta.shortDescription}`,
|
||||
meta: [
|
||||
{ charset: 'utf-8' },
|
||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1, minimum-scale=1, shrink-to-fit=no, minimal-ui' },
|
||||
{ hid: 'description', name: 'description', content: meta.description || '' },
|
||||
{ name: 'keywords', content: 'postwoman, api, request, testing, tool, rest, websocket'},
|
||||
|
||||
{ name: 'X-UA-Compatible', content: "IE=edge, chrome=1" },
|
||||
{ itemprop: "name", content: `${meta.name} \u2022 ${meta.shortDescription}` },
|
||||
{ itemprop: "description", content: meta.description },
|
||||
{ itemprop: "image", content: `${routerBase.router.base}icons/icon-192x192.png` },
|
||||
|
||||
{
|
||||
charset: 'utf-8'
|
||||
},
|
||||
{
|
||||
name: 'viewport',
|
||||
content: 'width=device-width, initial-scale=1, minimum-scale=1, shrink-to-fit=no, minimal-ui'
|
||||
},
|
||||
{
|
||||
hid: 'description',
|
||||
name: 'description',
|
||||
content: meta.description || ''
|
||||
},
|
||||
{
|
||||
name: 'keywords',
|
||||
content: 'postwoman, postwoman chrome, postwoman online, postwoman for mac, postwoman app, postwoman for windows, postwoman google chrome, postwoman chrome app, get postwoman, postwoman web, postwoman android, postwoman app for chrome, postwoman mobile app, postwoman web app, api, request, testing, tool, rest, websocket'
|
||||
},
|
||||
{
|
||||
name: 'X-UA-Compatible',
|
||||
content: "IE=edge, chrome=1"
|
||||
},
|
||||
{
|
||||
itemprop: "name",
|
||||
content: `${meta.name} \u2022 ${meta.shortDescription}`
|
||||
},
|
||||
{
|
||||
itemprop: "description",
|
||||
content: meta.description
|
||||
},
|
||||
{
|
||||
itemprop: "image",
|
||||
content: `${routerBase.router.base}icons/icon-192x192.png`
|
||||
},
|
||||
// Add to homescreen for Chrome on Android. Fallback for PWA (handled by nuxt)
|
||||
{ name: 'application-name', content: meta.name },
|
||||
|
||||
{
|
||||
name: 'application-name',
|
||||
content: meta.name
|
||||
},
|
||||
// Add to homescreen for Safari on iOS
|
||||
{ name: 'apple-mobile-web-app-capable', content: 'yes' },
|
||||
{ name: 'apple-mobile-web-app-status-bar-style', content: 'black-translucent' },
|
||||
{ name: 'apple-mobile-web-app-title', content: meta.name },
|
||||
|
||||
{
|
||||
name: 'apple-mobile-web-app-capable',
|
||||
content: 'yes'
|
||||
},
|
||||
{
|
||||
name: 'apple-mobile-web-app-status-bar-style',
|
||||
content: 'black-translucent'
|
||||
},
|
||||
{
|
||||
name: 'apple-mobile-web-app-title',
|
||||
content: meta.name
|
||||
},
|
||||
// Windows phone tile icon
|
||||
{ name: 'msapplication-TileImage', content: `${routerBase.router.base}icons/icon-144x144.png` },
|
||||
{ name: 'msapplication-TileColor', content: '#121212' },
|
||||
{ name: 'msapplication-tap-highlight', content: 'no' },
|
||||
|
||||
{
|
||||
name: 'msapplication-TileImage',
|
||||
content: `${routerBase.router.base}icons/icon-144x144.png`
|
||||
},
|
||||
{
|
||||
name: 'msapplication-TileColor',
|
||||
content: '#252628'
|
||||
},
|
||||
{
|
||||
name: 'msapplication-tap-highlight',
|
||||
content: 'no'
|
||||
},
|
||||
// OpenGraph
|
||||
{ property: 'og:site_name', content: meta.name },
|
||||
{ property: 'og:url', content: 'https://liyasthomas.github.io/postwoman' },
|
||||
{ property: 'og:type', content: 'website' },
|
||||
{ property: 'og:title', content: `${meta.name} \u2022 ${meta.shortDescription}` },
|
||||
{ property: 'og:description', content: meta.description },
|
||||
{ property: 'og:image', content: `${routerBase.router.base}icons/icon-144x144.png` },
|
||||
|
||||
{
|
||||
property: 'og:site_name',
|
||||
content: meta.name
|
||||
},
|
||||
{
|
||||
property: 'og:url',
|
||||
content: 'https://postwoman.io'
|
||||
},
|
||||
{
|
||||
property: 'og:type',
|
||||
content: 'website'
|
||||
},
|
||||
{
|
||||
property: 'og:title',
|
||||
content: `${meta.name} \u2022 ${meta.shortDescription}`
|
||||
},
|
||||
{
|
||||
property: 'og:description',
|
||||
content: meta.description
|
||||
},
|
||||
{
|
||||
property: 'og:image',
|
||||
content: `${routerBase.router.base}icons/icon-144x144.png`
|
||||
},
|
||||
// Twitter
|
||||
{ name: 'twitter:card', content: "summary" },
|
||||
{ name: 'twitter:site', content: "@liyasthomas" },
|
||||
{ name: 'twitter:creator', content: "@liyasthomas" },
|
||||
{ name: 'twitter:url', content: "https://liyasthomas.github.io/postwoman" },
|
||||
{ name: 'twitter:title', content: meta.name },
|
||||
{ name: 'twitter:description', content: meta.shortDescription },
|
||||
{ name: 'twitter:image', content: `${routerBase.router.base}icons/icon-144x144.png` },
|
||||
|
||||
{
|
||||
name: 'twitter:card',
|
||||
content: "summary"
|
||||
},
|
||||
{
|
||||
name: 'twitter:site',
|
||||
content: "@liyasthomas"
|
||||
},
|
||||
{
|
||||
name: 'twitter:creator',
|
||||
content: "@liyasthomas"
|
||||
},
|
||||
{
|
||||
name: 'twitter:url',
|
||||
content: "https://postwoman.io"
|
||||
},
|
||||
{
|
||||
name: 'twitter:title',
|
||||
content: meta.name
|
||||
},
|
||||
{
|
||||
name: 'twitter:description',
|
||||
content: meta.shortDescription
|
||||
},
|
||||
{
|
||||
name: 'twitter:image',
|
||||
content: `${routerBase.router.base}icons/icon-144x144.png`
|
||||
},
|
||||
],
|
||||
link: [
|
||||
{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
|
||||
|
||||
{
|
||||
rel: 'icon',
|
||||
type: 'image/x-icon',
|
||||
href: `${routerBase.router.base}favicon.ico`
|
||||
},
|
||||
// Home-screen icons (iOS)
|
||||
{ rel: 'apple-touch-icon', href: `${routerBase.router.base}icons/icon-48x48.png` },
|
||||
{ rel: 'apple-touch-icon', sizes: '72x72', href: `${routerBase.router.base}icons/icon-72x72.png` },
|
||||
{ rel: 'apple-touch-icon', sizes: '96x96', href: `${routerBase.router.base}icons/icon-96x96.png` },
|
||||
{ rel: 'apple-touch-icon', sizes: '144x144', href: `${routerBase.router.base}icons/icon-144x144.png` },
|
||||
{ rel: 'apple-touch-icon', sizes: '192x192', href: `${routerBase.router.base}icons/icon-192x192.png` },
|
||||
{
|
||||
rel: 'apple-touch-icon',
|
||||
href: `${routerBase.router.base}icons/icon-48x48.png`
|
||||
},
|
||||
{
|
||||
rel: 'apple-touch-icon',
|
||||
sizes: '72x72',
|
||||
href: `${routerBase.router.base}icons/icon-72x72.png`
|
||||
},
|
||||
{
|
||||
rel: 'apple-touch-icon',
|
||||
sizes: '96x96',
|
||||
href: `${routerBase.router.base}icons/icon-96x96.png`
|
||||
},
|
||||
{
|
||||
rel: 'apple-touch-icon',
|
||||
sizes: '144x144',
|
||||
href: `${routerBase.router.base}icons/icon-144x144.png`
|
||||
},
|
||||
{
|
||||
rel: 'apple-touch-icon',
|
||||
sizes: '192x192',
|
||||
href: `${routerBase.router.base}icons/icon-192x192.png`
|
||||
},
|
||||
]
|
||||
},
|
||||
/*
|
||||
** Customize the progress-bar color
|
||||
*/
|
||||
loading: { color: 'var(--ac-color)' },
|
||||
|
||||
** Customize the progress-bar color
|
||||
*/
|
||||
loading: {
|
||||
color: 'var(--ac-color)'
|
||||
},
|
||||
/*
|
||||
** Global CSS
|
||||
*/
|
||||
** Customize the loading indicator
|
||||
*/
|
||||
loadingIndicator: {
|
||||
name: 'pulse',
|
||||
color: 'var(--ac-color)',
|
||||
background: 'var(--bg-color)'
|
||||
},
|
||||
/*
|
||||
** Global CSS
|
||||
*/
|
||||
css: [
|
||||
'@/assets/css/themes.scss',
|
||||
'@/assets/css/fonts.scss',
|
||||
'@/assets/css/styles.scss'
|
||||
],
|
||||
/*
|
||||
** Plugins to load before mounting the App
|
||||
*/
|
||||
** Plugins to load before mounting the App
|
||||
*/
|
||||
plugins: [
|
||||
{ src: '~/plugins/vuex-persist' }
|
||||
{
|
||||
src: '~/plugins/vuex-persist'
|
||||
},
|
||||
{
|
||||
src: '~/plugins/v-tooltip'
|
||||
}
|
||||
],
|
||||
/*
|
||||
** Nuxt.js dev-modules
|
||||
*/
|
||||
** Nuxt.js dev-modules
|
||||
*/
|
||||
buildModules: [
|
||||
],
|
||||
/*
|
||||
** Nuxt.js modules
|
||||
*/
|
||||
** Nuxt.js modules
|
||||
*/
|
||||
modules: [
|
||||
// See https://goo.gl/OOhYW5
|
||||
['@nuxtjs/pwa', {
|
||||
manifest: {
|
||||
name: meta.name,
|
||||
short_name: meta.name,
|
||||
description: meta.shortDescription,
|
||||
|
||||
display: "standalone",
|
||||
theme_color: "#121212",
|
||||
background_color: "#121212",
|
||||
|
||||
icons: ((sizes) => {
|
||||
let icons = [];
|
||||
|
||||
for(let size of sizes){
|
||||
icons.push({
|
||||
"src": `${routerBase.router.base}icons/icon-${size}x${size}.png`,
|
||||
"type": "image/png",
|
||||
"sizes": `${size}x${size}`
|
||||
});
|
||||
}
|
||||
|
||||
return icons;
|
||||
})([48, 72, 96, 144, 192, 512])
|
||||
}
|
||||
}],
|
||||
|
||||
['@nuxtjs/axios']
|
||||
['@nuxtjs/pwa'],
|
||||
['@nuxtjs/axios'],
|
||||
['@nuxtjs/toast'],
|
||||
['@nuxtjs/google-analytics'],
|
||||
['@nuxtjs/sitemap'],
|
||||
['@nuxtjs/google-tag-manager', { id: process.env.GTM_ID || 'GTM-MXWD8NQ' }]
|
||||
],
|
||||
pwa: {
|
||||
manifest: {
|
||||
name: meta.name,
|
||||
short_name: meta.name,
|
||||
|
||||
display: "standalone",
|
||||
|
||||
theme_color: "#252628",
|
||||
background_color: "#252628",
|
||||
start_url: `${routerBase.router.base}`
|
||||
},
|
||||
|
||||
meta: {
|
||||
description: meta.shortDescription,
|
||||
theme_color: "#252628",
|
||||
},
|
||||
|
||||
icons: ((sizes) => {
|
||||
let icons = [];
|
||||
for (let size of sizes) {
|
||||
icons.push({
|
||||
"src": `${routerBase.router.base}icons/icon-${size}x${size}.png`,
|
||||
"type": "image/png",
|
||||
"sizes": `${size}x${size}`
|
||||
});
|
||||
}
|
||||
return icons;
|
||||
})([48, 72, 96, 144, 192, 512])
|
||||
},
|
||||
toast: {
|
||||
position: 'bottom-center',
|
||||
duration: 2000,
|
||||
theme: 'bubble'
|
||||
},
|
||||
googleAnalytics: {
|
||||
id: process.env.GA_ID || 'UA-61422507-2'
|
||||
},
|
||||
sitemap: {
|
||||
hostname: 'https://postwoman.io'
|
||||
},
|
||||
/*
|
||||
** Build configuration
|
||||
*/
|
||||
** Build configuration
|
||||
*/
|
||||
build: {
|
||||
/*
|
||||
** You can extend webpack config here
|
||||
*/
|
||||
extend (config, ctx) {
|
||||
}
|
||||
** You can extend webpack config here
|
||||
*/
|
||||
extend(config, ctx) {}
|
||||
},
|
||||
|
||||
/*
|
||||
** Generate configuration
|
||||
*/
|
||||
** Generate configuration
|
||||
*/
|
||||
generate: {
|
||||
fallback: true
|
||||
},
|
||||
|
||||
/*
|
||||
** Router configuration
|
||||
*/
|
||||
|
||||
3072
package-lock.json
generated
65
package.json
@@ -1,28 +1,41 @@
|
||||
{
|
||||
"name": "postwoman",
|
||||
"version": "0.1.0",
|
||||
"description": "Lightweight API request builder by Liyas Thomas",
|
||||
"author": "liyasthomas",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"predev": "node build.js --dev",
|
||||
"dev": "nuxt",
|
||||
"prebuild": "node build.js",
|
||||
"build": "nuxt build",
|
||||
"start": "nuxt start",
|
||||
"pregenerate": "node build.js",
|
||||
"generate": "nuxt generate"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nuxtjs/axios": "^5.6.0",
|
||||
"@nuxtjs/pwa": "^3.0.0-0",
|
||||
"nuxt": "^2.9.2",
|
||||
"vue-virtual-scroll-list": "^1.4.2",
|
||||
"vuejs-auto-complete": "^0.9.0",
|
||||
"vuex-persist": "^2.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"node-sass": "^4.12.0",
|
||||
"sass-loader": "^7.3.1"
|
||||
}
|
||||
"name": "postwoman",
|
||||
"version": "0.1.0",
|
||||
"description": "Lightweight API request builder by Liyas Thomas",
|
||||
"author": "liyasthomas",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"predev": "node build.js --dev",
|
||||
"dev": "nuxt",
|
||||
"prebuild": "node build.js",
|
||||
"build": "nuxt build",
|
||||
"start": "nuxt start",
|
||||
"pregenerate": "node build.js",
|
||||
"generate": "nuxt generate",
|
||||
"e2e": "cypress run",
|
||||
"e2e:open": "cypress open",
|
||||
"dev:e2e": "start-server-and-test dev http://localhost:3000 e2e:open",
|
||||
"test": "start-server-and-test dev http://localhost:3000 e2e"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nuxtjs/axios": "^5.6.0",
|
||||
"@nuxtjs/google-analytics": "^2.2.0",
|
||||
"@nuxtjs/google-tag-manager": "^2.3.0",
|
||||
"@nuxtjs/pwa": "^3.0.0-beta.19",
|
||||
"@nuxtjs/sitemap": "^2.0.0",
|
||||
"@nuxtjs/toast": "^3.2.1",
|
||||
"highlight.js": "^9.15.10",
|
||||
"nuxt": "^2.10.1",
|
||||
"v-tooltip": "^2.0.2",
|
||||
"vue-virtual-scroll-list": "^1.4.2",
|
||||
"vuejs-auto-complete": "^0.9.0",
|
||||
"vuex-persist": "^2.1.0",
|
||||
"yargs-parser": "^15.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cypress": "^3.4.1",
|
||||
"node-sass": "^4.12.0",
|
||||
"sass-loader": "^7.3.1",
|
||||
"start-server-and-test": "^1.10.6"
|
||||
}
|
||||
}
|
||||
|
||||
1737
pages/index.vue
@@ -1,41 +1,37 @@
|
||||
<template>
|
||||
<div class="page">
|
||||
<pw-section class="blue" label="Theme">
|
||||
<ul>
|
||||
<li>
|
||||
<h3 class="title">Background</h3>
|
||||
<div class="backgrounds">
|
||||
<div class="page">
|
||||
<pw-section class="cyan" label="Theme">
|
||||
<ul>
|
||||
<li>
|
||||
<h3 class="title">Background</h3>
|
||||
<div class="backgrounds">
|
||||
<span :key="theme.class" @click="applyTheme(theme.class)" v-for="theme in themes">
|
||||
<swatch :active="settings.THEME_CLASS === theme.class" :class="{ vibrant: theme.vibrant }" :color="theme.color" :name="theme.name"></swatch>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<h3 class="title">Color</h3>
|
||||
<div class="colors">
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<h3 class="title">Color</h3>
|
||||
<div class="colors">
|
||||
<span :key="entry.color" @click.prevent="setActiveColor(entry.color, entry.vibrant)" v-for="entry in colors">
|
||||
<swatch :active="settings.THEME_COLOR === entry.color.toUpperCase()" :class="{ vibrant: entry.vibrant }" :color="entry.color" :name="entry.name" />
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<h3 class="title">Frames</h3>
|
||||
<pw-toggle :on="!settings.DISABLE_FRAME_COLORS" @change="toggleSetting('DISABLE_FRAME_COLORS')">
|
||||
Multi-color {{ settings.DISABLE_FRAME_COLORS ? "disabled" : "enabled" }}
|
||||
</pw-toggle>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
|
||||
<!--
|
||||
PROXY SETTINGS
|
||||
--------------
|
||||
This feature is currently not finished.
|
||||
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<h3 class="title">Frames</h3>
|
||||
<span>
|
||||
<pw-toggle :on="!settings.DISABLE_FRAME_COLORS" @change="toggleSetting('DISABLE_FRAME_COLORS')">
|
||||
Multi-color {{ settings.DISABLE_FRAME_COLORS ? "Disabled" : "Enabled" }}
|
||||
</pw-toggle>
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
<pw-section class="blue" label="Proxy">
|
||||
<ul>
|
||||
<li>
|
||||
@@ -44,7 +40,10 @@
|
||||
</pw-toggle>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<!--
|
||||
PROXY SETTINGS URL AND KEY
|
||||
--------------
|
||||
This feature is currently not finished.
|
||||
<ul>
|
||||
<li>
|
||||
<label for="url">URL</label>
|
||||
@@ -55,143 +54,166 @@
|
||||
<input id="key" type="password" v-model="settings.PROXY_KEY" :disabled="!settings.PROXY_ENABLED" @change="applySetting('PROXY_KEY', $event)">
|
||||
</li>
|
||||
</ul>
|
||||
-->
|
||||
</pw-section>
|
||||
-->
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import section from "../components/section";
|
||||
import swatch from "../components/settings/swatch";
|
||||
import toggle from "../components/toggle";
|
||||
import section from "../components/section";
|
||||
import swatch from "../components/settings/swatch";
|
||||
import toggle from "../components/toggle";
|
||||
|
||||
export default {
|
||||
components: {
|
||||
'pw-section': section,
|
||||
'pw-toggle': toggle,
|
||||
'swatch': swatch
|
||||
},
|
||||
export default {
|
||||
components: {
|
||||
'pw-section': section,
|
||||
'pw-toggle': toggle,
|
||||
'swatch': swatch
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
// NOTE:: You need to first set the CSS for your theme in /assets/css/themes.scss
|
||||
// You should copy the existing light theme as a template and then just
|
||||
// set the relevant values.
|
||||
themes: [{
|
||||
"color": "#121212",
|
||||
"name": "Dark (Default)",
|
||||
"class": ""
|
||||
},
|
||||
{
|
||||
"color": "#DFDFDF",
|
||||
"name": "Light",
|
||||
"vibrant": true,
|
||||
"class": "light"
|
||||
}
|
||||
],
|
||||
// You can define a new color here! It will simply store the color value.
|
||||
colors: [
|
||||
// If the color is vibrant, black is used as the active foreground color.
|
||||
{
|
||||
"color": "#51ff0d",
|
||||
"name": "Lime (Default)",
|
||||
"vibrant": true
|
||||
},
|
||||
{
|
||||
"color": "#FFC107",
|
||||
"name": "Yellow",
|
||||
"vibrant": true
|
||||
},
|
||||
{
|
||||
"color": "#E91E63",
|
||||
"name": "Pink",
|
||||
"vibrant": false
|
||||
},
|
||||
{
|
||||
"color": "#e74c3c",
|
||||
"name": "Red",
|
||||
"vibrant": false
|
||||
},
|
||||
{
|
||||
"color": "#9b59b6",
|
||||
"name": "Purple",
|
||||
"vibrant": false
|
||||
},
|
||||
{
|
||||
"color": "#2980b9",
|
||||
"name": "Blue",
|
||||
"vibrant": false
|
||||
},
|
||||
],
|
||||
data() {
|
||||
return {
|
||||
// NOTE:: You need to first set the CSS for your theme in /assets/css/themes.scss
|
||||
// You should copy the existing light theme as a template and then just
|
||||
// set the relevant values.
|
||||
themes: [
|
||||
{
|
||||
"color": "rgb(37, 38, 40)",
|
||||
"name": "Kinda Dark",
|
||||
"class": ""
|
||||
},
|
||||
{
|
||||
"color": "#ffffff",
|
||||
"name": "Clearly White",
|
||||
"vibrant": true,
|
||||
"class": "light"
|
||||
},
|
||||
{
|
||||
"color": "#000000",
|
||||
"name": "Just Black",
|
||||
"class": "black"
|
||||
},
|
||||
{
|
||||
"color": "var(--bg-color)",
|
||||
"name": "Auto (system)",
|
||||
"vibrant": window.matchMedia('(prefers-color-scheme: light)').matches,
|
||||
"class": "auto"
|
||||
}
|
||||
],
|
||||
// You can define a new color here! It will simply store the color value.
|
||||
colors: [
|
||||
// If the color is vibrant, black is used as the active foreground color.
|
||||
{
|
||||
"color": "#50fa7b",
|
||||
"name": "Green",
|
||||
"vibrant": true
|
||||
},
|
||||
{
|
||||
"color": "#f1fa8c",
|
||||
"name": "Yellow",
|
||||
"vibrant": true
|
||||
},
|
||||
{
|
||||
"color": "#ff79c6",
|
||||
"name": "Pink",
|
||||
"vibrant": true
|
||||
},
|
||||
{
|
||||
"color": "#ff5555",
|
||||
"name": "Red",
|
||||
"vibrant": false
|
||||
},
|
||||
{
|
||||
"color": "#bd93f9",
|
||||
"name": "Purple",
|
||||
"vibrant": true
|
||||
},
|
||||
{
|
||||
"color": "#ffb86c",
|
||||
"name": "Orange",
|
||||
"vibrant": true
|
||||
},
|
||||
{
|
||||
"color": "#8be9fd",
|
||||
"name": "Cyan",
|
||||
"vibrant": true
|
||||
},
|
||||
{
|
||||
"color": "#57b5f9",
|
||||
"name": "Blue",
|
||||
"vibrant": false
|
||||
},
|
||||
],
|
||||
|
||||
settings: {
|
||||
THEME_CLASS: this.$store.state.postwoman.settings.THEME_CLASS || '',
|
||||
THEME_COLOR: '',
|
||||
THEME_COLOR_VIBRANT: true,
|
||||
settings: {
|
||||
THEME_CLASS: this.$store.state.postwoman.settings.THEME_CLASS || '',
|
||||
THEME_COLOR: '',
|
||||
THEME_COLOR_VIBRANT: true,
|
||||
|
||||
DISABLE_FRAME_COLORS: this.$store.state.postwoman.settings.DISABLE_FRAME_COLORS || false,
|
||||
PROXY_ENABLED: this.$store.state.postwoman.settings.PROXY_ENABLED || false,
|
||||
PROXY_URL: this.$store.state.postwoman.settings.PROXY_URL || '',
|
||||
PROXY_KEY: this.$store.state.postwoman.settings.PROXY_KEY || ''
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
proxySettings: {
|
||||
deep: true,
|
||||
handler (value) {
|
||||
this.applySetting('PROXY_URL', value.url);
|
||||
this.applySetting('PROXY_KEY', value.key);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
applyTheme(name) {
|
||||
this.applySetting('THEME_CLASS', name);
|
||||
document.documentElement.className = name;
|
||||
let imgGitHub = document.getElementById("imgGitHub");
|
||||
imgGitHub.style['filter'] = "";
|
||||
imgGitHub.style['webkit-filter'] = "invert(100%)";
|
||||
if (name.includes("light")){
|
||||
imgGitHub.style['filter'] = "invert(100%)";
|
||||
imgGitHub.style['webkit-filter'] = "invert(100%)";
|
||||
}
|
||||
},
|
||||
setActiveColor(color, vibrant) {
|
||||
// By default, the color is vibrant.
|
||||
if (vibrant == null) vibrant = true;
|
||||
document.documentElement.style.setProperty('--ac-color', color);
|
||||
document.documentElement.style.setProperty('--act-color', vibrant ? '#121212' : '#fff');
|
||||
this.applySetting('THEME_COLOR', color.toUpperCase());
|
||||
this.applySetting('THEME_COLOR_VIBRANT', vibrant);
|
||||
},
|
||||
getActiveColor() {
|
||||
// This strips extra spaces and # signs from the strings.
|
||||
const strip = (str) => str.replace(/#/g, '').replace(/ /g, '');
|
||||
return `#${strip(window.getComputedStyle(document.documentElement).getPropertyValue('--ac-color')).toUpperCase()}`;
|
||||
},
|
||||
applySetting(key, value) {
|
||||
this.settings[key] = value;
|
||||
this.$store.commit('postwoman/applySetting', [key, value]);
|
||||
},
|
||||
toggleSetting(key) {
|
||||
this.settings[key] = !this.settings[key];
|
||||
this.$store.commit('postwoman/applySetting', [key, this.settings[key]]);
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
this.settings.THEME_COLOR = this.getActiveColor();
|
||||
},
|
||||
|
||||
computed: {
|
||||
proxySettings () {
|
||||
return {
|
||||
url: this.settings.PROXY_URL,
|
||||
key: this.settings.PROXY_KEY
|
||||
}
|
||||
}
|
||||
DISABLE_FRAME_COLORS: this.$store.state.postwoman.settings.DISABLE_FRAME_COLORS || false,
|
||||
PROXY_ENABLED: this.$store.state.postwoman.settings.PROXY_ENABLED || false,
|
||||
PROXY_URL: this.$store.state.postwoman.settings.PROXY_URL || '',
|
||||
PROXY_KEY: this.$store.state.postwoman.settings.PROXY_KEY || ''
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
proxySettings: {
|
||||
deep: true,
|
||||
handler(value) {
|
||||
this.applySetting('PROXY_URL', value.url);
|
||||
this.applySetting('PROXY_KEY', value.key);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
applyTheme(name) {
|
||||
this.applySetting('THEME_CLASS', name);
|
||||
document.documentElement.className = name;
|
||||
let imgGitHub = document.getElementById("imgGitHub");
|
||||
imgGitHub.style['filter'] = "";
|
||||
imgGitHub.style['webkit-filter'] = "invert(100%)";
|
||||
if (name.includes("light")) {
|
||||
imgGitHub.style['filter'] = "invert(100%)";
|
||||
imgGitHub.style['webkit-filter'] = "invert(100%)";
|
||||
}
|
||||
},
|
||||
setActiveColor(color, vibrant) {
|
||||
// By default, the color is vibrant.
|
||||
if (vibrant == null) vibrant = true;
|
||||
document.documentElement.style.setProperty('--ac-color', color);
|
||||
document.documentElement.style.setProperty('--act-color', vibrant ? 'rgb(37, 38, 40)' : '#f8f8f2');
|
||||
this.applySetting('THEME_COLOR', color.toUpperCase());
|
||||
this.applySetting('THEME_COLOR_VIBRANT', vibrant);
|
||||
},
|
||||
getActiveColor() {
|
||||
// This strips extra spaces and # signs from the strings.
|
||||
const strip = (str) => str.replace(/#/g, '').replace(/ /g, '');
|
||||
return `#${strip(window.getComputedStyle(document.documentElement).getPropertyValue('--ac-color')).toUpperCase()}`;
|
||||
},
|
||||
applySetting(key, value) {
|
||||
this.settings[key] = value;
|
||||
this.$store.commit('postwoman/applySetting', [key, value]);
|
||||
},
|
||||
toggleSetting(key) {
|
||||
this.settings[key] = !this.settings[key];
|
||||
this.$store.commit('postwoman/applySetting', [key, this.settings[key]]);
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
this.settings.THEME_COLOR = this.getActiveColor();
|
||||
},
|
||||
|
||||
computed: {
|
||||
proxySettings() {
|
||||
return {
|
||||
url: this.settings.PROXY_URL,
|
||||
key: this.settings.PROXY_KEY
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
@@ -7,8 +7,14 @@
|
||||
<input id="url" type="url" :class="{ error: !urlValid }" v-model="url" @keyup.enter="urlValid ? toggleConnection() : null">
|
||||
</li>
|
||||
<li>
|
||||
<label for="action" class="hide-on-small-screen"> </label>
|
||||
<button :disabled="!urlValid" name="action" @click="toggleConnection">{{ toggleConnectionVerb }}</button>
|
||||
<label for="connect" class="hide-on-small-screen"> </label>
|
||||
<button :disabled="!urlValid" id="connect" name="connect" @click="toggleConnection">
|
||||
{{ toggleConnectionVerb }}
|
||||
<span>
|
||||
<i class="material-icons" v-if="!connectionState">sync</i>
|
||||
<i class="material-icons" v-if="connectionState">sync_disabled</i>
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
@@ -18,20 +24,25 @@
|
||||
<label for="log">Log</label>
|
||||
<div id="log" name="log" class="log">
|
||||
<span v-if="communication.log">
|
||||
<span v-for="logEntry in communication.log" :style="{ color: logEntry.color }">@ {{ logEntry.ts }} {{ getSourcePrefix(logEntry.source) }} {{ logEntry.payload }}</span>
|
||||
<span v-for="(logEntry, index) in communication.log" :style="{ color: logEntry.color }" :key="index">@ {{ logEntry.ts }} {{ getSourcePrefix(logEntry.source) }} {{ logEntry.payload }}</span>
|
||||
</span>
|
||||
<span v-else>(Waiting for connection...)</span>
|
||||
<span v-else>(waiting for connection)</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li>
|
||||
<label for="message">Message</label>
|
||||
<input id="message" name="message" type="text" v-model="communication.input" :disabled="!connectionState" @keyup.enter="connectionState ? sendMessage() : null">
|
||||
<input id="message" name="message" type="text" v-model="communication.input" :readonly="!connectionState" @keyup.enter="connectionState ? sendMessage() : null">
|
||||
</li>
|
||||
<li>
|
||||
<label for="send" class="hide-on-small-screen"> </label>
|
||||
<button name="send" :disabled="!connectionState" @click="sendMessage">Send</button>
|
||||
<button id="send" name="send" :disabled="!connectionState" @click="sendMessage">
|
||||
Send
|
||||
<span>
|
||||
<i class="material-icons">send</i>
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</pw-section>
|
||||
@@ -42,7 +53,7 @@
|
||||
margin: 4px;
|
||||
padding: 8px 16px;
|
||||
width: calc(100% - 8px);
|
||||
border-radius: 4px;
|
||||
border-radius: 8px;
|
||||
background-color: var(--bg-dark-color);
|
||||
color: var(--fg-color);
|
||||
height: 256px;
|
||||
@@ -50,9 +61,8 @@
|
||||
|
||||
&,
|
||||
span {
|
||||
font-weight: 700;
|
||||
font-size: 18px;
|
||||
font-family: monospace;
|
||||
font-family: 'Roboto Mono', monospace;
|
||||
}
|
||||
|
||||
span {
|
||||
@@ -104,7 +114,7 @@
|
||||
this.communication.log = [{
|
||||
payload: `Connecting to ${this.url}...`,
|
||||
source: 'info',
|
||||
color: 'lime'
|
||||
color: 'var(--ac-color)'
|
||||
}];
|
||||
try {
|
||||
this.socket = new WebSocket(this.url);
|
||||
@@ -113,9 +123,12 @@
|
||||
this.communication.log = [{
|
||||
payload: `Connected to ${this.url}.`,
|
||||
source: 'info',
|
||||
color: 'lime',
|
||||
color: 'var(--ac-color)',
|
||||
ts: (new Date()).toLocaleTimeString()
|
||||
}];
|
||||
this.$toast.success('Connected', {
|
||||
icon: 'sync'
|
||||
});
|
||||
};
|
||||
this.socket.onerror = (event) => {
|
||||
this.handleError();
|
||||
@@ -125,9 +138,12 @@
|
||||
this.communication.log.push({
|
||||
payload: `Disconnected from ${this.url}.`,
|
||||
source: 'info',
|
||||
color: 'red',
|
||||
color: '#ff5555',
|
||||
ts: (new Date()).toLocaleTimeString()
|
||||
});
|
||||
this.$toast.error('Disconnected', {
|
||||
icon: 'sync_disabled'
|
||||
});
|
||||
};
|
||||
this.socket.onmessage = (event) => {
|
||||
this.communication.log.push({
|
||||
@@ -138,6 +154,9 @@
|
||||
}
|
||||
} catch (ex) {
|
||||
this.handleError(ex);
|
||||
this.$toast.error('Something went wrong!', {
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
},
|
||||
disconnect() {
|
||||
@@ -149,13 +168,13 @@
|
||||
this.communication.log.push({
|
||||
payload: `An error has occurred.`,
|
||||
source: 'info',
|
||||
color: 'red',
|
||||
color: '#ff5555',
|
||||
ts: (new Date()).toLocaleTimeString()
|
||||
});
|
||||
if (error != null) this.communication.log.push({
|
||||
payload: error,
|
||||
source: 'info',
|
||||
color: 'red',
|
||||
color: '#ff5555',
|
||||
ts: (new Date()).toLocaleTimeString()
|
||||
});
|
||||
},
|
||||
|
||||
4
plugins/v-tooltip.js
Normal file
@@ -0,0 +1,4 @@
|
||||
import Vue from 'vue';
|
||||
import VTooltip from 'v-tooltip';
|
||||
|
||||
Vue.use(VTooltip);
|
||||
50
proxy/index.js
Normal file
@@ -0,0 +1,50 @@
|
||||
import express from 'express';
|
||||
import bodyParser from 'body-parser';
|
||||
import axios from 'axios';
|
||||
|
||||
const app = express();
|
||||
|
||||
app.use(bodyParser.json());
|
||||
|
||||
app.use((req, res, next) => {
|
||||
res.header('Access-Control-Allow-Origin', '*');
|
||||
next();
|
||||
});
|
||||
|
||||
app.post('/', async function(req, res) {
|
||||
const {method, url, auth, headers, data} = req.body;
|
||||
try {
|
||||
const payload = await axios({
|
||||
method,
|
||||
url,
|
||||
auth,
|
||||
headers,
|
||||
data
|
||||
})
|
||||
|
||||
return res.json({
|
||||
data: payload.data,
|
||||
status: payload.status,
|
||||
statusText: payload.statusText,
|
||||
headers: payload.headers,
|
||||
});
|
||||
|
||||
} catch(error) {
|
||||
if(error.response) {
|
||||
const errorResponse = error.response;
|
||||
return res.json({
|
||||
data: errorResponse.data,
|
||||
status: errorResponse.status,
|
||||
statusText: errorResponse.statusText,
|
||||
headers: errorResponse.headers,
|
||||
});
|
||||
} else {
|
||||
return res.status(500).send();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default {
|
||||
path: '/proxy',
|
||||
handler: app
|
||||
}
|
||||
16
static/.htaccess
Normal file
@@ -0,0 +1,16 @@
|
||||
<IfModule mod_expires.c>
|
||||
ExpiresActive On
|
||||
|
||||
# Images
|
||||
ExpiresByType image/jpeg "access plus 1 year"
|
||||
ExpiresByType image/gif "access plus 1 year"
|
||||
ExpiresByType image/png "access plus 1 year"
|
||||
ExpiresByType image/webp "access plus 1 year"
|
||||
ExpiresByType image/svg+xml "access plus 1 year"
|
||||
ExpiresByType image/x-icon "access plus 1 year"
|
||||
|
||||
# CSS, JavaScript
|
||||
ExpiresByType text/css "access plus 1 month"
|
||||
ExpiresByType text/javascript "access plus 1 month"
|
||||
ExpiresByType application/javascript "access plus 1 month"
|
||||
</IfModule>
|
||||
1
static/CNAME
Normal file
@@ -0,0 +1 @@
|
||||
postwoman.io
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
BIN
static/icon.png
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 18 KiB |
1
static/icons/error.svg
Normal file
|
After Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.9 KiB |
@@ -59,4 +59,4 @@
|
||||
data-original="#121212"
|
||||
d="M 64.601,236.822 C 64.601,394.256 192.786,612 306.001,612 412.582,612 547.4,394.256 547.4,236.822 547.4,79.388 439.322,0 306,0 172.678,0 64.601,79.388 64.601,236.822 Z m 304.12,116.415 c 29.475,-29.475 70.598,-40.195 108.552,-32.173 8.021,37.954 -2.698,79.077 -32.173,108.552 -29.475,29.475 -70.598,40.195 -108.552,32.173 -8.022,-37.955 2.698,-79.078 32.173,-108.552 z M 134.727,321.063 c 37.954,-8.021 79.077,2.698 108.552,32.173 29.475,29.475 40.195,70.598 32.173,108.552 -37.954,8.021 -79.077,-2.698 -108.552,-32.173 -29.475,-29.476 -40.194,-70.598 -32.173,-108.552 z"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#51ff0d" /></g></g></g></svg>
|
||||
style="fill:#50fa7b" /></g></g></g></svg>
|
||||
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 82 KiB |
BIN
static/screenshot.gif
Normal file
|
After Width: | Height: | Size: 323 KiB |
BIN
static/screenshot1.png
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
static/screenshot2.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
7
storage.rules
Normal file
@@ -0,0 +1,7 @@
|
||||
service firebase.storage {
|
||||
match /b/{bucket}/o {
|
||||
match /{allPaths=**} {
|
||||
allow read, write: if request.auth!=null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,16 +48,15 @@ export const state = () => ({
|
||||
export const mutations = {
|
||||
|
||||
applySetting (state, setting) {
|
||||
if(setting == null || !(setting instanceof Array) || setting.length !== 2)
|
||||
if (setting == null || !(setting instanceof Array) || setting.length !== 2)
|
||||
throw new Error("You must provide a setting (array in the form [key, value])");
|
||||
|
||||
let key = setting[0];
|
||||
let value = setting[1];
|
||||
const [key, value] = setting;
|
||||
// Do not just remove this check.
|
||||
// Add your settings key to the SETTINGS_KEYS array at the
|
||||
// top of the file.
|
||||
// This is to ensure that application settings remain documented.
|
||||
if(!SETTINGS_KEYS.includes(key)) throw new Error("The settings structure does not include the key " + key);
|
||||
if (!SETTINGS_KEYS.includes(key)) throw new Error("The settings structure does not include the key " + key);
|
||||
|
||||
state.settings[key] = value;
|
||||
}
|
||||
|
||||
1
tests/e2e/fixtures/catapi.json
Normal file
@@ -0,0 +1 @@
|
||||
{ "message": "FAKE Cat API" }
|
||||
7
tests/e2e/integration/app.starter.spec.js
Normal file
@@ -0,0 +1,7 @@
|
||||
describe('Visit home', () => {
|
||||
it('Have a page title with "Postwoman"', () => {
|
||||
cy.visit('/')
|
||||
.get('title')
|
||||
.should('contain','Postwoman')
|
||||
})
|
||||
})
|
||||
57
tests/e2e/integration/feature.url-queries.spec.js
Normal file
@@ -0,0 +1,57 @@
|
||||
describe('Methods', () => {
|
||||
const methods = [ 'POST', 'HEAD', 'POST', 'PUT', 'DELETE','OPTIONS', 'PATCH']
|
||||
methods.forEach(method => {
|
||||
it(`Change the default method GET to ${method} with url query`, () => {
|
||||
cy.visit(`/?method=${method}`)
|
||||
.get('#method').contains(method)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Url and path', () => {
|
||||
it('Change default url with query and reset default path to empty string and make a request to cat api', () => {
|
||||
cy.seedAndVisit('catapi', '/?url=https://api.thecatapi.com&path=')
|
||||
.get('#url').then(el => expect(el.val() === 'https://api.thecatapi.com').to.equal(true))
|
||||
.get("#path").then(el => expect(el.val() === '').to.equal(true))
|
||||
.get('#response-details-wrapper').should($wrapper => {
|
||||
expect($wrapper).to.contain('FAKE Cat API')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('Authentication', () => {
|
||||
it(`Change default auth 'None' to 'Basic' and set httpUser and httpPassword with url query`, () => {
|
||||
cy.visit(`?&auth=Basic&httpUser=foo&httpPassword=bar`, { retryOnStatusCodeFailure: true })
|
||||
.get('#authentication').contains('Authentication').click()
|
||||
.then(() => {
|
||||
cy.get('input[name="http_basic_user"]', { timeout: 500 })
|
||||
.invoke('val')
|
||||
.then(user => {
|
||||
expect(user === 'foo').to.equal(true)
|
||||
cy.log('Success! user === foo')
|
||||
})
|
||||
|
||||
cy.get('input[name="http_basic_passwd"]')
|
||||
.invoke('val')
|
||||
.then(user => {
|
||||
expect(user === 'bar').to.equal(true)
|
||||
cy.log('Success! password === bar')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
const base64Tkn = encodeURI(btoa('{"alg":"HS256", "typ": "JWT"}'))
|
||||
|
||||
it(`Change default auth 'None' to 'Bearer token' and set bearerToken with url query`, () => {
|
||||
cy.visit(`/?auth=Bearer Token&bearerToken=${base64Tkn}`, { retryOnStatusCodeFailure: true })
|
||||
.get('#authentication').contains('Authentication').click()
|
||||
.then(() => {
|
||||
cy.get('input[name="bearer_token"]', { timeout: 500 })
|
||||
.invoke('val')
|
||||
.then(tkn => {
|
||||
expect(tkn === base64Tkn).to.equal(true)
|
||||
cy.log(`Success! input[name="bearer_token"] === ${base64Tkn}`)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
16
tests/e2e/support/commands.js
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Creates cy.seedAndVisit() function
|
||||
* This function will go to some path and wait for some fake response from 'src/tests/fixtures/*.json'
|
||||
* @param { String } seedData The name of json at 'src/tests/fixtures/
|
||||
* @param { String } path The path or query parameters to go -ex. '/?path=/api/users'
|
||||
* @param { String } method The fake request method
|
||||
*/
|
||||
Cypress.Commands.add('seedAndVisit', (seedData, path = '/', method = 'GET') => {
|
||||
cy.server()
|
||||
.route(method, 'https://api.thecatapi.com/', `fixture:${seedData}`).as(
|
||||
'load'
|
||||
)
|
||||
cy.visit(path)
|
||||
.get('#send').click()
|
||||
.wait('@load')
|
||||
})
|
||||
1
tests/e2e/support/index.js
Normal file
@@ -0,0 +1 @@
|
||||
import './commands'
|
||||