1
0
mirror of https://github.com/jhillyerd/inbucket.git synced 2026-08-08 13:38:52 +00:00

Merge branch 'release/3.0.0-rc1'

This commit is contained in:
James Hillyerd
2020-09-24 20:58:53 -07:00
22 changed files with 1017 additions and 609 deletions
+44
View File
@@ -0,0 +1,44 @@
name: Build and Release
on:
push:
branches: [ "master", "develop" ]
tags: [ "v*" ]
pull_request:
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Setup Go
uses: actions/setup-go@v2
with:
go-version: 1.15
- name: Setup Node.js
uses: actions/setup-node@v1
with:
node-version: '10.x'
- name: Setup Elm
uses: jorelali/setup-elm@v2
with:
elm-version: 0.19.1
- name: Build frontend
run: |
npm ci
npm run build
working-directory: ./ui
- name: Test build release
uses: goreleaser/goreleaser-action@v2
if: "!startsWith(github.ref, 'refs/tags/v')"
with:
version: latest
args: release --snapshot
- name: Build and publish release
uses: goreleaser/goreleaser-action@v2
if: "startsWith(github.ref, 'refs/tags/v')"
with:
version: latest
args: release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-16
View File
@@ -25,22 +25,6 @@ jobs:
script: script:
- "elm-format --validate ." - "elm-format --validate ."
- "npm run build" - "npm run build"
- stage: deploy
go: "1.15.x"
before_install:
- "nvm install 10.19.0"
install:
- "cd ui"
- "npm ci"
- "npm run build"
- "cd .."
script: "curl -sL https://git.io/goreleaser | bash"
addons:
apt:
packages:
- rpm
stages: stages:
- test - test
- name: deploy
if: tag IS present
+12
View File
@@ -4,6 +4,17 @@ Change Log
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/). This project adheres to [Semantic Versioning](http://semver.org/).
## [v3.0.0-rc1]
### Added
- Refresh button to reload mailbox contents
- Improved keyboard (tab) focus highlights
### Changed
- The UI now includes the Open Sans webfont instead of relying on browser/OS
fonts
## [v3.0.0-beta3] ## [v3.0.0-beta3]
### Added ### Added
@@ -228,6 +239,7 @@ No change from beta1.
specific message. specific message.
[Unreleased]: https://github.com/inbucket/inbucket/compare/master...develop [Unreleased]: https://github.com/inbucket/inbucket/compare/master...develop
[v3.0.0-rc1]: https://github.com/inbucket/inbucket/compare/v3.0.0-beta3...v3.0.0-rc1
[v3.0.0-beta3]: https://github.com/inbucket/inbucket/compare/v3.0.0-beta2...v3.0.0-beta3 [v3.0.0-beta3]: https://github.com/inbucket/inbucket/compare/v3.0.0-beta2...v3.0.0-beta3
[v3.0.0-beta2]: https://github.com/inbucket/inbucket/compare/v3.0.0-beta1...v3.0.0-beta2 [v3.0.0-beta2]: https://github.com/inbucket/inbucket/compare/v3.0.0-beta1...v3.0.0-beta2
[v3.0.0-beta1]: https://github.com/inbucket/inbucket/compare/v2.1.0...v3.0.0-beta1 [v3.0.0-beta1]: https://github.com/inbucket/inbucket/compare/v2.1.0...v3.0.0-beta1
+30 -32
View File
@@ -1,11 +1,12 @@
Inbucket Inbucket
============================================================================= =============================================================================
[![Build Status](https://travis-ci.org/inbucket/inbucket.png?branch=master)][Build Status] [![Build Status](https://travis-ci.org/inbucket/inbucket.png?branch=master)][Build Status]
[![Docker Image](https://github.com/inbucket/inbucket/workflows/Docker%20Image/badge.svg)][Docker Image]
Inbucket is an email testing service; it will accept messages for any email Inbucket is an email testing service; it will accept messages for any email
address and make them available via web, REST and POP3. Once compiled, address and make them available via web, REST and POP3 interfaces. Once
Inbucket does not have any external dependencies (HTTP, SMTP, POP3 and storage compiled, Inbucket does not have any external dependencies - HTTP, SMTP, POP3
are all built in). and storage are all built in.
A Go client for the REST API is available in A Go client for the REST API is available in
`github.com/inbucket/inbucket/pkg/rest/client` - [Go API docs] `github.com/inbucket/inbucket/pkg/rest/client` - [Go API docs]
@@ -14,6 +15,7 @@ Read more at the [Inbucket Website]
![Screenshot](http://www.inbucket.org/images/inbucket-ss1.png "Viewing a message") ![Screenshot](http://www.inbucket.org/images/inbucket-ss1.png "Viewing a message")
## Development Status ## Development Status
Inbucket is currently production quality: it is being used for real work. Inbucket is currently production quality: it is being used for real work.
@@ -29,15 +31,6 @@ tracks our `master` branch (releases), `latest` tracks our unstable
`development` branch. `development` branch.
## Homebrew Tap
(currently broken, being tracked in [issue
#68](https://github.com/inbucket/inbucket/issues/68))
Inbucket has an OS X [Homebrew] tap available as [jhillyerd/inbucket][Homebrew Tap],
see the `README.md` there for installation instructions.
## Building from Source ## Building from Source
You will need functioning [Go] and [Node.js] installations for this to work. You will need functioning [Go] and [Node.js] installations for this to work.
@@ -45,17 +38,20 @@ You will need functioning [Go] and [Node.js] installations for this to work.
```sh ```sh
git clone https://github.com/inbucket/inbucket.git git clone https://github.com/inbucket/inbucket.git
cd inbucket/ui cd inbucket/ui
npm i npm ci
npm run build npm run build
cd .. cd ..
go build ./cmd/inbucket go build ./cmd/inbucket
``` ```
_Note:_ You may also use the included Makefile to build and test the Go binaries. For more information on building and development flows, check out the
[Development Quickstart] page of our wiki.
### Configure and Launch
Inbucket reads its configuration from environment variables, but comes with Inbucket reads its configuration from environment variables, but comes with
built in sane defaults. It should work on most Unix and OS X machines as is. reasonable defaults built-in. It should work on most Unix and OS X machines as
Launch the daemon: is. Launch the daemon:
```sh ```sh
./inbucket ./inbucket
@@ -65,27 +61,29 @@ By default the SMTP server will be listening on localhost port 2500 and
the web interface will be available at [localhost:9000](http://localhost:9000/). the web interface will be available at [localhost:9000](http://localhost:9000/).
See doc/[config.md] for more information on configuring Inbucket, but you will See doc/[config.md] for more information on configuring Inbucket, but you will
likely find the [Configurator] tool easier to use. likely find the [Configurator] tool the easiest way to generate a configuration.
## About ## About
Inbucket is written in [Go] Inbucket is written in [Go] and [Elm].
Inbucket is open source software released under the MIT License. The latest Inbucket is open source software released under the MIT License. The latest
version can be found at https://github.com/inbucket/inbucket version can be found at https://github.com/inbucket/inbucket
[Build Status]: https://travis-ci.org/inbucket/inbucket [Build Status]: https://travis-ci.org/inbucket/inbucket
[Change Log]: https://github.com/inbucket/inbucket/blob/master/CHANGELOG.md [Change Log]: https://github.com/inbucket/inbucket/blob/master/CHANGELOG.md
[config.md]: https://github.com/inbucket/inbucket/blob/master/doc/config.md [config.md]: https://github.com/inbucket/inbucket/blob/master/doc/config.md
[Configurator]: https://www.inbucket.org/configurator/ [Configurator]: https://www.inbucket.org/configurator/
[CONTRIBUTING.md]: https://github.com/inbucket/inbucket/blob/develop/CONTRIBUTING.md [CONTRIBUTING.md]: https://github.com/inbucket/inbucket/blob/develop/CONTRIBUTING.md
[Docker Image]: https://www.inbucket.org/binaries/docker.html [Development Quickstart]: https://github.com/inbucket/inbucket/wiki/Development-Quickstart
[From Source]: https://www.inbucket.org/installation/from-source.html [Docker Image]: https://www.inbucket.org/binaries/docker.html
[Go]: https://golang.org/ [Elm]: https://elm-lang.org/
[Go API docs]: https://godoc.org/github.com/inbucket/inbucket/pkg/rest/client [From Source]: https://www.inbucket.org/installation/from-source.html
[Homebrew]: http://brew.sh/ [Go]: https://golang.org/
[Homebrew Tap]: https://github.com/inbucket/homebrew-inbucket [Go API docs]: https://pkg.go.dev/github.com/inbucket/inbucket/pkg/rest/client
[Inbucket Website]: https://www.inbucket.org/ [Homebrew]: http://brew.sh/
[Issues List]: https://github.com/inbucket/inbucket/issues?state=open [Homebrew Tap]: https://github.com/inbucket/homebrew-inbucket
[Node.js]: https://nodejs.org/en/ [Inbucket Website]: https://www.inbucket.org/
[Issues List]: https://github.com/inbucket/inbucket/issues?state=open
[Node.js]: https://nodejs.org/en/
+2 -2
View File
@@ -15,9 +15,9 @@ require (
github.com/microcosm-cc/bluemonday v1.0.4 github.com/microcosm-cc/bluemonday v1.0.4
github.com/olekukonko/tablewriter v0.0.4 // indirect github.com/olekukonko/tablewriter v0.0.4 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/rs/zerolog v1.19.0 github.com/rs/zerolog v1.20.0
github.com/stretchr/testify v1.6.1 github.com/stretchr/testify v1.6.1
golang.org/x/net v0.0.0-20200822124328-c89045814202 golang.org/x/net v0.0.0-20200923182212-328152dc79b1
golang.org/x/text v0.3.3 // indirect golang.org/x/text v0.3.3 // indirect
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
) )
+4 -4
View File
@@ -50,8 +50,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
github.com/rs/zerolog v1.19.0 h1:hYz4ZVdUgjXTBUmrkrw55j1nHx68LfOKIQk5IYtyScg= github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs=
github.com/rs/zerolog v1.19.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo= github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI= github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI=
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo=
@@ -66,8 +66,8 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80 h1:Ao/3l156eZf2AW5wK8a7/smtodRU+gha3+BeqJ69lRk=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up98WAHf3f/ulnJ62IyA= golang.org/x/net v0.0.0-20200923182212-328152dc79b1 h1:Iu68XRPd67wN4aRGGWwwq6bZo/25jR6uu52l/j2KkUE=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200923182212-328152dc79b1/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+224 -257
View File
@@ -25,19 +25,19 @@
} }
}, },
"@babel/core": { "@babel/core": {
"version": "7.11.4", "version": "7.11.6",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.4.tgz", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.6.tgz",
"integrity": "sha512-5deljj5HlqRXN+5oJTY7Zs37iH3z3b++KjiKtIsJy1NrjOOVSEaJHEetLBhyu0aQOSNNZ/0IuEAan9GzRuDXHg==", "integrity": "sha512-Wpcv03AGnmkgm6uS6k8iwhIwTrcP0m17TL1n1sy7qD0qelDu4XNeW0dN0mHfa+Gei211yDaLoEe/VlbXQzM4Bg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/code-frame": "^7.10.4", "@babel/code-frame": "^7.10.4",
"@babel/generator": "^7.11.4", "@babel/generator": "^7.11.6",
"@babel/helper-module-transforms": "^7.11.0", "@babel/helper-module-transforms": "^7.11.0",
"@babel/helpers": "^7.10.4", "@babel/helpers": "^7.10.4",
"@babel/parser": "^7.11.4", "@babel/parser": "^7.11.5",
"@babel/template": "^7.10.4", "@babel/template": "^7.10.4",
"@babel/traverse": "^7.11.0", "@babel/traverse": "^7.11.5",
"@babel/types": "^7.11.0", "@babel/types": "^7.11.5",
"convert-source-map": "^1.7.0", "convert-source-map": "^1.7.0",
"debug": "^4.1.0", "debug": "^4.1.0",
"gensync": "^1.0.0-beta.1", "gensync": "^1.0.0-beta.1",
@@ -46,23 +46,15 @@
"resolve": "^1.3.2", "resolve": "^1.3.2",
"semver": "^5.4.1", "semver": "^5.4.1",
"source-map": "^0.5.0" "source-map": "^0.5.0"
},
"dependencies": {
"lodash": {
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
"dev": true
}
} }
}, },
"@babel/generator": { "@babel/generator": {
"version": "7.11.4", "version": "7.11.6",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.4.tgz", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.6.tgz",
"integrity": "sha512-Rn26vueFx0eOoz7iifCN2UHT6rGtnkSGWSoDRIy8jZN3B91PzeSULbswfLoOWuTuAcNwpG/mxy+uCTDnZ9Mp1g==", "integrity": "sha512-DWtQ1PV3r+cLbySoHrwn9RWEgKMBLLma4OBQloPRyDYvc5msJM9kvTLo1YnlJd1P/ZuKbdli3ijr5q3FvAF3uA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/types": "^7.11.0", "@babel/types": "^7.11.5",
"jsesc": "^2.5.1", "jsesc": "^2.5.1",
"source-map": "^0.5.0" "source-map": "^0.5.0"
} }
@@ -133,14 +125,6 @@
"@babel/helper-function-name": "^7.10.4", "@babel/helper-function-name": "^7.10.4",
"@babel/types": "^7.10.5", "@babel/types": "^7.10.5",
"lodash": "^4.17.19" "lodash": "^4.17.19"
},
"dependencies": {
"lodash": {
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
"dev": true
}
} }
}, },
"@babel/helper-explode-assignable-expression": { "@babel/helper-explode-assignable-expression": {
@@ -212,14 +196,6 @@
"@babel/template": "^7.10.4", "@babel/template": "^7.10.4",
"@babel/types": "^7.11.0", "@babel/types": "^7.11.0",
"lodash": "^4.17.19" "lodash": "^4.17.19"
},
"dependencies": {
"lodash": {
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
"dev": true
}
} }
}, },
"@babel/helper-optimise-call-expression": { "@babel/helper-optimise-call-expression": {
@@ -244,14 +220,6 @@
"dev": true, "dev": true,
"requires": { "requires": {
"lodash": "^4.17.19" "lodash": "^4.17.19"
},
"dependencies": {
"lodash": {
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
"dev": true
}
} }
}, },
"@babel/helper-remap-async-to-generator": { "@babel/helper-remap-async-to-generator": {
@@ -347,9 +315,9 @@
} }
}, },
"@babel/parser": { "@babel/parser": {
"version": "7.11.4", "version": "7.11.5",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.4.tgz", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.5.tgz",
"integrity": "sha512-MggwidiH+E9j5Sh8pbrX5sJvMcsqS5o+7iB42M9/k0CD63MjYbdP4nhSh7uB5wnv2/RVzTZFTxzF/kIa5mrCqA==", "integrity": "sha512-X9rD8qqm695vgmeaQ4fvz/o3+Wk4ZzQvSHkDBgpYKxpD4qTAUm88ZKtHkVqIOsYFFbIQ6wQYhC6q7pjqVK0E0Q==",
"dev": true "dev": true
}, },
"@babel/plugin-proposal-async-generator-functions": { "@babel/plugin-proposal-async-generator-functions": {
@@ -909,9 +877,9 @@
} }
}, },
"@babel/preset-env": { "@babel/preset-env": {
"version": "7.11.0", "version": "7.11.5",
"resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.11.0.tgz", "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.11.5.tgz",
"integrity": "sha512-2u1/k7rG/gTh02dylX2kL3S0IJNF+J6bfDSp4DI2Ma8QN6Y9x9pmAax59fsCk6QUQG0yqH47yJWA+u1I1LccAg==", "integrity": "sha512-kXqmW1jVcnB2cdueV+fyBM8estd5mlNfaQi6lwLgRwCby4edpavgbFhiBNjmWA3JpB/yZGSISa7Srf+TwxDQoA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/compat-data": "^7.11.0", "@babel/compat-data": "^7.11.0",
@@ -976,7 +944,7 @@
"@babel/plugin-transform-unicode-escapes": "^7.10.4", "@babel/plugin-transform-unicode-escapes": "^7.10.4",
"@babel/plugin-transform-unicode-regex": "^7.10.4", "@babel/plugin-transform-unicode-regex": "^7.10.4",
"@babel/preset-modules": "^0.1.3", "@babel/preset-modules": "^0.1.3",
"@babel/types": "^7.11.0", "@babel/types": "^7.11.5",
"browserslist": "^4.12.0", "browserslist": "^4.12.0",
"core-js-compat": "^3.6.2", "core-js-compat": "^3.6.2",
"invariant": "^2.2.2", "invariant": "^2.2.2",
@@ -1018,47 +986,31 @@
} }
}, },
"@babel/traverse": { "@babel/traverse": {
"version": "7.11.0", "version": "7.11.5",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.5.tgz",
"integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==", "integrity": "sha512-EjiPXt+r7LiCZXEfRpSJd+jUMnBd4/9OUv7Nx3+0u9+eimMwJmG0Q98lw4/289JCoxSE8OolDMNZaaF/JZ69WQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/code-frame": "^7.10.4", "@babel/code-frame": "^7.10.4",
"@babel/generator": "^7.11.0", "@babel/generator": "^7.11.5",
"@babel/helper-function-name": "^7.10.4", "@babel/helper-function-name": "^7.10.4",
"@babel/helper-split-export-declaration": "^7.11.0", "@babel/helper-split-export-declaration": "^7.11.0",
"@babel/parser": "^7.11.0", "@babel/parser": "^7.11.5",
"@babel/types": "^7.11.0", "@babel/types": "^7.11.5",
"debug": "^4.1.0", "debug": "^4.1.0",
"globals": "^11.1.0", "globals": "^11.1.0",
"lodash": "^4.17.19" "lodash": "^4.17.19"
},
"dependencies": {
"lodash": {
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
"dev": true
}
} }
}, },
"@babel/types": { "@babel/types": {
"version": "7.11.0", "version": "7.11.5",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz",
"integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/helper-validator-identifier": "^7.10.4", "@babel/helper-validator-identifier": "^7.10.4",
"lodash": "^4.17.19", "lodash": "^4.17.19",
"to-fast-properties": "^2.0.0" "to-fast-properties": "^2.0.0"
},
"dependencies": {
"lodash": {
"version": "4.17.20",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
"dev": true
}
} }
}, },
"@fortawesome/fontawesome-free": { "@fortawesome/fontawesome-free": {
@@ -1084,9 +1036,9 @@
} }
}, },
"@types/html-minifier-terser": { "@types/html-minifier-terser": {
"version": "5.1.0", "version": "5.1.1",
"resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.0.tgz", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.1.tgz",
"integrity": "sha512-iYCgjm1dGPRuo12+BStjd1HiVQqhlRhWDOQigNxn023HcjnhsiFz9pc6CzJj4HwDCSQca9bxTL4PxJDbkdm3PA==", "integrity": "sha512-giAlZwstKbmvMk1OO7WXSj4OZ0keXAcl2TQq4LWHiiPH2ByaH7WeUzng+Qej8UPxxv+8lRTuouo0iaNDBuzIBA==",
"dev": true "dev": true
}, },
"@types/json-schema": { "@types/json-schema": {
@@ -1137,9 +1089,9 @@
} }
}, },
"@types/webpack": { "@types/webpack": {
"version": "4.41.21", "version": "4.41.22",
"resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.21.tgz", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.22.tgz",
"integrity": "sha512-2j9WVnNrr/8PLAB5csW44xzQSJwS26aOnICsP3pSGCEdsu6KYtfQ6QJsVUKHWRnm1bL7HziJsfh5fHqth87yKA==", "integrity": "sha512-JQDJK6pj8OMV9gWOnN1dcLCyU9Hzs6lux0wBO4lr1+gyEhIBR9U3FMrz12t2GPkg110XAxEAw2WHF6g7nZIbRQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/anymatch": "*", "@types/anymatch": "*",
@@ -1762,6 +1714,16 @@
"dev": true, "dev": true,
"optional": true "optional": true
}, },
"bindings": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz",
"integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==",
"dev": true,
"optional": true,
"requires": {
"file-uri-to-path": "1.0.0"
}
},
"bluebird": { "bluebird": {
"version": "3.7.2", "version": "3.7.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
@@ -1987,15 +1949,15 @@
} }
}, },
"browserslist": { "browserslist": {
"version": "4.14.0", "version": "4.14.4",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.0.tgz", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.14.4.tgz",
"integrity": "sha512-pUsXKAF2lVwhmtpeA3LJrZ76jXuusrNyhduuQs7CDFf9foT4Y38aQOserd2lMe5DSSrjf3fx34oHwryuvxAUgQ==", "integrity": "sha512-7FOuawafVdEwa5Jv4nzeik/PepAjVte6HmVGHsjt2bC237jeL9QlcTBDF3PnHEvcC6uHwLGYPwZHNZMB7wWAnw==",
"dev": true, "dev": true,
"requires": { "requires": {
"caniuse-lite": "^1.0.30001111", "caniuse-lite": "^1.0.30001135",
"electron-to-chromium": "^1.3.523", "electron-to-chromium": "^1.3.570",
"escalade": "^3.0.2", "escalade": "^3.1.0",
"node-releases": "^1.1.60" "node-releases": "^1.1.61"
} }
}, },
"buffer": { "buffer": {
@@ -2075,12 +2037,6 @@
"once": "^1.3.0", "once": "^1.3.0",
"path-is-absolute": "^1.0.0" "path-is-absolute": "^1.0.0"
} }
},
"y18n": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
"dev": true
} }
} }
}, },
@@ -2111,10 +2067,16 @@
"tslib": "^1.10.0" "tslib": "^1.10.0"
} }
}, },
"camelcase": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.0.0.tgz",
"integrity": "sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w==",
"dev": true
},
"caniuse-lite": { "caniuse-lite": {
"version": "1.0.30001120", "version": "1.0.30001135",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001120.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001135.tgz",
"integrity": "sha512-JBP68okZs1X8D7MQTY602jxMYBmXEKOFkzTBaNSkubooMPFOAv2TXWaKle7qgHpjLDhUzA/TMT0qsNleVyXGUQ==", "integrity": "sha512-ziNcheTGTHlu9g34EVoHQdIu5g4foc8EsxMGC7Xkokmvw0dqNtX8BS8RgCgFBaAiSp2IdjvBxNdh0ssib28eVQ==",
"dev": true "dev": true
}, },
"chalk": { "chalk": {
@@ -2527,9 +2489,9 @@
} }
}, },
"css-loader": { "css-loader": {
"version": "4.2.2", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-4.2.2.tgz", "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-4.3.0.tgz",
"integrity": "sha512-omVGsTkZPVwVRpckeUnLshPp12KsmMSLqYxs12+RzM9jRR5Y+Idn/tBffjXRvOE+qW7if24cuceFJqYR5FmGBg==", "integrity": "sha512-rdezjCjScIrsL8BSYszgT4s476IcNKt6yX69t0pHjJVnPUTDpn4WfIpDQTN3wCJvUvfsz/mFjuGOekf3PY3NUg==",
"dev": true, "dev": true,
"requires": { "requires": {
"camelcase": "^6.0.0", "camelcase": "^6.0.0",
@@ -2542,14 +2504,14 @@
"postcss-modules-scope": "^2.2.0", "postcss-modules-scope": "^2.2.0",
"postcss-modules-values": "^3.0.0", "postcss-modules-values": "^3.0.0",
"postcss-value-parser": "^4.1.0", "postcss-value-parser": "^4.1.0",
"schema-utils": "^2.7.0", "schema-utils": "^2.7.1",
"semver": "^7.3.2" "semver": "^7.3.2"
}, },
"dependencies": { "dependencies": {
"ajv": { "ajv": {
"version": "6.12.4", "version": "6.12.5",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz",
"integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==", "integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==",
"dev": true, "dev": true,
"requires": { "requires": {
"fast-deep-equal": "^3.1.1", "fast-deep-equal": "^3.1.1",
@@ -2564,50 +2526,21 @@
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
"dev": true "dev": true
}, },
"big.js": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
"dev": true
},
"camelcase": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.0.0.tgz",
"integrity": "sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w==",
"dev": true
},
"emojis-list": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
"dev": true
},
"fast-deep-equal": { "fast-deep-equal": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true "dev": true
}, },
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
"dev": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"schema-utils": { "schema-utils": {
"version": "2.7.0", "version": "2.7.1",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
"integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/json-schema": "^7.0.4", "@types/json-schema": "^7.0.5",
"ajv": "^6.12.2", "ajv": "^6.12.4",
"ajv-keywords": "^3.4.1" "ajv-keywords": "^3.5.2"
} }
}, },
"semver": { "semver": {
@@ -2864,9 +2797,9 @@
}, },
"dependencies": { "dependencies": {
"domelementtype": { "domelementtype": {
"version": "2.0.1", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.2.tgz",
"integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==", "integrity": "sha512-wFwTwCVebUrMgGeAwRL/NhZtHAUyT9n9yg4IMDwf10+6iCMxSkVq9MGCVEH+QZWo1nNidy8kNvwmv4zWHDTqvA==",
"dev": true "dev": true
} }
} }
@@ -2931,9 +2864,9 @@
"dev": true "dev": true
}, },
"electron-to-chromium": { "electron-to-chromium": {
"version": "1.3.555", "version": "1.3.571",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.555.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.571.tgz",
"integrity": "sha512-/55x3nF2feXFZ5tdGUOr00TxnUjUgdxhrn+eCJ1FAcoAt+cKQTjQkUC5XF4frMWE1R5sjHk+JueuBalimfe5Pg==", "integrity": "sha512-UYEQ2Gtc50kqmyOmOVtj6Oqi38lm5yRJY3pLuWt6UIot0No1L09uu6Ja6/1XKwmz/p0eJFZTUZi+khd1PV1hHA==",
"dev": true "dev": true
}, },
"elliptic": { "elliptic": {
@@ -3019,9 +2952,9 @@
"dev": true "dev": true
}, },
"emojis-list": { "emojis-list": {
"version": "2.1.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
"integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
"dev": true "dev": true
}, },
"encodeurl": { "encodeurl": {
@@ -3131,9 +3064,9 @@
} }
}, },
"escalade": { "escalade": {
"version": "3.0.2", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.0.2.tgz", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.0.tgz",
"integrity": "sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ==", "integrity": "sha512-mAk+hPSO8fLDkhV7V0dXazH5pDc6MrjBTPyD3VeKzxnVFjH1MIxbCdqGZB9O8+EwWakZs3ZCbDS4IpRt79V1ig==",
"dev": true "dev": true
}, },
"escape-html": { "escape-html": {
@@ -3159,12 +3092,20 @@
} }
}, },
"esrecurse": { "esrecurse": {
"version": "4.2.1", "version": "4.3.0",
"resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
"integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
"dev": true, "dev": true,
"requires": { "requires": {
"estraverse": "^4.1.0" "estraverse": "^5.2.0"
},
"dependencies": {
"estraverse": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
"integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
"dev": true
}
} }
}, },
"estraverse": { "estraverse": {
@@ -3174,9 +3115,9 @@
"dev": true "dev": true
}, },
"esutils": { "esutils": {
"version": "2.0.2", "version": "2.0.3",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
"dev": true "dev": true
}, },
"etag": { "etag": {
@@ -3465,19 +3406,19 @@
"dev": true "dev": true
}, },
"file-loader": { "file-loader": {
"version": "6.0.0", "version": "6.1.0",
"resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.0.0.tgz", "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.1.0.tgz",
"integrity": "sha512-/aMOAYEFXDdjG0wytpTL5YQLfZnnTmLNjn+AIrJ/6HVnTfDqLsVKUUwkDf4I4kgex36BvjuXEn/TX9B/1ESyqQ==", "integrity": "sha512-26qPdHyTsArQ6gU4P1HJbAbnFTyT2r0pG7czh1GFAd9TZbj0n94wWbupgixZH/ET/meqi2/5+F7DhW4OAXD+Lg==",
"dev": true, "dev": true,
"requires": { "requires": {
"loader-utils": "^2.0.0", "loader-utils": "^2.0.0",
"schema-utils": "^2.6.5" "schema-utils": "^2.7.1"
}, },
"dependencies": { "dependencies": {
"ajv": { "ajv": {
"version": "6.12.4", "version": "6.12.5",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz",
"integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==", "integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==",
"dev": true, "dev": true,
"requires": { "requires": {
"fast-deep-equal": "^3.1.1", "fast-deep-equal": "^3.1.1",
@@ -3492,48 +3433,32 @@
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
"dev": true "dev": true
}, },
"big.js": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
"dev": true
},
"emojis-list": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
"dev": true
},
"fast-deep-equal": { "fast-deep-equal": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true "dev": true
}, },
"loader-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
"integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
"dev": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^2.1.2"
}
},
"schema-utils": { "schema-utils": {
"version": "2.7.0", "version": "2.7.1",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
"integrity": "sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==", "integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/json-schema": "^7.0.4", "@types/json-schema": "^7.0.5",
"ajv": "^6.12.2", "ajv": "^6.12.4",
"ajv-keywords": "^3.4.1" "ajv-keywords": "^3.5.2"
} }
} }
} }
}, },
"file-uri-to-path": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz",
"integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==",
"dev": true,
"optional": true
},
"fill-range": { "fill-range": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
@@ -4007,9 +3932,9 @@
} }
}, },
"html-webpack-plugin": { "html-webpack-plugin": {
"version": "4.4.1", "version": "4.5.0",
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.4.1.tgz", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-4.5.0.tgz",
"integrity": "sha512-nEtdEIsIGXdXGG7MjTTZlmhqhpHU9pJFc1OYxcP36c5/ZKP6b0BJMww2QTvJGQYA9aMxUnjDujpZdYcVOXiBCQ==", "integrity": "sha512-MouoXEYSjTzCrjIxWwg8gxL5fE2X2WZJLmBYXlaJhQUH5K/b5OrqmV7T4dB7iu0xkmJ6JlUuV6fFVtnqbPopZw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/html-minifier-terser": "^5.0.0", "@types/html-minifier-terser": "^5.0.0",
@@ -4023,6 +3948,26 @@
"util.promisify": "1.0.0" "util.promisify": "1.0.0"
}, },
"dependencies": { "dependencies": {
"json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"dev": true,
"requires": {
"minimist": "^1.2.0"
}
},
"loader-utils": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
"integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
"dev": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^1.0.1"
}
},
"tapable": { "tapable": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
@@ -4284,9 +4229,9 @@
"dev": true "dev": true
}, },
"is-callable": { "is-callable": {
"version": "1.2.0", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz",
"integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==",
"dev": true "dev": true
}, },
"is-data-descriptor": { "is-data-descriptor": {
@@ -4535,39 +4480,14 @@
"dev": true "dev": true
}, },
"loader-utils": { "loader-utils": {
"version": "1.2.3", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz",
"integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"big.js": "^5.2.2", "big.js": "^5.2.2",
"emojis-list": "^2.0.0", "emojis-list": "^3.0.0",
"json5": "^1.0.1" "json5": "^2.1.2"
},
"dependencies": {
"big.js": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
"integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==",
"dev": true
},
"json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"dev": true,
"requires": {
"minimist": "^1.2.0"
},
"dependencies": {
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true
}
}
}
} }
}, },
"locate-path": { "locate-path": {
@@ -4862,6 +4782,13 @@
"integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", "integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=",
"dev": true "dev": true
}, },
"nan": {
"version": "2.14.1",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz",
"integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==",
"dev": true,
"optional": true
},
"nanomatch": { "nanomatch": {
"version": "1.2.13", "version": "1.2.13",
"resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz",
@@ -4975,9 +4902,9 @@
} }
}, },
"node-releases": { "node-releases": {
"version": "1.1.60", "version": "1.1.61",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.60.tgz", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.61.tgz",
"integrity": "sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA==", "integrity": "sha512-DD5vebQLg8jLCOzwupn954fbIiZht05DAZs0k2u8NStSe6h9XdsuIQL8hSRKYiU8WUQRznmSDrKGbv3ObOmC7g==",
"dev": true "dev": true
}, },
"normalize-path": { "normalize-path": {
@@ -5192,6 +5119,11 @@
"wrappy": "1" "wrappy": "1"
} }
}, },
"opensans-npm-webfont": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/opensans-npm-webfont/-/opensans-npm-webfont-1.0.0.tgz",
"integrity": "sha512-2ehgrX+NpoxLOil30tYGr0cDsDXSJn9gon6PfM1Ki0CxZF6ui9Mi6Dm5DGglKyK2QiX0gMb6Ch7VmRHwfc4M6Q=="
},
"opn": { "opn": {
"version": "5.5.0", "version": "5.5.0",
"resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz", "resolved": "https://registry.npmjs.org/opn/-/opn-5.5.0.tgz",
@@ -5465,9 +5397,9 @@
"dev": true "dev": true
}, },
"postcss": { "postcss": {
"version": "7.0.32", "version": "7.0.34",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.32.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.34.tgz",
"integrity": "sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw==", "integrity": "sha512-H/7V2VeNScX9KE83GDrDZNiGT1m2H+UTnlinIzhjlLX9hfMUn1mHNnGeX81a1c8JSBdBvqk7c2ZOG6ZPn5itGw==",
"dev": true, "dev": true,
"requires": { "requires": {
"chalk": "^2.4.2", "chalk": "^2.4.2",
@@ -5534,14 +5466,15 @@
} }
}, },
"postcss-selector-parser": { "postcss-selector-parser": {
"version": "6.0.2", "version": "6.0.3",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.3.tgz",
"integrity": "sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg==", "integrity": "sha512-0ClFaY4X1ra21LRqbW6y3rUbWcxnSVkDFG57R7Nxus9J9myPFlv+jYDMohzpkBx0RrjjiqjtycpchQ+PLGmZ9w==",
"dev": true, "dev": true,
"requires": { "requires": {
"cssesc": "^3.0.0", "cssesc": "^3.0.0",
"indexes-of": "^1.0.1", "indexes-of": "^1.0.1",
"uniq": "^1.0.1" "uniq": "^1.0.1",
"util-deprecate": "^1.0.2"
} }
}, },
"postcss-value-parser": { "postcss-value-parser": {
@@ -5750,9 +5683,9 @@
} }
}, },
"regenerate": { "regenerate": {
"version": "1.4.0", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz",
"integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==", "integrity": "sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==",
"dev": true "dev": true
}, },
"regenerate-unicode-properties": { "regenerate-unicode-properties": {
@@ -5859,9 +5792,9 @@
} }
}, },
"regexpu-core": { "regexpu-core": {
"version": "4.7.0", "version": "4.7.1",
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.1.tgz",
"integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==", "integrity": "sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"regenerate": "^1.4.0", "regenerate": "^1.4.0",
@@ -5945,9 +5878,9 @@
"dev": true "dev": true
}, },
"resolve": { "resolve": {
"version": "1.12.0", "version": "1.17.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
"integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
"dev": true, "dev": true,
"requires": { "requires": {
"path-parse": "^1.0.6" "path-parse": "^1.0.6"
@@ -7342,7 +7275,11 @@
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
"integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
"dev": true, "dev": true,
"optional": true "optional": true,
"requires": {
"bindings": "^1.5.0",
"nan": "^2.12.1"
}
}, },
"glob-parent": { "glob-parent": {
"version": "3.1.0", "version": "3.1.0",
@@ -7401,9 +7338,9 @@
} }
}, },
"webpack": { "webpack": {
"version": "4.44.1", "version": "4.44.2",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-4.44.1.tgz", "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.44.2.tgz",
"integrity": "sha512-4UOGAohv/VGUNQJstzEywwNxqX417FnjZgZJpJQegddzPmTvph37eBIRbRTfdySXzVtJXLJfbMN3mMYhM6GdmQ==", "integrity": "sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"@webassemblyjs/ast": "1.9.0", "@webassemblyjs/ast": "1.9.0",
@@ -7432,9 +7369,9 @@
}, },
"dependencies": { "dependencies": {
"ajv": { "ajv": {
"version": "6.12.4", "version": "6.12.5",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz",
"integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==", "integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==",
"dev": true, "dev": true,
"requires": { "requires": {
"fast-deep-equal": "^3.1.1", "fast-deep-equal": "^3.1.1",
@@ -7455,6 +7392,26 @@
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true "dev": true
}, },
"json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"dev": true,
"requires": {
"minimist": "^1.2.0"
}
},
"loader-utils": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
"integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
"dev": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^3.0.0",
"json5": "^1.0.1"
}
},
"tapable": { "tapable": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz",
@@ -7785,7 +7742,11 @@
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz",
"integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==",
"dev": true, "dev": true,
"optional": true "optional": true,
"requires": {
"bindings": "^1.5.0",
"nan": "^2.12.1"
}
}, },
"get-caller-file": { "get-caller-file": {
"version": "2.0.5", "version": "2.0.5",
@@ -8029,6 +7990,12 @@
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
"dev": true "dev": true
}, },
"y18n": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==",
"dev": true
},
"yallist": { "yallist": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+11 -8
View File
@@ -4,26 +4,29 @@
"license": "MIT", "license": "MIT",
"private": true, "private": true,
"scripts": { "scripts": {
"clean": "rm -rf dist elm-stuff",
"build": "webpack --mode production", "build": "webpack --mode production",
"watch": "webpack --mode development --watch", "watch": "webpack --mode development --watch",
"dev": "webpack-dev-server --mode development --host 0.0.0.0 --port 3000 --hot", "dev": "webpack-dev-server --mode development --port 3000 --hot --watch",
"errors": "webpack --mode development --display-error-details" "errors": "webpack --mode development --display-error-details"
}, },
"dependencies": {}, "dependencies": {
"opensans-npm-webfont": "^1.0.0"
},
"devDependencies": { "devDependencies": {
"@babel/core": "^7.11.4", "@babel/core": "^7.11.6",
"@babel/preset-env": "^7.11.0", "@babel/preset-env": "^7.11.5",
"@fortawesome/fontawesome-free": "^5.14.0", "@fortawesome/fontawesome-free": "^5.14.0",
"@webcomponents/webcomponentsjs": "^2.4.4", "@webcomponents/webcomponentsjs": "^2.4.4",
"babel-loader": "^8.1.0", "babel-loader": "^8.1.0",
"css-loader": "^4.2.2", "css-loader": "^4.3.0",
"elm-hot-webpack-loader": "^1.1.7", "elm-hot-webpack-loader": "^1.1.7",
"elm-webpack-loader": "^7.0.1", "elm-webpack-loader": "^7.0.1",
"file-loader": "^6.0.0", "file-loader": "^6.1.0",
"html-webpack-plugin": "^4.4.1", "html-webpack-plugin": "^4.5.0",
"node-elm-compiler": "^5.0.5", "node-elm-compiler": "^5.0.5",
"style-loader": "^1.2.1", "style-loader": "^1.2.1",
"webpack": "^4.44.1", "webpack": "^4.44.2",
"webpack-cli": "^3.3.12", "webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0" "webpack-dev-server": "^3.11.0"
} }
+3 -1
View File
@@ -1,5 +1,7 @@
module Api exposing module Api exposing
( deleteMessage ( DataResult
, HttpResult
, deleteMessage
, getGreeting , getGreeting
, getHeaderList , getHeaderList
, getMessage , getMessage
+370
View File
@@ -0,0 +1,370 @@
module Effect exposing
( Effect
, addRecent
, append
, batch
, clearFlash
, deleteMessage
, disableRouting
, enableRouting
, focusModal
, focusModalResult
, getGreeting
, getHeaderList
, getMessage
, getServerConfig
, getServerMetrics
, map
, markMessageSeen
, navigateRoute
, none
, perform
, posixTime
, purgeMailbox
, schedule
, showFlash
, updateRoute
)
import Api exposing (DataResult, HttpResult)
import Browser.Navigation as Nav
import Data.Message exposing (Message)
import Data.MessageHeader exposing (MessageHeader)
import Data.Metrics exposing (Metrics)
import Data.ServerConfig exposing (ServerConfig)
import Data.Session as Session exposing (Session)
import Modal
import Route exposing (Route)
import Task
import Time
import Timer exposing (Timer)
type Effect msg
= None
| ApiEffect (ApiEffect msg)
| Batch (List (Effect msg))
| ModalFocus (Modal.Msg -> msg)
| PosixTime (Time.Posix -> msg)
| RouteNavigate Bool Route
| RouteUpdate Route
| ScheduleTimer (Timer -> msg) Timer Float
| SessionEffect SessionEffect
type ApiEffect msg
= DeleteMessage (HttpResult msg) String String
| GetGreeting (DataResult msg String)
| GetServerConfig (DataResult msg ServerConfig)
| GetServerMetrics (DataResult msg Metrics)
| GetHeaderList (DataResult msg (List MessageHeader)) String
| GetMessage (DataResult msg Message) String String
| MarkMessageSeen (HttpResult msg) String String
| PurgeMailbox (HttpResult msg) String
type SessionEffect
= FlashClear
| FlashShow Session.Flash
| ModalFocusResult Modal.Msg
| RecentAdd String
| RoutingDisable
| RoutingEnable
{-| Appends a new effect to a model/effect tuple.
-}
append : Effect msg -> ( a, Effect msg ) -> ( a, Effect msg )
append e ( model, effect ) =
( model, batch [ effect, e ] )
{-| Packs a List of Effects into a single Effect
-}
batch : List (Effect msg) -> Effect msg
batch effects =
Batch effects
{-| Transform message types produced by an effect.
-}
map : (a -> b) -> Effect a -> Effect b
map f effect =
case effect of
None ->
None
Batch effects ->
Batch <| List.map (map f) effects
ModalFocus toMsg ->
ModalFocus <| toMsg >> f
PosixTime toMsg ->
PosixTime <| toMsg >> f
ScheduleTimer toMsg timer millis ->
ScheduleTimer (toMsg >> f) timer millis
RouteNavigate pushHistory route ->
RouteNavigate pushHistory route
RouteUpdate route ->
RouteUpdate route
ApiEffect apiEffect ->
ApiEffect <| mapApi f apiEffect
SessionEffect sessionEffect ->
SessionEffect sessionEffect
mapApi : (a -> b) -> ApiEffect a -> ApiEffect b
mapApi f effect =
case effect of
DeleteMessage result mailbox id ->
DeleteMessage (result >> f) mailbox id
GetGreeting result ->
GetGreeting (result >> f)
GetServerConfig result ->
GetServerConfig (result >> f)
GetServerMetrics result ->
GetServerMetrics (result >> f)
GetHeaderList result mailbox ->
GetHeaderList (result >> f) mailbox
GetMessage result mailbox id ->
GetMessage (result >> f) mailbox id
MarkMessageSeen result mailbox id ->
MarkMessageSeen (result >> f) mailbox id
PurgeMailbox result mailbox ->
PurgeMailbox (result >> f) mailbox
{-| Applies an effect by updating the session and/or producing a Cmd.
-}
perform : ( Session, Effect msg ) -> ( Session, Cmd msg )
perform ( session, effect ) =
case effect of
None ->
( session, Cmd.none )
Batch effects ->
List.foldl batchPerform ( session, [] ) effects
|> Tuple.mapSecond Cmd.batch
ModalFocus toMsg ->
( session, Modal.resetFocusCmd toMsg )
PosixTime toMsg ->
( session, Task.perform toMsg Time.now )
ScheduleTimer toMsg timer millis ->
( session, Timer.schedule toMsg timer millis )
RouteNavigate pushHistory route ->
let
url =
session.router.toPath route
in
( Session.enableRouting session
, if pushHistory then
Nav.pushUrl session.key url
else
Nav.replaceUrl session.key url
)
RouteUpdate route ->
( Session.disableRouting session
, session.router.toPath route
|> Nav.replaceUrl session.key
)
ApiEffect apiEffect ->
performApi ( session, apiEffect )
SessionEffect sessionEffect ->
performSession ( session, sessionEffect )
performApi : ( Session, ApiEffect msg ) -> ( Session, Cmd msg )
performApi ( session, effect ) =
case effect of
DeleteMessage toMsg mailbox id ->
( session, Api.deleteMessage session toMsg mailbox id )
GetGreeting toMsg ->
( session, Api.getGreeting session toMsg )
GetServerConfig toMsg ->
( session, Api.getServerConfig session toMsg )
GetServerMetrics toMsg ->
( session, Api.getServerMetrics session toMsg )
GetHeaderList toMsg mailbox ->
( session, Api.getHeaderList session toMsg mailbox )
GetMessage toMsg mailbox id ->
( session, Api.getMessage session toMsg mailbox id )
MarkMessageSeen toMsg mailbox id ->
( session, Api.markMessageSeen session toMsg mailbox id )
PurgeMailbox toMsg mailbox ->
( session, Api.purgeMailbox session toMsg mailbox )
performSession : ( Session, SessionEffect ) -> ( Session, Cmd msg )
performSession ( session, effect ) =
case effect of
RecentAdd mailbox ->
( Session.addRecent mailbox session, Cmd.none )
FlashClear ->
( Session.clearFlash session, Cmd.none )
FlashShow flash ->
( Session.showFlash flash session, Cmd.none )
ModalFocusResult result ->
( Modal.updateSession result session, Cmd.none )
RoutingDisable ->
( Session.disableRouting session, Cmd.none )
RoutingEnable ->
( Session.enableRouting session, Cmd.none )
-- EFFECT CONSTRUCTORS
none : Effect msg
none =
None
{-| Adds specified mailbox to the recently viewed list
-}
addRecent : String -> Effect msg
addRecent mailbox =
SessionEffect (RecentAdd mailbox)
disableRouting : Effect msg
disableRouting =
SessionEffect RoutingDisable
enableRouting : Effect msg
enableRouting =
SessionEffect RoutingEnable
clearFlash : Effect msg
clearFlash =
SessionEffect FlashClear
showFlash : Session.Flash -> Effect msg
showFlash flash =
SessionEffect (FlashShow flash)
{-| Locks focus to the `modal-dialog` dom ID.
-}
focusModal : (Modal.Msg -> msg) -> Effect msg
focusModal toMsg =
ModalFocus toMsg
focusModalResult : Modal.Msg -> Effect msg
focusModalResult msg =
SessionEffect (ModalFocusResult msg)
deleteMessage : HttpResult msg -> String -> String -> Effect msg
deleteMessage toMsg mailboxName id =
ApiEffect (DeleteMessage toMsg mailboxName id)
getGreeting : DataResult msg String -> Effect msg
getGreeting toMsg =
ApiEffect (GetGreeting toMsg)
getHeaderList : DataResult msg (List MessageHeader) -> String -> Effect msg
getHeaderList toMsg mailboxName =
ApiEffect (GetHeaderList toMsg mailboxName)
getServerConfig : DataResult msg ServerConfig -> Effect msg
getServerConfig toMsg =
ApiEffect (GetServerConfig toMsg)
getServerMetrics : DataResult msg Metrics -> Effect msg
getServerMetrics toMsg =
ApiEffect (GetServerMetrics toMsg)
getMessage : DataResult msg Message -> String -> String -> Effect msg
getMessage toMsg mailboxName id =
ApiEffect (GetMessage toMsg mailboxName id)
markMessageSeen : HttpResult msg -> String -> String -> Effect msg
markMessageSeen toMsg mailboxName id =
ApiEffect (MarkMessageSeen toMsg mailboxName id)
posixTime : (Time.Posix -> msg) -> Effect msg
posixTime toMsg =
PosixTime toMsg
purgeMailbox : HttpResult msg -> String -> Effect msg
purgeMailbox toMsg mailboxName =
ApiEffect (PurgeMailbox toMsg mailboxName)
{-| Schedules a Timer to fire after the specified delay.
-}
schedule : (Timer -> msg) -> Timer -> Float -> Effect msg
schedule toMsg timer millis =
ScheduleTimer toMsg timer millis
{-| Updates the browsers displayed URL to the specified route, and triggers the route to be
handled by the frontend.
-}
navigateRoute : Bool -> Route -> Effect msg
navigateRoute pushHistory route =
RouteNavigate pushHistory route
{-| Updates the browsers displayed URL to the specified route. Does not trigger our own route
handling.
-}
updateRoute : Route -> Effect msg
updateRoute route =
RouteUpdate route
-- UTILITY
batchPerform : Effect msg -> ( Session, List (Cmd msg) ) -> ( Session, List (Cmd msg) )
batchPerform effect ( session, cmds ) =
perform ( session, effect )
|> Tuple.mapSecond (\cmd -> cmd :: cmds)
+17 -35
View File
@@ -1,7 +1,7 @@
module Layout exposing (Model, Msg, Page(..), frame, init, reset, update) module Layout exposing (Model, Msg, Page(..), frame, init, reset, update)
import Browser.Navigation as Nav
import Data.Session as Session exposing (Session) import Data.Session as Session exposing (Session)
import Effect exposing (Effect)
import Html import Html
exposing exposing
( Attribute ( Attribute
@@ -39,7 +39,7 @@ import Html.Attributes
) )
import Html.Events as Events import Html.Events as Events
import Modal import Modal
import Route exposing (Route) import Route
import Timer exposing (Timer) import Timer exposing (Timer)
@@ -96,46 +96,31 @@ type Msg
| RecentMenuToggled | RecentMenuToggled
update : Msg -> Model msg -> Session -> ( Model msg, Session, Cmd msg ) update : Msg -> Model msg -> ( Model msg, Effect msg )
update msg model session = update msg model =
case msg of case msg of
ClearFlash -> ClearFlash ->
( model ( model, Effect.clearFlash )
, Session.clearFlash session
, Cmd.none
)
MainMenuToggled -> MainMenuToggled ->
( { model | mainMenuVisible = not model.mainMenuVisible } ( { model | mainMenuVisible = not model.mainMenuVisible }, Effect.none )
, session
, Cmd.none
)
ModalFocused message -> ModalFocused message ->
( model ( model, Effect.focusModalResult message )
, Modal.updateSession message session
, Cmd.none
)
ModalUnfocused -> ModalUnfocused ->
( model, session, Modal.resetFocusCmd (ModalFocused >> model.mapMsg) ) ( model, Effect.focusModal (ModalFocused >> model.mapMsg) )
OnMailboxNameInput name -> OnMailboxNameInput name ->
( { model | mailboxName = name } ( { model | mailboxName = name }, Effect.none )
, session
, Cmd.none
)
OpenMailbox -> OpenMailbox ->
if model.mailboxName == "" then if model.mailboxName == "" then
( model, session, Cmd.none ) ( model, Effect.none )
else else
( model ( model
, session , Effect.navigateRoute True (Route.Mailbox model.mailboxName)
, Route.Mailbox model.mailboxName
|> session.router.toPath
|> Nav.pushUrl session.key
) )
RecentMenuMouseOver -> RecentMenuMouseOver ->
@@ -143,20 +128,19 @@ update msg model session =
| recentMenuVisible = True | recentMenuVisible = True
, recentMenuTimer = Timer.cancel model.recentMenuTimer , recentMenuTimer = Timer.cancel model.recentMenuTimer
} }
, session , Effect.none
, Cmd.none
) )
RecentMenuMouseOut -> RecentMenuMouseOut ->
let let
-- Keep the recent menu open for a moment even if the mouse leaves it.
newTimer = newTimer =
Timer.replace model.recentMenuTimer Timer.replace model.recentMenuTimer
in in
( { model ( { model
| recentMenuTimer = newTimer | recentMenuTimer = newTimer
} }
, session , Effect.schedule (RecentMenuTimeout >> model.mapMsg) newTimer 400
, Timer.schedule (RecentMenuTimeout >> model.mapMsg) newTimer 400
) )
RecentMenuTimeout timer -> RecentMenuTimeout timer ->
@@ -165,18 +149,16 @@ update msg model session =
| recentMenuVisible = False | recentMenuVisible = False
, recentMenuTimer = Timer.cancel timer , recentMenuTimer = Timer.cancel timer
} }
, session , Effect.none
, Cmd.none
) )
else else
-- Timer was no longer valid. -- Timer was no longer valid.
( model, session, Cmd.none ) ( model, Effect.none )
RecentMenuToggled -> RecentMenuToggled ->
( { model | recentMenuVisible = not model.recentMenuVisible } ( { model | recentMenuVisible = not model.recentMenuVisible }
, session , Effect.none
, Cmd.none
) )
+58 -50
View File
@@ -4,6 +4,7 @@ import Browser exposing (Document, UrlRequest)
import Browser.Navigation as Nav import Browser.Navigation as Nav
import Data.AppConfig as AppConfig exposing (AppConfig) import Data.AppConfig as AppConfig exposing (AppConfig)
import Data.Session as Session exposing (Session) import Data.Session as Session exposing (Session)
import Effect exposing (Effect)
import Html exposing (Html) import Html exposing (Html)
import Json.Decode as D exposing (Value) import Json.Decode as D exposing (Value)
import Layout import Layout
@@ -58,6 +59,8 @@ init configValue location key =
Session.initError key location (D.errorToString error) Session.initError key location (D.errorToString error)
( subModel, _ ) = ( subModel, _ ) =
-- Home.init effect is discarded because this subModel will be immediately replaced
-- when we change routes to the specified location.
Home.init session Home.init session
initModel = initModel =
@@ -67,11 +70,9 @@ init configValue location key =
route = route =
session.router.fromUrl location session.router.fromUrl location
( model, cmd ) =
changeRouteTo route initModel
in in
( model, Cmd.batch [ cmd, Task.perform TimeZoneLoaded Time.here ] ) changeRouteTo route initModel
|> Tuple.mapSecond (\cmd -> Cmd.batch [ cmd, Task.perform TimeZoneLoaded Time.here ])
type Msg type Msg
@@ -198,20 +199,18 @@ updateMain msg model session =
LayoutMsg subMsg -> LayoutMsg subMsg ->
let let
( layout, newSession, cmd ) = ( layout, effect ) =
Layout.update subMsg model.layout session Layout.update subMsg model.layout
in in
( updateSession { model | layout = layout } newSession ( { model | layout = layout }, effect ) |> performEffects
, cmd
)
_ -> _ ->
updatePage msg model updatePage msg model |> performEffects
{-| Delegate incoming messages to their respective sub-pages. {-| Delegate incoming messages to their respective sub-pages.
-} -}
updatePage : Msg -> Model -> ( Model, Cmd Msg ) updatePage : Msg -> Model -> ( Model, Effect Msg )
updatePage msg model = updatePage msg model =
case ( msg, model.page ) of case ( msg, model.page ) of
( HomeMsg subMsg, Home subModel ) -> ( HomeMsg subMsg, Home subModel ) ->
@@ -232,61 +231,70 @@ updatePage msg model =
( _, _ ) -> ( _, _ ) ->
-- Disregard messages destined for the wrong page. -- Disregard messages destined for the wrong page.
( model, Cmd.none ) ( model, Effect.none )
changeRouteTo : Route -> Model -> ( Model, Cmd Msg ) changeRouteTo : Route -> Model -> ( Model, Cmd Msg )
changeRouteTo route model = changeRouteTo route model =
let let
session = session =
getSession model |> Session.clearFlash Session.clearFlash (getSession model)
newModel = newModel =
{ model | layout = Layout.reset model.layout } { model | layout = Layout.reset model.layout }
in in
case route of performEffects <|
Route.Home -> case route of
Home.init session Route.Home ->
|> updateWith Home HomeMsg newModel Home.init session
|> updateWith Home HomeMsg newModel
Route.Mailbox name -> Route.Mailbox name ->
Mailbox.init session name Nothing Mailbox.init session name Nothing
|> updateWith Mailbox MailboxMsg newModel |> updateWith Mailbox MailboxMsg newModel
Route.Message mailbox id -> Route.Message mailbox id ->
Mailbox.init session mailbox (Just id) Mailbox.init session mailbox (Just id)
|> updateWith Mailbox MailboxMsg newModel |> updateWith Mailbox MailboxMsg newModel
Route.Monitor -> Route.Monitor ->
if session.config.monitorVisible then if session.config.monitorVisible then
Monitor.init session Monitor.init session
|> updateWith Monitor MonitorMsg newModel |> updateWith Monitor MonitorMsg newModel
else else
let
flash =
{ title = "Disabled route requested"
, table = [ ( "Error", "Monitor disabled by configuration." ) ]
}
in
( applyToModelSession (Session.showFlash flash) newModel
, Effect.none
)
Route.Status ->
Status.init session
|> updateWith Status StatusMsg newModel
Route.Unknown path ->
-- Unknown routes display Home with an error flash.
let let
flash = flash =
{ title = "Disabled route requested" { title = "Unknown route requested"
, table = [ ( "Error", "Monitor disabled by configuration." ) ] , table = [ ( "Path", path ) ]
} }
in in
( applyToModelSession (Session.showFlash flash) newModel Home.init (Session.showFlash flash session)
, Cmd.none |> updateWith Home HomeMsg newModel
)
Route.Status ->
Status.init session
|> updateWith Status StatusMsg newModel
Route.Unknown path -> {-| Perform effects by updating model and/or producing Cmds to be executed.
-- Unknown routes display Home with an error flash. -}
let performEffects : ( Model, Effect Msg ) -> ( Model, Cmd Msg )
flash = performEffects ( model, effect ) =
{ title = "Unknown route requested" Effect.perform ( getSession model, effect )
, table = [ ( "Path", path ) ] |> Tuple.mapFirst (\newSession -> updateSession model newSession)
}
in
Home.init (Session.showFlash flash session)
|> updateWith Home HomeMsg newModel
getSession : Model -> Session getSession : Model -> Session
@@ -332,11 +340,11 @@ updateWith :
(subModel -> PageModel) (subModel -> PageModel)
-> (subMsg -> Msg) -> (subMsg -> Msg)
-> Model -> Model
-> ( subModel, Cmd subMsg ) -> ( subModel, Effect subMsg )
-> ( Model, Cmd Msg ) -> ( Model, Effect Msg )
updateWith toPage toMsg model ( subModel, subCmd ) = updateWith toPage toMsg model ( subModel, subEffect ) =
( { model | page = toPage subModel } ( { model | page = toPage subModel }
, Cmd.map toMsg subCmd , Effect.map toMsg subEffect
) )
+7 -9
View File
@@ -1,7 +1,7 @@
module Page.Home exposing (Model, Msg, init, update, view) module Page.Home exposing (Model, Msg, init, update, view)
import Api import Data.Session exposing (Session)
import Data.Session as Session exposing (Session) import Effect exposing (Effect)
import Html exposing (Html) import Html exposing (Html)
import Html.Attributes exposing (class, property) import Html.Attributes exposing (class, property)
import HttpUtil import HttpUtil
@@ -18,9 +18,9 @@ type alias Model =
} }
init : Session -> ( Model, Cmd Msg ) init : Session -> ( Model, Effect Msg )
init session = init session =
( Model session "", Api.getGreeting session GreetingLoaded ) ( Model session "", Effect.getGreeting GreetingLoaded )
@@ -31,16 +31,14 @@ type Msg
= GreetingLoaded (Result HttpUtil.Error String) = GreetingLoaded (Result HttpUtil.Error String)
update : Msg -> Model -> ( Model, Cmd Msg ) update : Msg -> Model -> ( Model, Effect Msg )
update msg model = update msg model =
case msg of case msg of
GreetingLoaded (Ok greeting) -> GreetingLoaded (Ok greeting) ->
( { model | greeting = greeting }, Cmd.none ) ( { model | greeting = greeting }, Effect.none )
GreetingLoaded (Err err) -> GreetingLoaded (Err err) ->
( { model | session = Session.showFlash (HttpUtil.errorFlash err) model.session } ( model, Effect.showFlash (HttpUtil.errorFlash err) )
, Cmd.none
)
+176 -155
View File
@@ -1,12 +1,12 @@
module Page.Mailbox exposing (Model, Msg, init, load, subscriptions, update, view) module Page.Mailbox exposing (Model, Msg, init, subscriptions, update, view)
import Api import Api
import Browser.Navigation as Nav
import Data.Message as Message exposing (Message) import Data.Message as Message exposing (Message)
import Data.MessageHeader exposing (MessageHeader) import Data.MessageHeader exposing (MessageHeader)
import Data.Session as Session exposing (Session) import Data.Session exposing (Session)
import DateFormat as DF import DateFormat as DF
import DateFormat.Relative as Relative import DateFormat.Relative as Relative
import Effect exposing (Effect)
import Html import Html
exposing exposing
( Attribute ( Attribute
@@ -54,7 +54,6 @@ import Json.Decode as D
import Json.Encode as E import Json.Encode as E
import Modal import Modal
import Route import Route
import Task
import Time exposing (Posix) import Time exposing (Posix)
import Timer exposing (Timer) import Timer exposing (Timer)
@@ -95,6 +94,7 @@ type alias Model =
{ session : Session { session : Session
, mailboxName : String , mailboxName : String
, state : State , state : State
, socketConnected : Bool
, bodyMode : Body , bodyMode : Body
, searchInput : String , searchInput : String
, promptPurge : Bool , promptPurge : Bool
@@ -103,27 +103,27 @@ type alias Model =
} }
init : Session -> String -> Maybe MessageID -> ( Model, Cmd Msg ) type alias ServeUrl =
List String -> String
init : Session -> String -> Maybe MessageID -> ( Model, Effect Msg )
init session mailboxName selection = init session mailboxName selection =
( { session = session ( { session = session
, mailboxName = mailboxName , mailboxName = mailboxName
, state = LoadingList selection , state = LoadingList selection
, socketConnected = False
, bodyMode = SafeHtmlBody , bodyMode = SafeHtmlBody
, searchInput = "" , searchInput = ""
, promptPurge = False , promptPurge = False
, markSeenTimer = Timer.empty , markSeenTimer = Timer.empty
, now = Time.millisToPosix 0 , now = Time.millisToPosix 0
} }
, load session mailboxName , Effect.batch
) [ Effect.posixTime Tick
, Effect.getHeaderList ListLoaded mailboxName
load : Session -> String -> Cmd Msg
load session mailboxName =
Cmd.batch
[ Task.perform Tick Time.now
, Api.getHeaderList session ListLoaded mailboxName
] ]
)
@@ -142,6 +142,7 @@ subscriptions _ =
type Msg type Msg
= ListLoaded (Result HttpUtil.Error (List MessageHeader)) = ListLoaded (Result HttpUtil.Error (List MessageHeader))
| ClickMessage MessageID | ClickMessage MessageID
| ClickRefresh
| ListKeyPress String Int | ListKeyPress String Int
| CloseMessage | CloseMessage
| MessageLoaded (Result HttpUtil.Error Message) | MessageLoaded (Result HttpUtil.Error Message)
@@ -159,38 +160,49 @@ type Msg
| ModalFocused Modal.Msg | ModalFocused Modal.Msg
update : Msg -> Model -> ( Model, Cmd Msg ) update : Msg -> Model -> ( Model, Effect Msg )
update msg model = update msg model =
case msg of case msg of
ClickMessage id -> ClickMessage id ->
( updateSelected { model | session = Session.disableRouting model.session } id ( updateSelected model id
, Cmd.batch , Effect.batch
[ -- Update browser location. [ -- Update browser location.
Route.Message model.mailboxName id Effect.updateRoute (Route.Message model.mailboxName id)
|> model.session.router.toPath , Effect.getMessage MessageLoaded model.mailboxName id
|> Nav.replaceUrl model.session.key
, Api.getMessage model.session MessageLoaded model.mailboxName id
] ]
) )
ClickRefresh ->
let
selection =
case model.state of
ShowingList _ (ShowingMessage message) ->
Just message.id
_ ->
Nothing
in
-- Reset to loading state, preserving the current message selection.
( { model | state = LoadingList selection }
, Effect.getHeaderList ListLoaded model.mailboxName
)
CloseMessage -> CloseMessage ->
case model.state of case model.state of
ShowingList list _ -> ShowingList list _ ->
( { model | state = ShowingList list NoMessage }, Cmd.none ) ( { model | state = ShowingList list NoMessage }, Effect.none )
_ -> _ ->
( model, Cmd.none ) ( model, Effect.none )
DeleteMessage message -> DeleteMessage message ->
updateDeleteMessage model message updateDeleteMessage model message
DeletedMessage (Ok _) -> DeletedMessage (Ok _) ->
( model, Cmd.none ) ( model, Effect.none )
DeletedMessage (Err err) -> DeletedMessage (Err err) ->
( { model | session = Session.showFlash (HttpUtil.errorFlash err) model.session } ( model, Effect.showFlash (HttpUtil.errorFlash err) )
, Cmd.none
)
ListKeyPress id keyCode -> ListKeyPress id keyCode ->
case keyCode of case keyCode of
@@ -198,79 +210,49 @@ update msg model =
updateOpenMessage model id updateOpenMessage model id
_ -> _ ->
( model, Cmd.none ) ( model, Effect.none )
ListLoaded (Ok headers) -> ListLoaded (Ok headers) ->
case model.state of updateListLoaded model headers
LoadingList selection ->
let
newModel =
{ model
| state = ShowingList (MessageList headers Nothing "") NoMessage
}
in
case selection of
Just id ->
updateOpenMessage newModel id
Nothing ->
( { newModel
| session = Session.addRecent model.mailboxName model.session
}
, Cmd.none
)
_ ->
( model, Cmd.none )
ListLoaded (Err err) -> ListLoaded (Err err) ->
( { model | session = Session.showFlash (HttpUtil.errorFlash err) model.session } ( model, Effect.showFlash (HttpUtil.errorFlash err) )
, Cmd.none
)
MarkSeenLoaded (Ok _) -> MarkSeenLoaded (Ok _) ->
( model, Cmd.none ) ( model, Effect.none )
MarkSeenLoaded (Err err) -> MarkSeenLoaded (Err err) ->
( { model | session = Session.showFlash (HttpUtil.errorFlash err) model.session } ( model, Effect.showFlash (HttpUtil.errorFlash err) )
, Cmd.none
)
MessageLoaded (Ok message) -> MessageLoaded (Ok message) ->
updateMessageResult model message updateMessageResult model message
MessageLoaded (Err err) -> MessageLoaded (Err err) ->
( { model | session = Session.showFlash (HttpUtil.errorFlash err) model.session } ( model, Effect.showFlash (HttpUtil.errorFlash err) )
, Cmd.none
)
MessageBody bodyMode -> MessageBody bodyMode ->
( { model | bodyMode = bodyMode }, Cmd.none ) ( { model | bodyMode = bodyMode }, Effect.none )
ModalFocused message -> ModalFocused message ->
( { model | session = Modal.updateSession message model.session } ( model, Effect.focusModalResult message )
, Cmd.none
)
OnSearchInput searchInput -> OnSearchInput searchInput ->
updateSearchInput model searchInput updateSearchInput model searchInput
PurgeMailboxPrompt -> PurgeMailboxPrompt ->
( { model | promptPurge = True }, Modal.resetFocusCmd ModalFocused ) ( { model | promptPurge = True }, Effect.focusModal ModalFocused )
PurgeMailboxCanceled -> PurgeMailboxCanceled ->
( { model | promptPurge = False }, Cmd.none ) ( { model | promptPurge = False }, Effect.none )
PurgeMailboxConfirmed -> PurgeMailboxConfirmed ->
updateTriggerPurge model updateTriggerPurge model
PurgedMailbox (Ok _) -> PurgedMailbox (Ok _) ->
( model, Cmd.none ) ( model, Effect.none )
PurgedMailbox (Err err) -> PurgedMailbox (Err err) ->
( { model | session = Session.showFlash (HttpUtil.errorFlash err) model.session } ( model, Effect.showFlash (HttpUtil.errorFlash err) )
, Cmd.none
)
MarkSeenTriggered timer -> MarkSeenTriggered timer ->
if timer == model.markSeenTimer then if timer == model.markSeenTimer then
@@ -278,15 +260,42 @@ update msg model =
updateMarkMessageSeen model updateMarkMessageSeen model
else else
( model, Cmd.none ) ( model, Effect.none )
Tick now -> Tick now ->
( { model | now = now }, Cmd.none ) ( { model | now = now }, Effect.none )
updateListLoaded : Model -> List MessageHeader -> ( Model, Effect Msg )
updateListLoaded model headers =
case model.state of
LoadingList selection ->
let
newModel =
{ model
| state = ShowingList (MessageList headers Nothing "") NoMessage
}
in
Effect.append (Effect.addRecent newModel.mailboxName) <|
case selection of
Just id ->
-- Don't try to load selected message if not present in headers.
if List.any (\header -> Just header.id == selection) headers then
updateOpenMessage newModel id
else
( newModel, Effect.updateRoute (Route.Mailbox model.mailboxName) )
Nothing ->
( newModel, Effect.none )
_ ->
( model, Effect.none )
{-| Replace the currently displayed message. {-| Replace the currently displayed message.
-} -}
updateMessageResult : Model -> Message -> ( Model, Cmd Msg ) updateMessageResult : Model -> Message -> ( Model, Effect Msg )
updateMessageResult model message = updateMessageResult model message =
let let
bodyMode = bodyMode =
@@ -298,7 +307,7 @@ updateMessageResult model message =
in in
case model.state of case model.state of
LoadingList _ -> LoadingList _ ->
( model, Cmd.none ) ( model, Effect.none )
ShowingList list _ -> ShowingList list _ ->
let let
@@ -314,38 +323,26 @@ updateMessageResult model message =
, markSeenTimer = newTimer , markSeenTimer = newTimer
} }
-- Set 1500ms delay before reporting message as seen to backend. -- Set 1500ms delay before reporting message as seen to backend.
, Timer.schedule MarkSeenTriggered newTimer 1500 , Effect.schedule MarkSeenTriggered newTimer 1500
) )
{-| Updates model and triggers commands to purge this mailbox. {-| Updates model and triggers commands to purge this mailbox.
-} -}
updateTriggerPurge : Model -> ( Model, Cmd Msg ) updateTriggerPurge : Model -> ( Model, Effect Msg )
updateTriggerPurge model = updateTriggerPurge model =
let ( { model
cmd = | promptPurge = False
Cmd.batch , state = ShowingList (MessageList [] Nothing "") NoMessage
[ Route.Mailbox model.mailboxName }
|> model.session.router.toPath , Effect.batch
|> Nav.replaceUrl model.session.key [ Effect.updateRoute (Route.Mailbox model.mailboxName)
, Api.purgeMailbox model.session PurgedMailbox model.mailboxName , Effect.purgeMailbox PurgedMailbox model.mailboxName
] ]
in )
case model.state of
ShowingList _ _ ->
( { model
| promptPurge = False
, session = Session.disableRouting model.session
, state = ShowingList (MessageList [] Nothing "") NoMessage
}
, cmd
)
_ ->
( model, cmd )
updateSearchInput : Model -> String -> ( Model, Cmd Msg ) updateSearchInput : Model -> String -> ( Model, Effect Msg )
updateSearchInput model searchInput = updateSearchInput model searchInput =
let let
searchFilter = searchFilter =
@@ -357,14 +354,14 @@ updateSearchInput model searchInput =
in in
case model.state of case model.state of
LoadingList _ -> LoadingList _ ->
( model, Cmd.none ) ( model, Effect.none )
ShowingList list messageState -> ShowingList list messageState ->
( { model ( { model
| searchInput = searchInput | searchInput = searchInput
, state = ShowingList { list | searchFilter = searchFilter } messageState , state = ShowingList { list | searchFilter = searchFilter } messageState
} }
, Cmd.none , Effect.none
) )
@@ -396,7 +393,7 @@ updateSelected model id =
{ model | state = ShowingList newList (Transitioning visible) } { model | state = ShowingList newList (Transitioning visible) }
updateDeleteMessage : Model -> Message -> ( Model, Cmd Msg ) updateDeleteMessage : Model -> Message -> ( Model, Effect Msg )
updateDeleteMessage model message = updateDeleteMessage model message =
let let
filter f messageList = filter f messageList =
@@ -404,26 +401,20 @@ updateDeleteMessage model message =
in in
case model.state of case model.state of
ShowingList list _ -> ShowingList list _ ->
( { model ( { model | state = ShowingList (filter (\x -> x.id /= message.id) list) NoMessage }
| session = Session.disableRouting model.session , Effect.batch
, state = [ Effect.deleteMessage DeletedMessage message.mailbox message.id
ShowingList (filter (\x -> x.id /= message.id) list) NoMessage , Effect.updateRoute (Route.Mailbox model.mailboxName)
}
, Cmd.batch
[ Api.deleteMessage model.session DeletedMessage message.mailbox message.id
, Route.Mailbox model.mailboxName
|> model.session.router.toPath
|> Nav.replaceUrl model.session.key
] ]
) )
_ -> _ ->
( model, Cmd.none ) ( model, Effect.none )
{-| Updates both the active message, and the message list to mark the currently viewed message as seen. {-| Updates both the active message, and the message list to mark the currently viewed message as seen.
-} -}
updateMarkMessageSeen : Model -> ( Model, Cmd Msg ) updateMarkMessageSeen : Model -> ( Model, Effect Msg )
updateMarkMessageSeen model = updateMarkMessageSeen model =
case model.state of case model.state of
ShowingList messages (ShowingMessage visibleMessage) -> ShowingList messages (ShowingMessage visibleMessage) ->
@@ -442,21 +433,17 @@ updateMarkMessageSeen model =
| state = | state =
ShowingList newMessages (ShowingMessage { visibleMessage | seen = True }) ShowingList newMessages (ShowingMessage { visibleMessage | seen = True })
} }
, Api.markMessageSeen model.session MarkSeenLoaded visibleMessage.mailbox visibleMessage.id , Effect.markMessageSeen MarkSeenLoaded visibleMessage.mailbox visibleMessage.id
) )
_ -> _ ->
( model, Cmd.none ) ( model, Effect.none )
updateOpenMessage : Model -> String -> ( Model, Cmd Msg ) updateOpenMessage : Model -> String -> ( Model, Effect Msg )
updateOpenMessage model id = updateOpenMessage model id =
let ( updateSelected model id
newModel = , Effect.getMessage MessageLoaded model.mailboxName id
{ model | session = Session.addRecent model.mailboxName model.session }
in
( updateSelected newModel id
, Api.getMessage model.session MessageLoaded model.mailboxName id
) )
@@ -467,6 +454,10 @@ updateOpenMessage model id =
view : Model -> { title : String, modal : Maybe (Html Msg), content : List (Html Msg) } view : Model -> { title : String, modal : Maybe (Html Msg), content : List (Html Msg) }
view model = view model =
let let
serveUrl : ServeUrl
serveUrl =
Api.serveUrl model.session
mode = mode =
case model.state of case model.state of
ShowingList _ (ShowingMessage _) -> ShowingList _ (ShowingMessage _) ->
@@ -479,26 +470,7 @@ view model =
, modal = viewModal model.promptPurge , modal = viewModal model.promptPurge
, content = , content =
[ div [ class ("mailbox " ++ mode) ] [ div [ class ("mailbox " ++ mode) ]
[ aside [ class "message-list-controls" ] [ viewMessageListControls model
[ input
[ type_ "text"
, placeholder "search"
, Events.onInput OnSearchInput
, value model.searchInput
]
[]
, button
[ Events.onClick (OnSearchInput "")
, disabled (model.searchInput == "")
, alt "Clear Search"
]
[ i [ class "fas fa-times" ] [] ]
, button
[ Events.onClick PurgeMailboxPrompt
, alt "Purge Mailbox"
]
[ i [ class "fas fa-trash" ] [] ]
]
, viewMessageList model , viewMessageList model
, main_ , main_
[ class "message" ] [ class "message" ]
@@ -510,10 +482,10 @@ view model =
) )
ShowingList _ (ShowingMessage message) -> ShowingList _ (ShowingMessage message) ->
viewMessage model.session model.session.zone message model.bodyMode viewMessage serveUrl model.session.zone message model.bodyMode
ShowingList _ (Transitioning message) -> ShowingList _ (Transitioning message) ->
viewMessage model.session model.session.zone message model.bodyMode viewMessage serveUrl model.session.zone message model.bodyMode
_ -> _ ->
text "" text ""
@@ -539,6 +511,53 @@ viewModal promptPurge =
Nothing Nothing
viewMessageListControls : Model -> Html Msg
viewMessageListControls model =
let
clearButton =
Just <|
button
[ Events.onClick (OnSearchInput "")
, disabled (model.searchInput == "")
, alt "Clear Search"
]
[ i [ class "fas fa-times" ] [] ]
purgeButton =
Just <|
button
[ Events.onClick PurgeMailboxPrompt
, alt "Purge Mailbox"
]
[ i [ class "fas fa-trash" ] [] ]
refreshButton =
if model.socketConnected then
Nothing
else
Just <|
button
[ Events.onClick ClickRefresh
, alt "Refresh Mailbox"
]
[ i [ class "fas fa-sync" ] [] ]
searchInput =
Just <|
input
[ type_ "text"
, placeholder "search"
, Events.onInput OnSearchInput
, value model.searchInput
]
[]
in
[ searchInput, clearButton, refreshButton, purgeButton ]
|> List.filterMap identity
|> aside [ class "message-list-controls" ]
viewMessageList : Model -> Html Msg viewMessageList : Model -> Html Msg
viewMessageList model = viewMessageList model =
aside [ class "message-list" ] <| aside [ class "message-list" ] <|
@@ -571,14 +590,14 @@ messageChip model selected message =
] ]
viewMessage : Session -> Time.Zone -> Message -> Body -> Html Msg viewMessage : ServeUrl -> Time.Zone -> Message -> Body -> Html Msg
viewMessage session zone message bodyMode = viewMessage serveUrl zone message bodyMode =
let let
htmlUrl = htmlUrl =
Api.serveUrl session [ "mailbox", message.mailbox, message.id, "html" ] serveUrl [ "mailbox", message.mailbox, message.id, "html" ]
sourceUrl = sourceUrl =
Api.serveUrl session [ "mailbox", message.mailbox, message.id, "source" ] serveUrl [ "mailbox", message.mailbox, message.id, "source" ]
htmlButton = htmlButton =
if message.html == "" then if message.html == "" then
@@ -609,7 +628,7 @@ viewMessage session zone message bodyMode =
] ]
, messageErrors message , messageErrors message
, messageBody message bodyMode , messageBody message bodyMode
, attachments session message , attachments serveUrl message
] ]
@@ -672,20 +691,22 @@ messageBody message bodyMode =
] ]
attachments : Session -> Message -> Html Msg attachments : ServeUrl -> Message -> Html Msg
attachments session message = attachments serveUrl message =
if List.isEmpty message.attachments then if List.isEmpty message.attachments then
div [] [] text ""
else else
table [ class "attachments well" ] (List.map (attachmentRow session message) message.attachments) message.attachments
|> List.map (attachmentRow serveUrl message)
|> table [ class "attachments well" ]
attachmentRow : Session -> Message -> Message.Attachment -> Html Msg attachmentRow : ServeUrl -> Message -> Message.Attachment -> Html Msg
attachmentRow session message attach = attachmentRow serveUrl message attach =
let let
url = url =
Api.serveUrl session serveUrl
[ "mailbox" [ "mailbox"
, message.mailbox , message.mailbox
, message.id , message.id
+13 -17
View File
@@ -1,10 +1,10 @@
module Page.Monitor exposing (Model, Msg, init, update, view) module Page.Monitor exposing (Model, Msg, init, update, view)
import Api import Api
import Browser.Navigation as Nav
import Data.MessageHeader as MessageHeader exposing (MessageHeader) import Data.MessageHeader as MessageHeader exposing (MessageHeader)
import Data.Session as Session exposing (Session) import Data.Session exposing (Session)
import DateFormat as DF import DateFormat as DF
import Effect exposing (Effect)
import Html import Html
exposing exposing
( Attribute ( Attribute
@@ -41,9 +41,9 @@ type alias Model =
} }
init : Session -> ( Model, Cmd Msg ) init : Session -> ( Model, Effect Msg )
init session = init session =
( Model session False [], Cmd.none ) ( Model session False [], Effect.none )
@@ -58,20 +58,20 @@ type Msg
| MessageKeyPress MessageHeader Int | MessageKeyPress MessageHeader Int
update : Msg -> Model -> ( Model, Cmd Msg ) update : Msg -> Model -> ( Model, Effect Msg )
update msg model = update msg model =
case msg of case msg of
Connected True -> Connected True ->
( { model | connected = True, messages = [] }, Cmd.none ) ( { model | connected = True, messages = [] }, Effect.none )
Connected False -> Connected False ->
( { model | connected = False }, Cmd.none ) ( { model | connected = False }, Effect.none )
MessageReceived value -> MessageReceived value ->
case D.decodeValue (MessageHeader.decoder |> D.at [ "detail" ]) value of case D.decodeValue (MessageHeader.decoder |> D.at [ "detail" ]) value of
Ok header -> Ok header ->
( { model | messages = header :: List.take 500 model.messages } ( { model | messages = header :: List.take 500 model.messages }
, Cmd.none , Effect.none
) )
Err err -> Err err ->
@@ -81,12 +81,10 @@ update msg model =
, table = [ ( "Error", D.errorToString err ) ] , table = [ ( "Error", D.errorToString err ) ]
} }
in in
( { model | session = Session.showFlash flash model.session } ( model, Effect.showFlash flash )
, Cmd.none
)
Clear -> Clear ->
( { model | messages = [] }, Cmd.none ) ( { model | messages = [] }, Effect.none )
OpenMessage header -> OpenMessage header ->
openMessage header model openMessage header model
@@ -97,15 +95,13 @@ update msg model =
openMessage header model openMessage header model
_ -> _ ->
( model, Cmd.none ) ( model, Effect.none )
openMessage : MessageHeader -> Model -> ( Model, Cmd Msg ) openMessage : MessageHeader -> Model -> ( Model, Effect Msg )
openMessage header model = openMessage header model =
( model ( model
, Route.Message header.mailbox header.id , Effect.navigateRoute True (Route.Message header.mailbox header.id)
|> model.session.router.toPath
|> Nav.replaceUrl model.session.key
) )
+15 -18
View File
@@ -1,10 +1,10 @@
module Page.Status exposing (Model, Msg, init, subscriptions, update, view) module Page.Status exposing (Model, Msg, init, subscriptions, update, view)
import Api
import Data.Metrics exposing (Metrics) import Data.Metrics exposing (Metrics)
import Data.ServerConfig exposing (ServerConfig) import Data.ServerConfig exposing (ServerConfig)
import Data.Session as Session exposing (Session) import Data.Session exposing (Session)
import DateFormat.Relative as Relative import DateFormat.Relative as Relative
import Effect exposing (Effect)
import Filesize import Filesize
import Html import Html
exposing exposing
@@ -19,7 +19,6 @@ import Html.Attributes exposing (class)
import HttpUtil import HttpUtil
import Sparkline as Spark import Sparkline as Spark
import Svg.Attributes as SvgAttrib import Svg.Attributes as SvgAttrib
import Task
import Time exposing (Posix) import Time exposing (Posix)
@@ -60,7 +59,7 @@ type alias Metric =
} }
init : Session -> ( Model, Cmd Msg ) init : Session -> ( Model, Effect Msg )
init session = init session =
( { session = session ( { session = session
, now = Time.millisToPosix 0 , now = Time.millisToPosix 0
@@ -82,9 +81,9 @@ init session =
, retainedCount = Metric "Stored Messages" 0 fmtInt graphZero initDataSet 60 , retainedCount = Metric "Stored Messages" 0 fmtInt graphZero initDataSet 60
, retainedSize = Metric "Store Size" 0 Filesize.format graphZero initDataSet 60 , retainedSize = Metric "Store Size" 0 Filesize.format graphZero initDataSet 60
} }
, Cmd.batch , Effect.batch
[ Task.perform Tick Time.now [ Effect.posixTime Tick
, Api.getServerConfig session ServerConfigLoaded , Effect.getServerConfig ServerConfigLoaded
] ]
) )
@@ -114,27 +113,25 @@ type Msg
| Tick Posix | Tick Posix
update : Msg -> Model -> ( Model, Cmd Msg ) update : Msg -> Model -> ( Model, Effect Msg )
update msg model = update msg model =
case msg of case msg of
MetricsReceived (Ok metrics) -> MetricsReceived (Ok metrics) ->
( updateMetrics metrics model, Cmd.none ) ( updateMetrics metrics model, Effect.none )
MetricsReceived (Err err) -> MetricsReceived (Err err) ->
( { model | session = Session.showFlash (HttpUtil.errorFlash err) model.session } ( model, Effect.showFlash (HttpUtil.errorFlash err) )
, Cmd.none
)
ServerConfigLoaded (Ok config) -> ServerConfigLoaded (Ok config) ->
( { model | config = Just config }, Cmd.none ) ( { model | config = Just config }, Effect.none )
ServerConfigLoaded (Err err) -> ServerConfigLoaded (Err err) ->
( { model | session = Session.showFlash (HttpUtil.errorFlash err) model.session } ( model, Effect.showFlash (HttpUtil.errorFlash err) )
, Cmd.none
)
Tick time -> Tick time ->
( { model | now = time }, Api.getServerMetrics model.session MetricsReceived ) ( { model | now = time }
, Effect.getServerMetrics MetricsReceived
)
{-| Update all metrics in Model; increment xCounter. {-| Update all metrics in Model; increment xCounter.
@@ -416,7 +413,7 @@ viewMetric metric =
, div [ class "value" ] [ text (metric.formatter metric.value) ] , div [ class "value" ] [ text (metric.formatter metric.value) ]
, div [ class "graph" ] , div [ class "graph" ]
[ metric.graph metric.history [ metric.graph metric.history
, text ("(" ++ String.fromInt metric.minutes ++ "min)") , text (" (" ++ String.fromInt metric.minutes ++ "min)")
] ]
] ]
+2
View File
@@ -49,6 +49,8 @@ cancel previous =
previous previous
{-| Increments the timer identity, preventing integer overflow.
-}
next : Int -> Int next : Int -> Int
next index = next index =
if index > 2 ^ 30 then if index > 2 ^ 30 then
+1
View File
@@ -3,6 +3,7 @@ import './mailbox.css'
import './navbar.css' import './navbar.css'
import '@fortawesome/fontawesome-free/css/all.css' import '@fortawesome/fontawesome-free/css/all.css'
import '@webcomponents/webcomponentsjs/webcomponents-bundle' import '@webcomponents/webcomponentsjs/webcomponents-bundle'
import 'opensans-npm-webfont'
import { Elm } from './Main.elm' import { Elm } from './Main.elm'
import './monitorMessages' import './monitorMessages'
import './renderedHtml' import './renderedHtml'
+15
View File
@@ -98,9 +98,14 @@
border-width: 1px; border-width: 1px;
border-style: none solid solid solid; border-style: none solid solid solid;
cursor: pointer; cursor: pointer;
outline: none;
padding: 5px 8px; padding: 5px 8px;
} }
.message-list-entry:focus {
background-color: var(--focused-bg-color) !important;
}
.message-list-entry.selected { .message-list-entry.selected {
background-color: var(--selected-color); background-color: var(--selected-color);
} }
@@ -109,6 +114,10 @@
border-style: solid; border-style: solid;
} }
.message-list-entry:focus .subject {
color: var(--focused-color);
}
.message-list-entry .subject { .message-list-entry .subject {
color: var(--high-color); color: var(--high-color);
} }
@@ -117,6 +126,12 @@
font-weight: bold; font-weight: bold;
} }
.message-list-entry:focus .from,
.message-list-entry:focus .date {
color: var(--focused-color);
opacity: 0.8;
}
.message-list-entry .from, .message-list-entry .from,
.message-list-entry .date { .message-list-entry .date {
color: var(--low-color); color: var(--low-color);
+12 -3
View File
@@ -9,6 +9,8 @@
--border-color: #ddd; --border-color: #ddd;
--placeholder-color: #9f9f9f; --placeholder-color: #9f9f9f;
--selected-color: #eee; --selected-color: #eee;
--focused-color: #fff;
--focused-bg-color: #337ab7;
} }
html, body, div, span, applet, object, iframe, html, body, div, span, applet, object, iframe,
@@ -46,8 +48,9 @@ body {
} }
body, button, input, table { body, button, input, table {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; font-family: "Open Sans", Helvetica, Arial, sans-serif;
font-size: 14px; font-size: 14px;
font-weight: 400;
line-height: 1.43; line-height: 1.43;
color: var(--primary-color); color: var(--primary-color);
} }
@@ -73,7 +76,7 @@ a.button {
font-size: 11px; font-size: 11px;
font-style: normal; font-style: normal;
margin: 4px; margin: 4px;
padding: 3px 8px; padding: 3px 8px 4px;
text-decoration: none; text-decoration: none;
text-shadow: 0 -1px 0 rgba(0,0,0,0.2); text-shadow: 0 -1px 0 rgba(0,0,0,0.2);
} }
@@ -245,7 +248,7 @@ h3 {
font-weight: 400; font-weight: 400;
height: 30px; height: 30px;
margin: 0; margin: 0;
padding: 5px 10px; padding: 5px 10px 6px;
text-align: center; text-align: center;
text-decoration: none; text-decoration: none;
text-shadow: 0 -1px 0 rgba(0,0,0,0.2); text-shadow: 0 -1px 0 rgba(0,0,0,0.2);
@@ -351,3 +354,9 @@ h3 {
background-color: var(--selected-color); background-color: var(--selected-color);
cursor: pointer; cursor: pointer;
} }
.monitor tr:focus {
color: var(--focused-color);
background-color: var(--focused-bg-color);
outline: none;
}
+1
View File
@@ -25,6 +25,7 @@
.navbar-brand { .navbar-brand {
font-size: 18px; font-size: 18px;
font-weight: 600;
} }
.navbar-toggle { .navbar-toggle {
-2
View File
@@ -35,12 +35,10 @@ module.exports = (env, argv) => {
], ],
}, },
{ {
include: [/\/src/, /\/node_modules\/@fortawesome\/fontawesome-free\/css/],
test: /\.css$/, test: /\.css$/,
loader: ['style-loader', 'css-loader'], loader: ['style-loader', 'css-loader'],
}, },
{ {
include: [/\/node_modules\/@fortawesome\/fontawesome-free\/webfonts/],
test: /\.(eot|svg|ttf|woff|woff2)$/, test: /\.(eot|svg|ttf|woff|woff2)$/,
loader: 'file-loader', loader: 'file-loader',
options: { options: {