diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..dd3ef83
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,20 @@
+/resources/public/js/**
+/resources/main.js
+figwheel_server.log
+pom.xml
+*jar
+/lib/
+/classes/
+/out/
+/target/
+.lein-deps-sum
+.lein-repl-history
+.lein-plugins/
+.repl
+.nrepl-port
+*~
+\#*\#
+.\#*
+/node_modules
+.DS_Store
+/out/
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..2e69496
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,13 @@
+.DEFAULT_GOAL := build
+
+install:
+ npm install
+ npm install -g electron-packager
+
+compile:
+ lein build
+
+build: install compile
+ electron-packager . Tomaat --out=out --ignore='\b(src\/tomaat|out|target|dev|Procfile~?)\b' --ignore='[.](clj|log)' --ignore='~$$' --overwrite --icon="tomato.icns"
+
+.PHONY: install
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a7a87a3
--- /dev/null
+++ b/README.md
@@ -0,0 +1,54 @@
+# Tomaat
+
+An electron app written in ClojureScript. Integrates with Slack to let people know you
+are doing great things! Uses [reagent](http://reagent-project.github.io/) for the UI.
+
+
+
+
+
+## Features
+
+* 25 minute pomodoro timer
+* Update Slack profile with tomato while pom is in progress
+* Send a Slack notification when pomodoro is complete
+
+Slack usage is optional, but if configured - your profile will be updated like so:
+
+
+
+
+
+## Development
+Tomaat uses [lein-figwheel](https://github.com/bhauman/lein-figwheel) for hot reloading of *all*
+source files. Since Tomaat leverages electron apis across the board - all builds target `:nodejs`.
+
+To start development process:
+
+```
+$ lein start
+$ lein electron
+```
+
+### Project structure
+All source files are under `src/tomaat`, and several build targets are laid out in `project.clj`. Tomaat follows
+a structure that allows for the main electron process, a renderer process, and a worker process.
+
+The worker process runs as an invisible window for the purpose of running the timer, persisting config, and interacting
+with slack.
+
+## Building
+Tomaat is packaged using [electron-packager](https://github.com/electron-userland/electron-packager). It has currently
+only been built and tested on macOS.
+
+Just use `make` to build Tomaat.
+
+```
+$ make
+```
+
+## Todo
+- [ ] webview for fetching slack token
+- [ ] add timer to tray
+- [ ] maybe play a sound on complete (useful if slack not configured)
+- [ ] test and build for other operating systems
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..d44f13b
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,1626 @@
+{
+ "name": "tomaat",
+ "version": "1.0.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@cljs-oss/module-deps": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@cljs-oss/module-deps/-/module-deps-1.1.1.tgz",
+ "integrity": "sha1-YmZ/KCFk8/EParnxJLpBb9EkOfo=",
+ "requires": {
+ "babel-traverse": "6.26.0",
+ "babylon": "6.18.0",
+ "browser-resolve": "1.11.2",
+ "cached-path-relative": "1.0.1",
+ "concat-stream": "1.5.2",
+ "defined": "1.0.0",
+ "detective": "4.5.0",
+ "duplexer2": "0.1.4",
+ "enhanced-resolve": "3.4.1",
+ "inherits": "2.0.3",
+ "JSONStream": "1.3.1",
+ "konan": "1.1.0",
+ "parents": "1.0.1",
+ "readable-stream": "2.3.3",
+ "resolve": "1.5.0",
+ "stream-combiner2": "1.1.1",
+ "subarg": "1.0.0",
+ "through2": "2.0.3",
+ "xtend": "4.0.1"
+ }
+ },
+ "@types/node": {
+ "version": "8.0.51",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-8.0.51.tgz",
+ "integrity": "sha512-El3+WJk2D/ppWNd2X05aiP5l2k4EwF7KwheknQZls+I26eSICoWRhRIJ56jGgw2dqNGQ5LtNajmBU2ajS28EvQ=="
+ },
+ "acorn": {
+ "version": "4.0.13",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz",
+ "integrity": "sha1-EFSVrlNh1pe9GVyCUZLhrX8lN4c="
+ },
+ "ajv": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.3.0.tgz",
+ "integrity": "sha1-RBT/dKUIecII7l/cgm4ywwNUnto=",
+ "requires": {
+ "co": "4.6.0",
+ "fast-deep-equal": "1.0.0",
+ "fast-json-stable-stringify": "2.0.0",
+ "json-schema-traverse": "0.3.1"
+ }
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+ "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+ },
+ "ansi-styles": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
+ "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
+ },
+ "array-find-index": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz",
+ "integrity": "sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E="
+ },
+ "asap": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+ "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY="
+ },
+ "asn1": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz",
+ "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y="
+ },
+ "assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
+ },
+ "asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
+ },
+ "aws-sign2": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
+ "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
+ },
+ "aws4": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz",
+ "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4="
+ },
+ "babel-code-frame": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
+ "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
+ "requires": {
+ "chalk": "1.1.3",
+ "esutils": "2.0.2",
+ "js-tokens": "3.0.2"
+ }
+ },
+ "babel-messages": {
+ "version": "6.23.0",
+ "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz",
+ "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=",
+ "requires": {
+ "babel-runtime": "6.26.0"
+ }
+ },
+ "babel-runtime": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
+ "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
+ "requires": {
+ "core-js": "2.5.1",
+ "regenerator-runtime": "0.11.0"
+ }
+ },
+ "babel-traverse": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz",
+ "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=",
+ "requires": {
+ "babel-code-frame": "6.26.0",
+ "babel-messages": "6.23.0",
+ "babel-runtime": "6.26.0",
+ "babel-types": "6.26.0",
+ "babylon": "6.18.0",
+ "debug": "2.6.9",
+ "globals": "9.18.0",
+ "invariant": "2.2.2",
+ "lodash": "4.17.4"
+ }
+ },
+ "babel-types": {
+ "version": "6.26.0",
+ "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
+ "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
+ "requires": {
+ "babel-runtime": "6.26.0",
+ "esutils": "2.0.2",
+ "lodash": "4.17.4",
+ "to-fast-properties": "1.0.3"
+ }
+ },
+ "babylon": {
+ "version": "6.18.0",
+ "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
+ "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ=="
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
+ "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
+ },
+ "bcrypt-pbkdf": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
+ "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=",
+ "optional": true,
+ "requires": {
+ "tweetnacl": "0.14.5"
+ }
+ },
+ "boom": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz",
+ "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=",
+ "requires": {
+ "hoek": "4.2.0"
+ }
+ },
+ "brace-expansion": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
+ "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=",
+ "requires": {
+ "balanced-match": "1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "browser-resolve": {
+ "version": "1.11.2",
+ "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.2.tgz",
+ "integrity": "sha1-j/CbCixCFxihBRwmCzLkj0QpOM4=",
+ "requires": {
+ "resolve": "1.1.7"
+ },
+ "dependencies": {
+ "resolve": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
+ "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs="
+ }
+ }
+ },
+ "builtin-modules": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
+ "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8="
+ },
+ "cached-path-relative": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/cached-path-relative/-/cached-path-relative-1.0.1.tgz",
+ "integrity": "sha1-0JxLUoAKpMB44t2BqGmqyQ0uVOc="
+ },
+ "camelcase": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
+ "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8="
+ },
+ "camelcase-keys": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-2.1.0.tgz",
+ "integrity": "sha1-MIvur/3ygRkFHvodkyITyRuPkuc=",
+ "requires": {
+ "camelcase": "2.1.1",
+ "map-obj": "1.0.1"
+ }
+ },
+ "caseless": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+ "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
+ },
+ "chalk": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
+ "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
+ "requires": {
+ "ansi-styles": "2.2.1",
+ "escape-string-regexp": "1.0.5",
+ "has-ansi": "2.0.0",
+ "strip-ansi": "3.0.1",
+ "supports-color": "2.0.0"
+ }
+ },
+ "co": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz",
+ "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
+ },
+ "code-point-at": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
+ },
+ "combined-stream": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz",
+ "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=",
+ "requires": {
+ "delayed-stream": "1.0.0"
+ }
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
+ },
+ "concat-stream": {
+ "version": "1.5.2",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.2.tgz",
+ "integrity": "sha1-cIl4Yk2FavQaWnQd790mHadSwmY=",
+ "requires": {
+ "inherits": "2.0.3",
+ "readable-stream": "2.0.6",
+ "typedarray": "0.0.6"
+ },
+ "dependencies": {
+ "readable-stream": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
+ "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=",
+ "requires": {
+ "core-util-is": "1.0.2",
+ "inherits": "2.0.3",
+ "isarray": "1.0.0",
+ "process-nextick-args": "1.0.7",
+ "string_decoder": "0.10.31",
+ "util-deprecate": "1.0.2"
+ }
+ }
+ }
+ },
+ "core-js": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz",
+ "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs="
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
+ },
+ "create-react-class": {
+ "version": "15.6.2",
+ "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.2.tgz",
+ "integrity": "sha1-zx7RXxKq1/FO9fLf4F5sQvke8Co=",
+ "requires": {
+ "fbjs": "0.8.16",
+ "loose-envify": "1.3.1",
+ "object-assign": "4.1.1"
+ }
+ },
+ "cryptiles": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz",
+ "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=",
+ "requires": {
+ "boom": "5.2.0"
+ },
+ "dependencies": {
+ "boom": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz",
+ "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==",
+ "requires": {
+ "hoek": "4.2.0"
+ }
+ }
+ }
+ },
+ "currently-unhandled": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/currently-unhandled/-/currently-unhandled-0.4.1.tgz",
+ "integrity": "sha1-mI3zP+qxke95mmE2nddsF635V+o=",
+ "requires": {
+ "array-find-index": "1.0.2"
+ }
+ },
+ "dashdash": {
+ "version": "1.14.1",
+ "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
+ "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
+ "requires": {
+ "assert-plus": "1.0.0"
+ }
+ },
+ "debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "requires": {
+ "ms": "2.0.0"
+ }
+ },
+ "decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
+ },
+ "deep-extend": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.4.2.tgz",
+ "integrity": "sha1-SLaZwn4zS/ifEIkr5DL25MfTSn8="
+ },
+ "defined": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz",
+ "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM="
+ },
+ "delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
+ },
+ "detective": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/detective/-/detective-4.5.0.tgz",
+ "integrity": "sha1-blqMaybmx6JUsca210kNmOyR7dE=",
+ "requires": {
+ "acorn": "4.0.13",
+ "defined": "1.0.0"
+ }
+ },
+ "duplexer2": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
+ "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=",
+ "requires": {
+ "readable-stream": "2.3.3"
+ }
+ },
+ "ecc-jsbn": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz",
+ "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=",
+ "optional": true,
+ "requires": {
+ "jsbn": "0.1.1"
+ }
+ },
+ "electron": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/electron/-/electron-1.8.1.tgz",
+ "integrity": "sha1-GbbznyAT4gSpGmC8MIbcekoH7Yg=",
+ "requires": {
+ "@types/node": "8.0.51",
+ "electron-download": "3.3.0",
+ "extract-zip": "1.6.6"
+ }
+ },
+ "electron-default-menu": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/electron-default-menu/-/electron-default-menu-1.0.1.tgz",
+ "integrity": "sha1-MXPFAY61B0BP7GO987eMOO7bqAg="
+ },
+ "electron-download": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/electron-download/-/electron-download-3.3.0.tgz",
+ "integrity": "sha1-LP1U1pZsAZxNSa1l++Zcyc3vaMg=",
+ "requires": {
+ "debug": "2.6.9",
+ "fs-extra": "0.30.0",
+ "home-path": "1.0.5",
+ "minimist": "1.2.0",
+ "nugget": "2.0.1",
+ "path-exists": "2.1.0",
+ "rc": "1.2.2",
+ "semver": "5.4.1",
+ "sumchecker": "1.3.1"
+ }
+ },
+ "encoding": {
+ "version": "0.1.12",
+ "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
+ "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=",
+ "requires": {
+ "iconv-lite": "0.4.19"
+ }
+ },
+ "enhanced-resolve": {
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz",
+ "integrity": "sha1-BCHjOf1xQZs9oT0Smzl5BAIwR24=",
+ "requires": {
+ "graceful-fs": "4.1.11",
+ "memory-fs": "0.4.1",
+ "object-assign": "4.1.1",
+ "tapable": "0.2.8"
+ }
+ },
+ "errno": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz",
+ "integrity": "sha1-uJbiOp5ei6M4cfyZar02NfyaHH0=",
+ "requires": {
+ "prr": "0.0.0"
+ }
+ },
+ "error-ex": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz",
+ "integrity": "sha1-+FWobOYa3E6GIcPNoh56dhLDqNw=",
+ "requires": {
+ "is-arrayish": "0.2.1"
+ }
+ },
+ "es6-promise": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.1.1.tgz",
+ "integrity": "sha512-OaU1hHjgJf+b0NzsxCg7NdIYERD6Hy/PEmFLTjw+b65scuisG3Kt4QoTvJ66BBkPZ581gr0kpoVzKnxniM8nng=="
+ },
+ "escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ="
+ },
+ "esutils": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
+ "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
+ },
+ "extend": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz",
+ "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ="
+ },
+ "extract-zip": {
+ "version": "1.6.6",
+ "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.6.6.tgz",
+ "integrity": "sha1-EpDt6NINCHK0Kf0/NRyhKOxe+Fw=",
+ "requires": {
+ "concat-stream": "1.6.0",
+ "debug": "2.6.9",
+ "mkdirp": "0.5.0",
+ "yauzl": "2.4.1"
+ },
+ "dependencies": {
+ "concat-stream": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz",
+ "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=",
+ "requires": {
+ "inherits": "2.0.3",
+ "readable-stream": "2.3.3",
+ "typedarray": "0.0.6"
+ }
+ }
+ }
+ },
+ "extsprintf": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+ "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
+ },
+ "fast-deep-equal": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz",
+ "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8="
+ },
+ "fast-json-stable-stringify": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
+ "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
+ },
+ "fbjs": {
+ "version": "0.8.16",
+ "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.16.tgz",
+ "integrity": "sha1-XmdDL1UNxBtXK/VYR7ispk5TN9s=",
+ "requires": {
+ "core-js": "1.2.7",
+ "isomorphic-fetch": "2.2.1",
+ "loose-envify": "1.3.1",
+ "object-assign": "4.1.1",
+ "promise": "7.3.1",
+ "setimmediate": "1.0.5",
+ "ua-parser-js": "0.7.17"
+ },
+ "dependencies": {
+ "core-js": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz",
+ "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY="
+ }
+ }
+ },
+ "fd-slicer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz",
+ "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=",
+ "requires": {
+ "pend": "1.2.0"
+ }
+ },
+ "find-up": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz",
+ "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=",
+ "requires": {
+ "path-exists": "2.1.0",
+ "pinkie-promise": "2.0.1"
+ }
+ },
+ "forever-agent": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+ "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
+ },
+ "form-data": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz",
+ "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=",
+ "requires": {
+ "asynckit": "0.4.0",
+ "combined-stream": "1.0.5",
+ "mime-types": "2.1.17"
+ }
+ },
+ "fs-extra": {
+ "version": "0.30.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz",
+ "integrity": "sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A=",
+ "requires": {
+ "graceful-fs": "4.1.11",
+ "jsonfile": "2.4.0",
+ "klaw": "1.3.1",
+ "path-is-absolute": "1.0.1",
+ "rimraf": "2.6.2"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+ },
+ "get-stdin": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
+ "integrity": "sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4="
+ },
+ "getpass": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
+ "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
+ "requires": {
+ "assert-plus": "1.0.0"
+ }
+ },
+ "glob": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
+ "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
+ "requires": {
+ "fs.realpath": "1.0.0",
+ "inflight": "1.0.6",
+ "inherits": "2.0.3",
+ "minimatch": "3.0.4",
+ "once": "1.4.0",
+ "path-is-absolute": "1.0.1"
+ }
+ },
+ "globals": {
+ "version": "9.18.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
+ "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ=="
+ },
+ "graceful-fs": {
+ "version": "4.1.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
+ "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
+ },
+ "har-schema": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
+ "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
+ },
+ "har-validator": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz",
+ "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=",
+ "requires": {
+ "ajv": "5.3.0",
+ "har-schema": "2.0.0"
+ }
+ },
+ "has-ansi": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
+ "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
+ "requires": {
+ "ansi-regex": "2.1.1"
+ }
+ },
+ "hawk": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz",
+ "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==",
+ "requires": {
+ "boom": "4.3.1",
+ "cryptiles": "3.1.2",
+ "hoek": "4.2.0",
+ "sntp": "2.1.0"
+ }
+ },
+ "hoek": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz",
+ "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ=="
+ },
+ "home-path": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/home-path/-/home-path-1.0.5.tgz",
+ "integrity": "sha1-eIspgVsS1Tus9XVkhHbm+QQdEz8="
+ },
+ "hosted-git-info": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.5.0.tgz",
+ "integrity": "sha512-pNgbURSuab90KbTqvRPsseaTxOJCZBD0a7t+haSN33piP9cCM4l0CqdzAif2hUqm716UovKB2ROmiabGAKVXyg=="
+ },
+ "http-signature": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
+ "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
+ "requires": {
+ "assert-plus": "1.0.0",
+ "jsprim": "1.4.1",
+ "sshpk": "1.13.1"
+ }
+ },
+ "iconv-lite": {
+ "version": "0.4.19",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz",
+ "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ=="
+ },
+ "indent-string": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-2.1.0.tgz",
+ "integrity": "sha1-ji1INIdCEhtKghi3oTfppSBJ3IA=",
+ "requires": {
+ "repeating": "2.0.1"
+ }
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
+ "requires": {
+ "once": "1.4.0",
+ "wrappy": "1.0.2"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4="
+ },
+ "ini": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.4.tgz",
+ "integrity": "sha1-BTfLedr1m1mhpRff9wbIbsA5Fi4="
+ },
+ "invariant": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz",
+ "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=",
+ "requires": {
+ "loose-envify": "1.3.1"
+ }
+ },
+ "is-arrayish": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
+ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0="
+ },
+ "is-builtin-module": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-1.0.0.tgz",
+ "integrity": "sha1-VAVy0096wxGfj3bDDLwbHgN6/74=",
+ "requires": {
+ "builtin-modules": "1.1.1"
+ }
+ },
+ "is-finite": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
+ "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=",
+ "requires": {
+ "number-is-nan": "1.0.1"
+ }
+ },
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+ "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+ "requires": {
+ "number-is-nan": "1.0.1"
+ }
+ },
+ "is-stream": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz",
+ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ="
+ },
+ "is-typedarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
+ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
+ },
+ "is-utf8": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
+ "integrity": "sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI="
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
+ },
+ "isomorphic-fetch": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz",
+ "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=",
+ "requires": {
+ "node-fetch": "1.7.3",
+ "whatwg-fetch": "2.0.3"
+ }
+ },
+ "isstream": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
+ "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
+ },
+ "js-tokens": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
+ "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls="
+ },
+ "jsbn": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
+ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
+ "optional": true
+ },
+ "json-schema": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
+ "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
+ },
+ "json-schema-traverse": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz",
+ "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A="
+ },
+ "json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
+ },
+ "jsonfile": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
+ "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
+ "requires": {
+ "graceful-fs": "4.1.11"
+ }
+ },
+ "jsonparse": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
+ "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA="
+ },
+ "JSONStream": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz",
+ "integrity": "sha1-cH92HgHa6eFvG8+TcDt4xwlmV5o=",
+ "requires": {
+ "jsonparse": "1.3.1",
+ "through": "2.3.8"
+ }
+ },
+ "jsprim": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
+ "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
+ "requires": {
+ "assert-plus": "1.0.0",
+ "extsprintf": "1.3.0",
+ "json-schema": "0.2.3",
+ "verror": "1.10.0"
+ }
+ },
+ "klaw": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz",
+ "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=",
+ "requires": {
+ "graceful-fs": "4.1.11"
+ }
+ },
+ "konan": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/konan/-/konan-1.1.0.tgz",
+ "integrity": "sha1-M3dDxLl7S9Hvi2KiSzFeuLxLIJQ=",
+ "requires": {
+ "babel-traverse": "6.26.0",
+ "babylon": "6.18.0"
+ }
+ },
+ "load-json-file": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz",
+ "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA=",
+ "requires": {
+ "graceful-fs": "4.1.11",
+ "parse-json": "2.2.0",
+ "pify": "2.3.0",
+ "pinkie-promise": "2.0.1",
+ "strip-bom": "2.0.0"
+ }
+ },
+ "lodash": {
+ "version": "4.17.4",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
+ "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4="
+ },
+ "loose-envify": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz",
+ "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=",
+ "requires": {
+ "js-tokens": "3.0.2"
+ }
+ },
+ "loud-rejection": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/loud-rejection/-/loud-rejection-1.6.0.tgz",
+ "integrity": "sha1-W0b4AUft7leIcPCG0Eghz5mOVR8=",
+ "requires": {
+ "currently-unhandled": "0.4.1",
+ "signal-exit": "3.0.2"
+ }
+ },
+ "map-obj": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz",
+ "integrity": "sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0="
+ },
+ "memory-fs": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
+ "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
+ "requires": {
+ "errno": "0.1.4",
+ "readable-stream": "2.3.3"
+ }
+ },
+ "meow": {
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/meow/-/meow-3.7.0.tgz",
+ "integrity": "sha1-cstmi0JSKCkKu/qFaJJYcwioAfs=",
+ "requires": {
+ "camelcase-keys": "2.1.0",
+ "decamelize": "1.2.0",
+ "loud-rejection": "1.6.0",
+ "map-obj": "1.0.1",
+ "minimist": "1.2.0",
+ "normalize-package-data": "2.4.0",
+ "object-assign": "4.1.1",
+ "read-pkg-up": "1.0.1",
+ "redent": "1.0.0",
+ "trim-newlines": "1.0.0"
+ }
+ },
+ "mime-db": {
+ "version": "1.30.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz",
+ "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE="
+ },
+ "mime-types": {
+ "version": "2.1.17",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz",
+ "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=",
+ "requires": {
+ "mime-db": "1.30.0"
+ }
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
+ "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "requires": {
+ "brace-expansion": "1.1.8"
+ }
+ },
+ "minimist": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
+ },
+ "mkdirp": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz",
+ "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=",
+ "requires": {
+ "minimist": "0.0.8"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
+ }
+ }
+ },
+ "ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+ },
+ "node-fetch": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
+ "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==",
+ "requires": {
+ "encoding": "0.1.12",
+ "is-stream": "1.1.0"
+ }
+ },
+ "normalize-package-data": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
+ "integrity": "sha512-9jjUFbTPfEy3R/ad/2oNbKtW9Hgovl5O1FvFWKkKblNXoN/Oou6+9+KKohPK13Yc3/TyunyWhJp6gvRNR/PPAw==",
+ "requires": {
+ "hosted-git-info": "2.5.0",
+ "is-builtin-module": "1.0.0",
+ "semver": "5.4.1",
+ "validate-npm-package-license": "3.0.1"
+ }
+ },
+ "nugget": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/nugget/-/nugget-2.0.1.tgz",
+ "integrity": "sha1-IBCVpIfhrTYIGzQy+jytpPjQcbA=",
+ "requires": {
+ "debug": "2.6.9",
+ "minimist": "1.2.0",
+ "pretty-bytes": "1.0.4",
+ "progress-stream": "1.2.0",
+ "request": "2.83.0",
+ "single-line-log": "1.1.2",
+ "throttleit": "0.0.2"
+ }
+ },
+ "number-is-nan": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+ "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
+ },
+ "oauth-sign": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz",
+ "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM="
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
+ },
+ "object-keys": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz",
+ "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY="
+ },
+ "once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
+ "requires": {
+ "wrappy": "1.0.2"
+ }
+ },
+ "parents": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parents/-/parents-1.0.1.tgz",
+ "integrity": "sha1-/t1NK/GTp3dF/nHjcdc8MwfZx1E=",
+ "requires": {
+ "path-platform": "0.11.15"
+ }
+ },
+ "parse-json": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
+ "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
+ "requires": {
+ "error-ex": "1.3.1"
+ }
+ },
+ "path-exists": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz",
+ "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=",
+ "requires": {
+ "pinkie-promise": "2.0.1"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
+ },
+ "path-parse": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz",
+ "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME="
+ },
+ "path-platform": {
+ "version": "0.11.15",
+ "resolved": "https://registry.npmjs.org/path-platform/-/path-platform-0.11.15.tgz",
+ "integrity": "sha1-6GQhf3TDaFDwhSt43Hv31KVyG/I="
+ },
+ "path-type": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
+ "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE=",
+ "requires": {
+ "graceful-fs": "4.1.11",
+ "pify": "2.3.0",
+ "pinkie-promise": "2.0.1"
+ }
+ },
+ "pend": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+ "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
+ },
+ "performance-now": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
+ "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
+ },
+ "pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw="
+ },
+ "pinkie": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
+ "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA="
+ },
+ "pinkie-promise": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
+ "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
+ "requires": {
+ "pinkie": "2.0.4"
+ }
+ },
+ "pretty-bytes": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-1.0.4.tgz",
+ "integrity": "sha1-CiLoIQYJrTVUL4yNXSFZr/B1HIQ=",
+ "requires": {
+ "get-stdin": "4.0.1",
+ "meow": "3.7.0"
+ }
+ },
+ "process-nextick-args": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz",
+ "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M="
+ },
+ "progress-stream": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/progress-stream/-/progress-stream-1.2.0.tgz",
+ "integrity": "sha1-LNPP6jO6OonJwSHsM0er6asSX3c=",
+ "requires": {
+ "speedometer": "0.1.4",
+ "through2": "0.2.3"
+ },
+ "dependencies": {
+ "isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
+ },
+ "readable-stream": {
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
+ "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
+ "requires": {
+ "core-util-is": "1.0.2",
+ "inherits": "2.0.3",
+ "isarray": "0.0.1",
+ "string_decoder": "0.10.31"
+ }
+ },
+ "through2": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-0.2.3.tgz",
+ "integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=",
+ "requires": {
+ "readable-stream": "1.1.14",
+ "xtend": "2.1.2"
+ }
+ },
+ "xtend": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz",
+ "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=",
+ "requires": {
+ "object-keys": "0.4.0"
+ }
+ }
+ }
+ },
+ "promise": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
+ "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
+ "requires": {
+ "asap": "2.0.6"
+ }
+ },
+ "prop-types": {
+ "version": "15.6.0",
+ "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.0.tgz",
+ "integrity": "sha1-zq8IMCL8RrSjX2nhPvda7Q1jmFY=",
+ "requires": {
+ "fbjs": "0.8.16",
+ "loose-envify": "1.3.1",
+ "object-assign": "4.1.1"
+ }
+ },
+ "prr": {
+ "version": "0.0.0",
+ "resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz",
+ "integrity": "sha1-GoS4WQgyVQFBGFPQCB7j+obikmo="
+ },
+ "punycode": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
+ "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
+ },
+ "qs": {
+ "version": "6.5.1",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz",
+ "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A=="
+ },
+ "rc": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.2.tgz",
+ "integrity": "sha1-2M6ctX6NZNnHut2YdsfDTL48cHc=",
+ "requires": {
+ "deep-extend": "0.4.2",
+ "ini": "1.3.4",
+ "minimist": "1.2.0",
+ "strip-json-comments": "2.0.1"
+ }
+ },
+ "react": {
+ "version": "15.6.2",
+ "resolved": "https://registry.npmjs.org/react/-/react-15.6.2.tgz",
+ "integrity": "sha1-26BDSrQ5z+gvEI8PURZjkIF5qnI=",
+ "requires": {
+ "create-react-class": "15.6.2",
+ "fbjs": "0.8.16",
+ "loose-envify": "1.3.1",
+ "object-assign": "4.1.1",
+ "prop-types": "15.6.0"
+ }
+ },
+ "react-dom": {
+ "version": "15.6.2",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-15.6.2.tgz",
+ "integrity": "sha1-Qc+t9pO3V/rycIRDodH9WgK+9zA=",
+ "requires": {
+ "fbjs": "0.8.16",
+ "loose-envify": "1.3.1",
+ "object-assign": "4.1.1",
+ "prop-types": "15.6.0"
+ }
+ },
+ "read-pkg": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
+ "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg=",
+ "requires": {
+ "load-json-file": "1.1.0",
+ "normalize-package-data": "2.4.0",
+ "path-type": "1.1.0"
+ }
+ },
+ "read-pkg-up": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz",
+ "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI=",
+ "requires": {
+ "find-up": "1.1.2",
+ "read-pkg": "1.1.0"
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz",
+ "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==",
+ "requires": {
+ "core-util-is": "1.0.2",
+ "inherits": "2.0.3",
+ "isarray": "1.0.0",
+ "process-nextick-args": "1.0.7",
+ "safe-buffer": "5.1.1",
+ "string_decoder": "1.0.3",
+ "util-deprecate": "1.0.2"
+ },
+ "dependencies": {
+ "string_decoder": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz",
+ "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==",
+ "requires": {
+ "safe-buffer": "5.1.1"
+ }
+ }
+ }
+ },
+ "redent": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/redent/-/redent-1.0.0.tgz",
+ "integrity": "sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94=",
+ "requires": {
+ "indent-string": "2.1.0",
+ "strip-indent": "1.0.1"
+ }
+ },
+ "regenerator-runtime": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz",
+ "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A=="
+ },
+ "repeating": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
+ "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
+ "requires": {
+ "is-finite": "1.0.2"
+ }
+ },
+ "request": {
+ "version": "2.83.0",
+ "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz",
+ "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==",
+ "requires": {
+ "aws-sign2": "0.7.0",
+ "aws4": "1.6.0",
+ "caseless": "0.12.0",
+ "combined-stream": "1.0.5",
+ "extend": "3.0.1",
+ "forever-agent": "0.6.1",
+ "form-data": "2.3.1",
+ "har-validator": "5.0.3",
+ "hawk": "6.0.2",
+ "http-signature": "1.2.0",
+ "is-typedarray": "1.0.0",
+ "isstream": "0.1.2",
+ "json-stringify-safe": "5.0.1",
+ "mime-types": "2.1.17",
+ "oauth-sign": "0.8.2",
+ "performance-now": "2.1.0",
+ "qs": "6.5.1",
+ "safe-buffer": "5.1.1",
+ "stringstream": "0.0.5",
+ "tough-cookie": "2.3.3",
+ "tunnel-agent": "0.6.0",
+ "uuid": "3.1.0"
+ }
+ },
+ "resolve": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.5.0.tgz",
+ "integrity": "sha512-hgoSGrc3pjzAPHNBg+KnFcK2HwlHTs/YrAGUr6qgTVUZmXv1UEXXl0bZNBKMA9fud6lRYFdPGz0xXxycPzmmiw==",
+ "requires": {
+ "path-parse": "1.0.5"
+ }
+ },
+ "rimraf": {
+ "version": "2.6.2",
+ "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz",
+ "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==",
+ "requires": {
+ "glob": "7.1.2"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz",
+ "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg=="
+ },
+ "semver": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz",
+ "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg=="
+ },
+ "setimmediate": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+ "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU="
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz",
+ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0="
+ },
+ "single-line-log": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/single-line-log/-/single-line-log-1.1.2.tgz",
+ "integrity": "sha1-wvg/Jzo+GhbtsJlWYdoO1e8DM2Q=",
+ "requires": {
+ "string-width": "1.0.2"
+ }
+ },
+ "sntp": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz",
+ "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==",
+ "requires": {
+ "hoek": "4.2.0"
+ }
+ },
+ "spdx-correct": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-1.0.2.tgz",
+ "integrity": "sha1-SzBz2TP/UfORLwOsVRlJikFQ20A=",
+ "requires": {
+ "spdx-license-ids": "1.2.2"
+ }
+ },
+ "spdx-expression-parse": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-1.0.4.tgz",
+ "integrity": "sha1-m98vIOH0DtRH++JzJmGR/O1RYmw="
+ },
+ "spdx-license-ids": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-1.2.2.tgz",
+ "integrity": "sha1-yd96NCRZSt5r0RkA1ZZpbcBrrFc="
+ },
+ "speedometer": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/speedometer/-/speedometer-0.1.4.tgz",
+ "integrity": "sha1-mHbb0qFp0xFUAtSObqYynIgWpQ0="
+ },
+ "sshpk": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz",
+ "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=",
+ "requires": {
+ "asn1": "0.2.3",
+ "assert-plus": "1.0.0",
+ "bcrypt-pbkdf": "1.0.1",
+ "dashdash": "1.14.1",
+ "ecc-jsbn": "0.1.1",
+ "getpass": "0.1.7",
+ "jsbn": "0.1.1",
+ "tweetnacl": "0.14.5"
+ }
+ },
+ "stream-combiner2": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/stream-combiner2/-/stream-combiner2-1.1.1.tgz",
+ "integrity": "sha1-+02KFCDqNidk4hrUeAOXvry0HL4=",
+ "requires": {
+ "duplexer2": "0.1.4",
+ "readable-stream": "2.3.3"
+ }
+ },
+ "string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+ "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+ "requires": {
+ "code-point-at": "1.1.0",
+ "is-fullwidth-code-point": "1.0.0",
+ "strip-ansi": "3.0.1"
+ }
+ },
+ "stringstream": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz",
+ "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg="
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+ "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+ "requires": {
+ "ansi-regex": "2.1.1"
+ }
+ },
+ "strip-bom": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
+ "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4=",
+ "requires": {
+ "is-utf8": "0.2.1"
+ }
+ },
+ "strip-indent": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-1.0.1.tgz",
+ "integrity": "sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI=",
+ "requires": {
+ "get-stdin": "4.0.1"
+ }
+ },
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo="
+ },
+ "subarg": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz",
+ "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=",
+ "requires": {
+ "minimist": "1.2.0"
+ }
+ },
+ "sumchecker": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-1.3.1.tgz",
+ "integrity": "sha1-ebs7RFbdBPGOvbwNcDodHa7FEF0=",
+ "requires": {
+ "debug": "2.6.9",
+ "es6-promise": "4.1.1"
+ }
+ },
+ "supports-color": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
+ "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
+ },
+ "tapable": {
+ "version": "0.2.8",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.2.8.tgz",
+ "integrity": "sha1-mTcqXJmb8t8WCvwNdL7U9HlIzSI="
+ },
+ "throttleit": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-0.0.2.tgz",
+ "integrity": "sha1-z+34jmDADdlpe2H90qg0OptoDq8="
+ },
+ "through": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+ "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
+ },
+ "through2": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.3.tgz",
+ "integrity": "sha1-AARWmzfHx0ujnEPzzteNGtlBQL4=",
+ "requires": {
+ "readable-stream": "2.3.3",
+ "xtend": "4.0.1"
+ }
+ },
+ "to-fast-properties": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
+ "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc="
+ },
+ "tough-cookie": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz",
+ "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=",
+ "requires": {
+ "punycode": "1.4.1"
+ }
+ },
+ "trim-newlines": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz",
+ "integrity": "sha1-WIeWa7WCpFA6QetST301ARgVphM="
+ },
+ "tunnel-agent": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
+ "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
+ "requires": {
+ "safe-buffer": "5.1.1"
+ }
+ },
+ "tweetnacl": {
+ "version": "0.14.5",
+ "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
+ "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
+ "optional": true
+ },
+ "typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
+ },
+ "ua-parser-js": {
+ "version": "0.7.17",
+ "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.17.tgz",
+ "integrity": "sha512-uRdSdu1oA1rncCQL7sCj8vSyZkgtL7faaw9Tc9rZ3mGgraQ7+Pdx7w5mnOSF3gw9ZNG6oc+KXfkon3bKuROm0g=="
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
+ },
+ "uuid": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz",
+ "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g=="
+ },
+ "validate-npm-package-license": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz",
+ "integrity": "sha1-KAS6vnEq0zeUWaz74kdGqywwP7w=",
+ "requires": {
+ "spdx-correct": "1.0.2",
+ "spdx-expression-parse": "1.0.4"
+ }
+ },
+ "verror": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
+ "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
+ "requires": {
+ "assert-plus": "1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "1.3.0"
+ }
+ },
+ "whatwg-fetch": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz",
+ "integrity": "sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ="
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+ },
+ "xtend": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",
+ "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68="
+ },
+ "yauzl": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz",
+ "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=",
+ "requires": {
+ "fd-slicer": "1.0.1"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..70c0d64
--- /dev/null
+++ b/package.json
@@ -0,0 +1,14 @@
+{
+ "name": "tomaat",
+ "version": "1.0.0",
+ "productName": "Tomaat",
+ "main": "resources/main.js",
+ "dependencies": {
+ "@cljs-oss/module-deps": "^1.1.1",
+ "create-react-class": "^15.6.2",
+ "electron": "^1.8.1",
+ "electron-default-menu": "^1.0.1",
+ "react": "^15.6.2",
+ "react-dom": "^15.6.2"
+ }
+}
diff --git a/project.clj b/project.clj
new file mode 100644
index 0000000..faae0fc
--- /dev/null
+++ b/project.clj
@@ -0,0 +1,133 @@
+(defproject tomaat "1.0.0"
+ :description "Pomodoro electron app written in ClojureScript"
+ :url "http://github.com/brianium/tomaat"
+ :license {:name "Eclipse Public License"
+ :url "http://www.eclipse.org/legal/epl-v10.html"}
+
+ :min-lein-version "2.7.1"
+
+ :dependencies [[org.clojure/clojure "1.9.0-alpha17"]
+ [org.clojure/clojurescript "1.9.908"]
+ [org.clojure/core.async "0.3.443"]
+ [cljsjs/nodejs-externs "1.0.4-1"]
+ [cljs-http "0.1.44"]
+ [reagent "0.8.0-alpha2"]]
+
+ :plugins [[lein-cljsbuild "1.1.7" :exclusions [[org.clojure/clojure]]]
+ [lein-shell "0.5.0"]
+ [lein-figwheel "0.5.14"]]
+
+ :source-paths ["src"]
+
+ :cljsbuild {:builds
+ [{:id "main-dev"
+ :source-paths ["src/tomaat"]
+ :figwheel {:on-jsload "tomaat.main.core/reload"}
+ :compiler {:main tomaat.main.core
+ :output-to "resources/main.js"
+ :output-dir "resources/public/js/main-out"
+ :optimizations :none
+ :install-deps true
+ :target :nodejs
+ :parallel-build true
+ :source-map true
+ :npm-deps {:electron "1.8.1"
+ :ws "3.3.1"}}}
+
+ {:id "main"
+ :source-paths ["src/tomaat"]
+ :compiler {:main tomaat.main.core
+ :optimizations :simple
+ :output-to "resources/main.js"
+ :output-dir "resources/public/js/main/out"
+ :install-deps true
+ :target :nodejs
+ :parallel-build true
+ :process-shim true
+ :pretty-print false
+ :npm-deps {:electron "1.8.1"}}}
+
+ {:id "ui-dev"
+ :source-paths ["src/tomaat"]
+ :figwheel true
+ :compiler {:main tomaat.ui.core
+ :output-to "resources/public/js/ui.js"
+ :output-dir "resources/public/js/ui-out"
+ :optimizations :none
+ :install-deps true
+ :hashbang false
+ :target :nodejs
+ :parallel-build true
+ :source-map true
+ :npm-deps {:electron "1.8.1"
+ :react "15.6.2"
+ :react-dom "15.6.2"
+ :electron-default-menu "1.0.1"
+ :create-react-class "15.6.2"}}}
+
+ {:id "ui"
+ :source-paths ["src/tomaat"]
+ :compiler {:main tomaat.ui.core
+ :optimizations :simple
+ :output-to "resources/public/js/ui.js"
+ :output-dir "resources/public/js/ui/out"
+ :install-deps true
+ :target :nodejs
+ :hashbang false
+ :parallel-build true
+ :process-shim true
+ :pretty-print false
+ :npm-deps {:electron "1.8.1"
+ :react "15.6.2"
+ :react-dom "15.6.2"
+ :electron-default-menu "1.0.1"
+ :create-react-class "15.6.2"}}}
+
+ {:id "worker-dev"
+ :source-paths ["src/tomaat"]
+ :figwheel true
+ :compiler {:main tomaat.worker.core
+ :output-to "resources/public/js/worker.js"
+ :output-dir "resources/public/js/worker-out"
+ :optimizations :none
+ :install-deps true
+ :hashbang false
+ :target :nodejs
+ :parallel-build true
+ :npm-deps {:electron "1.8.1"}}}
+
+ {:id "worker"
+ :source-paths ["src/tomaat"]
+ :compiler {:main tomaat.worker.core
+ :optimizations :simple
+ :output-to "resources/public/js/worker.js"
+ :output-dir "resources/public/js/worker/out"
+ :install-deps true
+ :hashbang false
+ :process-shim true
+ :target :nodejs
+ :parallel-build true
+ :npm-deps {:electron "1.8.1"}}}]}
+
+ :aliases {"electron" ["shell" "node_modules/.bin/electron" "resources/main.js"]
+ "build" ["do" "clean" ["cljsbuild" "once" "worker" "main" "ui"]]
+ "start" ["do" "clean" ["figwheel" "main-dev" "worker-dev" "ui-dev"]]}
+
+ :figwheel {:css-dirs ["resources/public/css"]
+ :nrepl-port 7888
+ :nrepl-middleware ["cider.nrepl/cider-middleware"
+ "cemerick.piggieback/wrap-cljs-repl"]}
+
+ ;; Setting up nREPL for Figwheel and ClojureScript dev
+ ;; Please see:
+ ;; https://github.com/bhauman/lein-figwheel/wiki/Using-the-Figwheel-REPL-within-NRepl
+ :profiles {:dev {:dependencies [[figwheel-sidecar "0.5.13"]
+ [com.cemerick/piggieback "0.2.2"]]
+ ;; need to add dev source path here to get user.clj loaded
+ :source-paths ["src/tomaat" "dev"]
+ :plugins [[cider/cider-nrepl "0.15.1-SNAPSHOT"]]
+ :repl-options {:nrepl-middleware [cemerick.piggieback/wrap-cljs-repl]}
+
+ :clean-targets ^{:protect false} ["resources/main.js"
+ "resources/public/js"
+ :target-path]}})
diff --git a/resources/public/css/Lato-Regular.ttf b/resources/public/css/Lato-Regular.ttf
new file mode 100755
index 0000000..04ea8ef
Binary files /dev/null and b/resources/public/css/Lato-Regular.ttf differ
diff --git a/resources/public/css/Pacifico-Regular.ttf b/resources/public/css/Pacifico-Regular.ttf
new file mode 100755
index 0000000..ccbe0f3
Binary files /dev/null and b/resources/public/css/Pacifico-Regular.ttf differ
diff --git a/resources/public/css/style.css b/resources/public/css/style.css
new file mode 100644
index 0000000..8f95688
--- /dev/null
+++ b/resources/public/css/style.css
@@ -0,0 +1,225 @@
+/**
+ * Colors from https://coolors.co/d0db97-69b578-5eb1bf-fada5e-ff6666
+ * Font "Pacifico" and "Lato" from Google Fonts
+ */
+
+/**
+ * Font Faces
+ */
+@font-face {
+ font-family: 'Pacifico';
+ font-style: normal;
+ font-weight: 400;
+ src: url('Pacifico-Regular.ttf');
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
+}
+
+@font-face {
+ font-family: 'Lato';
+ font-style: normal;
+ font-weight: 400;
+ src: url('Lato-Regular.ttf');
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215;
+}
+
+/**
+ * Base Styles
+ */
+html {
+ height: 500px;
+ width: 300px;
+ box-sizing: border-box;
+}
+
+*, *:before, *:after {
+ box-sizing: inherit;
+}
+
+body {
+ background-color: #D0DB97;
+ align-items: center;
+ height: 100%;
+ width: 100%;
+ color: #fff;
+ font-family: Lato;
+ margin: 0;
+ padding: 0;
+ text-align: center;
+ overflow: hidden;
+}
+
+/**
+ * Component styles
+ */
+.button {
+ background-color: #5EB1BF;
+ border: none;
+ border-radius: 4px;
+ box-shadow: 0 5px #46848F;
+ color: #fff;
+ cursor: pointer;
+ display: block;
+ font-size: 18px;
+ text-transform: uppercase;
+ text-shadow: 0 -1px 0 rgba(0,0,0,0.6);
+ padding: 20px 5px;
+ position: relative;
+ width: 100%;
+}
+
+.button:focus {
+ outline: none;
+}
+
+.button:hover {
+ top: 3px;
+ box-shadow: 0 3px #46848F;
+}
+
+.button:active {
+ top: 5px;
+ box-shadow: 0 0 #46848F;
+}
+
+.form {
+ text-align: left;
+ width: 100%;
+}
+
+.form__label {
+ display: block;
+ font-variant: small-caps;
+ margin-bottom: 5px;
+}
+
+.form__toggle {
+ position: relative;
+ display: flex;
+ align-items: center;
+}
+
+.form__toggle .label__input {
+ position: absolute;
+ left: -9999px;
+}
+
+.form__toggle:before {
+ content: '';
+ display: block;
+ width: 60px;
+ height: 25px;
+ border-radius: 20px;
+ background: #6bcadb;
+ position: absolute;
+ right: 0;
+}
+
+.form__toggle:after {
+ content: '';
+ cursor: pointer;
+ display: block;
+ width: 21px;
+ height: 21px;
+ border-radius: 100%;
+ background: #fff;
+ position:absolute;
+ right: 37px;
+ top: -1px;
+ transform: translateX(0);
+ transition: transform 200ms ease-in-out;
+}
+
+.form__toggle--checked:after {
+ transform: translateX(35px);
+}
+
+.form__input {
+ border: none;
+ border-radius: 4px;
+ color: #666;
+ display: block;
+ font-size: 16px;
+ padding: 10px;
+ width: 100%;
+}
+
+.form__input:disabled {
+ background-color: #ddd;
+}
+
+.header {
+ background: url('tomato.png') no-repeat top center / 60%;
+ display: flex;
+ align-items: center;
+ font-family: Pacifico;
+ font-size: 32px;
+ height: 190px;
+}
+
+.header__text {
+ margin: 0;
+ width: 100%;
+}
+
+.label {
+ margin-top: 20px;
+}
+
+.label__input {
+ margin-left: 15px;
+}
+
+.link {
+ background-color: transparent;
+ border: none;
+ color: #FADA5E;
+ cursor: pointer;
+ padding: 20px;
+ text-decoration: underline;
+ outline: none;
+}
+
+.container {
+ width: 300px;
+ height: 500px;
+ position: relative;
+ perspective: 400px;
+}
+
+.screen {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ transform: translateZ(0);
+ transform-style: preserve-3d;
+ transition: transform 300ms linear;
+}
+
+.screen--flipped {
+ transform: rotateY(180deg);
+}
+
+.screen__state {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ padding: 30px;
+ will-change: transform;
+ transform-style: preserve-3d;
+ backface-visibility: hidden;
+}
+
+.screen__main {
+ background-color: #69B578;
+}
+
+.screen__config {
+ background-color: #46848F;
+ transform: rotateY(180deg);
+}
+
+.timer {
+ font-size: 50px;
+ text-align: center;
+ margin-bottom: 30px;
+}
diff --git a/resources/public/css/tomato.png b/resources/public/css/tomato.png
new file mode 100644
index 0000000..f4b3e0d
Binary files /dev/null and b/resources/public/css/tomato.png differ
diff --git a/resources/public/index.html b/resources/public/index.html
new file mode 100644
index 0000000..8d027c1
--- /dev/null
+++ b/resources/public/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+ It's Tomaat!
+
+
+
+
+
+
+
diff --git a/resources/public/worker.html b/resources/public/worker.html
new file mode 100644
index 0000000..6776a11
--- /dev/null
+++ b/resources/public/worker.html
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/slack-screenshot.png b/slack-screenshot.png
new file mode 100644
index 0000000..163964c
Binary files /dev/null and b/slack-screenshot.png differ
diff --git a/src/tomaat/data.cljs b/src/tomaat/data.cljs
new file mode 100644
index 0000000..5572f4f
--- /dev/null
+++ b/src/tomaat/data.cljs
@@ -0,0 +1,35 @@
+(ns tomaat.data
+ (:require [fs :refer [writeFileSync readFileSync]]
+ [tomaat.util :refer [get-path]]))
+
+(defn db-path []
+ (get-path "userData" "tomaat.json"))
+
+(defn json-parse
+ [string]
+ (.parse js/JSON string))
+
+(defn json-stringify
+ [data]
+ (.stringify js/JSON data))
+
+(defn read []
+ (try
+ (-> (db-path)
+ readFileSync
+ json-parse
+ (js->clj :keywordize-keys true))
+ (catch js/Object e
+ {})))
+
+(defn- write-file-sync
+ [data path]
+ (writeFileSync path data))
+
+(defn write
+ [data]
+ (-> (read)
+ (merge data)
+ clj->js
+ json-stringify
+ (write-file-sync (db-path))))
diff --git a/src/tomaat/main/core.cljs b/src/tomaat/main/core.cljs
new file mode 100644
index 0000000..0464cbb
--- /dev/null
+++ b/src/tomaat/main/core.cljs
@@ -0,0 +1,43 @@
+(ns tomaat.main.core
+ (:require [cljs.nodejs :as nodejs]
+ [electron :refer [app Menu shell]]
+ [electron-default-menu]
+ [tomaat.util :as u]))
+
+(nodejs/enable-util-print!)
+
+(defonce *main-window (atom nil))
+
+(defn default-menu []
+ (let [menu (electron-default-menu app shell)]
+ (->> menu
+ (.buildFromTemplate Menu)
+ (.setApplicationMenu Menu))))
+
+(defn create-window []
+ (reset! *main-window (u/browser-window {:width 300
+ :height 500
+ :resizable false}))
+ (u/load-url @*main-window "index.html")
+ (u/on @*main-window "closed" #(reset! *main-window nil))
+ (default-menu))
+
+(defn quit-app []
+ (when-not (= "darwin" (.-platform js/process))
+ (u/quit)))
+
+(defn activate []
+ (when (nil? @*main-window)
+ (create-window)))
+
+(defn reload []
+ (when-not (nil? @*main-window)
+ (u/relaunch)
+ (u/quit)))
+
+(defn -main []
+ (u/on-app "ready" create-window)
+ (u/on-app "window-all-closed" quit-app)
+ (u/on-app "activate" activate))
+
+(set! *main-cli-fn* -main)
diff --git a/src/tomaat/ui/config.cljs b/src/tomaat/ui/config.cljs
new file mode 100644
index 0000000..ba37611
--- /dev/null
+++ b/src/tomaat/ui/config.cljs
@@ -0,0 +1,50 @@
+(ns tomaat.ui.config
+ "The config screen shows settings for Tomaat"
+ (:require [goog.string :as gstring]))
+
+(defn token-input
+ [{:keys [disabled? on-change token]}]
+ [:input#user-token.form__input
+ {:type "text"
+ :value token
+ :autoFocus true
+ :disabled disabled?
+ :on-change on-change}])
+
+(defn toggle [{:keys [disabled? checked? on-change id]}]
+ [:input.label__input
+ {:type "checkbox"
+ :id id
+ :checked checked?
+ :disabled disabled?
+ :on-change on-change}])
+
+(defn form [{:keys [disabled? update token slack-me]}]
+ [:form.form
+ [:label.form__label {:htmlFor "user-token"} "Slack Token"]
+ [token-input
+ {:disabled? disabled?
+ :update update
+ :token token
+ :on-change #(update :token (.. % -currentTarget -value))}]
+ [:label.form__label.label.form__toggle
+ {:htmlFor "notify"
+ :class (when slack-me "form__toggle--checked")}
+ "Slack me when time is up"
+ [toggle
+ {:id "notify"
+ :disabled? disabled?
+ :checked? slack-me
+ :on-change #(update :slack-me (.. % -currentTarget -checked))}]]])
+
+(defn config-screen [{:keys [toggle started? update data]}]
+ [:div.screen__config.screen__state
+ [:h1 "Settings"]
+ [form {:disabled? started?
+ :update update
+ :token (get data :token "")
+ :slack-me (get data :slack-me false)}]
+ [:button.link
+ {:on-click toggle
+ :type "button"}
+ (gstring/unescapeEntities "« back")]])
diff --git a/src/tomaat/ui/core.cljs b/src/tomaat/ui/core.cljs
new file mode 100644
index 0000000..a5ab4e9
--- /dev/null
+++ b/src/tomaat/ui/core.cljs
@@ -0,0 +1,76 @@
+(ns tomaat.ui.core
+ "Houses application state and defines actions and the main application component"
+ (:require [reagent.core :as reagent]
+ [goog.dom :refer [getElement]]
+ [tomaat.data :as data]
+ [tomaat.util :refer [on-ipc]]
+ [tomaat.ui.timer :refer [start-timer stop-timer]]
+ [tomaat.ui.settings :as settings]
+ [tomaat.ui.config :refer [config-screen]]
+ [tomaat.ui.main :refer [main-screen]]))
+
+(enable-console-print!)
+
+(defonce *state
+ (reagent/atom {:flipped? false
+ :started? false
+ :time [25 0]
+ :data {}}))
+
+;;; Actions
+(defn toggle-screen
+ "Changes the focused screen between the main and config screen"
+ [e]
+ (let [flipped? (:flipped? @*state)]
+ (.preventDefault e)
+ (if flipped?
+ (swap! *state assoc :flipped? false)
+ (swap! *state assoc :flipped? true))))
+
+(defn start []
+ (swap! *state assoc :started? true)
+ (start-timer))
+
+(defn stop []
+ (swap! *state assoc :started? false)
+ (stop-timer))
+
+(defn update-setting
+ [k v]
+ (swap! *state assoc-in [:data k] v)
+ (settings/update-setting k v))
+
+;;; Updates state based on messages from the worker process
+(on-ipc "time-changed" #(swap! *state assoc :time (js->clj %2)))
+(on-ipc "timer-complete" #(swap! *state assoc :started? false))
+
+;;; Application Component
+(defn tomaat []
+ (let [state @*state
+ started? (:started? state)]
+ [:div.container
+ [:div {:class (str
+ "screen"
+ (when (:flipped? state) " screen--flipped"))}
+ [main-screen
+ {:toggle toggle-screen
+ :start start
+ :stop stop
+ :started? started?
+ :time (:time state)}]
+ [config-screen
+ {:toggle toggle-screen
+ :started? started?
+ :update update-setting
+ :data (:data state)}]]]))
+
+(def app (with-meta tomaat
+ {:component-did-mount (swap! *state assoc :data (data/read))}))
+
+;;; Render Tomaat as node application
+(defn -main []
+ (reagent/render
+ [app]
+ (getElement "tomaat")))
+
+(set! *main-cli-fn* -main)
diff --git a/src/tomaat/ui/main.cljs b/src/tomaat/ui/main.cljs
new file mode 100644
index 0000000..0048662
--- /dev/null
+++ b/src/tomaat/ui/main.cljs
@@ -0,0 +1,31 @@
+(ns tomaat.ui.main
+ "Houses the main screen of Tomaat - that is the timer screen"
+ (:require [goog.string :as gstring]
+ goog.string.format))
+
+(defn header []
+ [:header.header
+ [:h1.header__text "Tomaat!"]])
+
+(defn timer
+ [time]
+ (->> time
+ (mapv #(gstring/format "%02d" %))
+ (interpose ":")
+ (into [:div.timer])))
+
+(defn button [{:keys [on-click]} & children]
+ [:button.button
+ {:type "button"
+ :on-click on-click}
+ children])
+
+(defn main-screen [{:keys [toggle start stop time started?]}]
+ [:div.screen__main.screen__state
+ [header]
+ [timer time]
+ [button
+ {:on-click (if started? stop start)}
+ (if started? "Stop Pomodoro" "Start Pomodoro")]
+ [:button.link {:type "button"
+ :on-click toggle} "settings"]])
diff --git a/src/tomaat/ui/settings.cljs b/src/tomaat/ui/settings.cljs
new file mode 100644
index 0000000..f5dee71
--- /dev/null
+++ b/src/tomaat/ui/settings.cljs
@@ -0,0 +1,10 @@
+(ns tomaat.ui.settings
+ (:require [tomaat.ui.worker :as worker]))
+
+(defn update-setting
+ "Tells the worker process to update a setting value"
+ [k v]
+ (->> [[k v]]
+ (into {})
+ clj->js
+ (worker/message! "update-settings")))
diff --git a/src/tomaat/ui/timer.cljs b/src/tomaat/ui/timer.cljs
new file mode 100644
index 0000000..8eade40
--- /dev/null
+++ b/src/tomaat/ui/timer.cljs
@@ -0,0 +1,12 @@
+(ns tomaat.ui.timer
+ (:require [tomaat.ui.worker :as worker]))
+
+(defn start-timer
+ "Starts the timer in the worker process"
+ []
+ (worker/message! "start-timer"))
+
+(defn stop-timer
+ "Stops the timer in the worker process"
+ []
+ (worker/message! "stop-timer"))
diff --git a/src/tomaat/ui/worker.cljs b/src/tomaat/ui/worker.cljs
new file mode 100644
index 0000000..47df21d
--- /dev/null
+++ b/src/tomaat/ui/worker.cljs
@@ -0,0 +1,39 @@
+(ns tomaat.ui.worker
+ (:require [tomaat.util :as u]))
+
+;;; Maintain a single reference to a worker window
+(defonce *worker (atom nil))
+
+(defn create-worker
+ "Creates a worker and calls the given on-load handler. Returns the
+ created worker."
+ [on-load & rest]
+ (let [worker (u/browser-window {:width 400 :height 400 :show false})
+ args (concat [worker] rest)]
+ (u/load-url worker "worker.html")
+ (-> worker
+ .-webContents
+ (u/on "did-finish-load" #(apply on-load args)))
+ worker))
+
+(defn send
+ "Send a message to a worker window"
+ [worker event-name & rest]
+ (let [id (u/current-window-id)
+ args (concat [event-name id] rest)]
+ (-> worker
+ .-webContents
+ .-send
+ (apply args))))
+
+(defn message!
+ "Sends a message to the remote worker. This function calls the send function - but
+ it manages the *worker atom - creating it if it does not exist, otherwise reusing it"
+ [event-name & rest]
+ (if (nil? @*worker)
+ (reset!
+ *worker
+ (create-worker
+ (fn [worker]
+ (apply send worker event-name rest))))
+ (apply send @*worker event-name rest)))
diff --git a/src/tomaat/util.cljs b/src/tomaat/util.cljs
new file mode 100644
index 0000000..1bcfd82
--- /dev/null
+++ b/src/tomaat/util.cljs
@@ -0,0 +1,68 @@
+(ns tomaat.util
+ "A namespace to hide interop details and electron use. Might be
+ a library some day"
+ (:require [electron :refer [BrowserWindow remote app ipcRenderer]]
+ [path]
+ [clojure.string :refer [includes?]]))
+
+(defn get-app []
+ (or app (.-app remote)))
+
+(defn get-path
+ [path-id filename]
+ (-> (get-app)
+ (.getPath path-id)
+ (path/join filename)))
+
+(defn- browser-window-ctor []
+ (or BrowserWindow (.-BrowserWindow remote)))
+
+(defn browser-window
+ [options]
+ (let [ctor (browser-window-ctor)]
+ (ctor. (clj->js options))))
+
+(defn get-url [filename]
+ (let [env (.. js/process -env -NODE_ENV)
+ dirname js/__dirname
+ public? (includes? dirname "public")
+ prefix (if public? "/" "/public/")]
+ (if (= env "production")
+ (str "file://" dirname prefix filename)
+ (->> (path/join dirname "../../../" filename)
+ (path/resolve)
+ (str "file://")))))
+
+(defn load-url
+ [window filename]
+ (.loadURL window (get-url filename)))
+
+(defn on
+ [emitter event-name handler]
+ (.on emitter event-name handler))
+
+(defn on-app
+ [event-name handler]
+ (on app event-name handler))
+
+(defn on-ipc
+ [event-name handler]
+ (on ipcRenderer event-name handler))
+
+(defn quit []
+ (.quit app))
+
+(defn relaunch []
+ (.relaunch app))
+
+(defn current-window-id []
+ (->> (browser-window-ctor)
+ .getFocusedWindow
+ .-id))
+
+(defn web-contents
+ "Get the webContents of a browser window identified by id"
+ [id]
+ (->> id
+ (.fromId (browser-window-ctor))
+ .-webContents))
diff --git a/src/tomaat/worker/core.cljs b/src/tomaat/worker/core.cljs
new file mode 100644
index 0000000..56ec183
--- /dev/null
+++ b/src/tomaat/worker/core.cljs
@@ -0,0 +1,18 @@
+(ns tomaat.worker.core
+ (:require [tomaat.util :refer [on-ipc]]
+ [tomaat.worker.timer :as timer]
+ [tomaat.worker.settings :as settings]
+ [tomaat.worker.slack :as slack]))
+
+(def start
+ (juxt timer/start slack/start-pomodoro))
+
+(def stop
+ (juxt timer/stop slack/stop-pomodoro))
+
+(defn -main []
+ (on-ipc "start-timer" start)
+ (on-ipc "stop-timer" stop)
+ (on-ipc "update-settings" settings/update-settings))
+
+(set! *main-cli-fn* -main)
diff --git a/src/tomaat/worker/settings.cljs b/src/tomaat/worker/settings.cljs
new file mode 100644
index 0000000..053dccb
--- /dev/null
+++ b/src/tomaat/worker/settings.cljs
@@ -0,0 +1,9 @@
+(ns tomaat.worker.settings
+ (:require [tomaat.data :as data]))
+
+(defn update-settings
+ "Merge the given settings into stored settings for Tomaat"
+ [event id settings]
+ (-> settings
+ (js->clj :keywordize-keys true)
+ data/write))
diff --git a/src/tomaat/worker/slack.cljs b/src/tomaat/worker/slack.cljs
new file mode 100644
index 0000000..650425b
--- /dev/null
+++ b/src/tomaat/worker/slack.cljs
@@ -0,0 +1,79 @@
+(ns tomaat.worker.slack
+ (:require-macros [cljs.core.async.macros :refer [go]])
+ (:require [cljs-http.client :as http]
+ [cljs.core.async :refer [> params
+ clj->js
+ (uri/appendParamsFromMap "")))
+
+(defn encode-json
+ "Converts a hash map to json and encodes the whole structure"
+ [data]
+ (.stringify js/JSON (clj->js data)))
+
+(defn url
+ "Create a slack api url"
+ [action params]
+ (-> "https://slack.com/api/"
+ (str action)
+ (str (query params))))
+
+(defn request
+ "Performs an http get request and returns a core.async channel"
+ [action params]
+ (http/get (url action params)))
+
+(defn with-token
+ "Executes the given function if a slack token has been stored"
+ [fn-request]
+ (let [settings (data/read)]
+ (when-let [token (:token settings)]
+ (fn-request token))))
+
+(defn update-profile
+ "Updates token user's profile with the given text and emoji"
+ [text emoji]
+ (with-token
+ #(request
+ "users.profile.set"
+ {:profile (encode-json {:status_text text
+ :status_emoji emoji})
+ :token %})))
+
+(defn start-pomodoro
+ "Updates the token user's profile with pom text and a tomato!"
+ [event id]
+ (update-profile "pom party" ":tomato:"))
+
+(defn stop-pomodoro
+ "Stopping the pomodoro clears the user profile of any pom related business"
+ ([]
+ (update-profile "" ""))
+ ([event id]
+ (stop-pomodoro)))
+
+(defn notify
+ "Notifies the user's slackbot channel that the pom is complete and it's time
+ to take a break from all their labors."
+ []
+ (with-token
+ (fn [token]
+ (go (let [response (js (minutes-and-seconds time)))
+ (when (= 0 time)
+ (.send contents "timer-complete")
+ (complete-pomodoro)
+ (clear-interval)
+ (js/setTimeout stop 2500)))
+
+(defn start
+ "Starts the timer interval and adds a watch that notifies the renderer process"
+ [event id]
+ (let [contents (web-contents id)]
+ (add-watch *time :time-changed #(time-changed contents %4))
+ (reset! *interval (js/setInterval #(tick *time) 1000))))
diff --git a/tomaat-example.gif b/tomaat-example.gif
new file mode 100644
index 0000000..1ec09d4
Binary files /dev/null and b/tomaat-example.gif differ
diff --git a/tomato.icns b/tomato.icns
new file mode 100644
index 0000000..50c34bd
Binary files /dev/null and b/tomato.icns differ