├── .babelrc ├── .gitignore ├── README.md ├── app ├── UserStore.js ├── dist │ ├── .gitkeep │ ├── fonts │ │ ├── MaterialIcons-Regular.012cf6a.woff │ │ ├── MaterialIcons-Regular.570eb83.woff2 │ │ ├── MaterialIcons-Regular.a37b0c0.ttf │ │ └── MaterialIcons-Regular.e79bfd8.eot │ ├── imgs │ │ └── pocket-icon.34cbd22.png │ ├── styles.css │ └── viewer.html ├── electron.js ├── icons │ ├── icon.icns │ └── icon.ico ├── index.ejs ├── package.json ├── src │ ├── App.vue │ ├── SharedStore.js │ ├── assets │ │ └── pocket-icon.png │ ├── bus.js │ ├── components │ │ ├── LandingPageView.vue │ │ ├── LandingPageView │ │ │ ├── PocketItem.vue │ │ │ └── Tag.vue │ │ └── Login.vue │ ├── config │ │ ├── AppSettings.js │ │ └── pocketApiConfig.js │ ├── main.js │ └── viewer.js ├── viewer-backup.ejs └── viewer.html ├── builds └── .gitkeep ├── config.js ├── icons-backup ├── icon.icns └── icon.ico ├── package.json ├── tasks ├── release.js └── runner.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015", "stage-0"], 3 | "plugins": ["transform-runtime"] 4 | } 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | app/dist/index.html 3 | app/dist/build.js 4 | builds/* 5 | node_modules 6 | npm-debug.log 7 | npm-debug.log.* 8 | thumbs.db 9 | !.gitkeep 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

PocketVue PocketVue Logo

2 | 3 | 4 | PocketVue is a desktop app for Mac uses Electron and Vue.js 2 5 | ![Transitions Gif](https://dl.dropboxusercontent.com/u/11825776/TagFiltersTransition-Github.gif) 6 | 7 | It is still in development. The initial goal was to explore the capabilities of using VUE and electron. 8 | 9 | However, the plan is to integrate more robust features that allow for editing and customization of a user's pocket articles. 10 | 11 | Right now, this is for Mac only. You can download the latest version at the [Releases Page](https://github.com/davidroyer/pocketvue/releases) 12 | 13 | If you ever want to disable remove authorization for PocketVue then you can visit the [Connected Services and Applications](https://getpocket.com/connected_applications) page on Pocket's Website and remove access. 14 | 15 | ## Build Setup 16 | 17 | ``` bash 18 | # install dependencies 19 | npm install 20 | 21 | # serve with hot reload at localhost:9080 22 | npm run dev 23 | 24 | # build electron app for production 25 | npm run build 26 | 27 | # run webpack in production 28 | npm run pack 29 | ``` 30 | -------------------------------------------------------------------------------- /app/UserStore.js: -------------------------------------------------------------------------------- 1 | const electron = require('electron'); 2 | const path = require('path'); 3 | const fs = require('fs'); 4 | 5 | class UserStore { 6 | constructor(opts) { 7 | // Renderer process has to get `app` module via `remote`, whereas the main process can get it directly 8 | // app.getPath('userData') will return a string of the user's app data directory path. 9 | const userDataPath = (electron.app || electron.remote.app).getPath('userData'); 10 | // We'll use the `configName` property to set the file name and path.join to bring it all together as a string 11 | this.path = path.join(userDataPath, opts.configName + '.json'); 12 | 13 | this.data = parseDataFile(this.path, opts.defaults); 14 | } 15 | 16 | // This will just return the property on the `data` object 17 | get(key) { 18 | return this.data[key]; 19 | } 20 | 21 | // ...and this will set it 22 | set(key, val) { 23 | this.data[key] = val; 24 | // Wait, I thought using the node.js' synchronous APIs was bad form? 25 | // We're not writing a server so there's not nearly the same IO demand on the process 26 | // Also if we used an async API and our app was quit before the asynchronous write had a chance to complete, 27 | // we might lose that data. Note that in a real app, we would try/catch this. 28 | fs.writeFileSync(this.path, JSON.stringify(this.data)); 29 | } 30 | 31 | nullify(key) { 32 | this.data[key] = null 33 | fs.writeFileSync(this.path, JSON.stringify(this.data)); 34 | } 35 | } 36 | 37 | function parseDataFile(filePath, defaults) { 38 | // We'll try/catch it in case the file doesn't exist yet, which will be the case on the first application run. 39 | // `fs.readFileSync` will return a JSON string which we then parse into a Javascript object 40 | try { 41 | return JSON.parse(fs.readFileSync(filePath)); 42 | } catch(error) { 43 | // if there was some kind of error, return the passed in defaults instead. 44 | return defaults; 45 | } 46 | } 47 | 48 | // expose the class 49 | module.exports = UserStore; 50 | -------------------------------------------------------------------------------- /app/dist/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidroyer/pocketvue/a63fb5ea5a1383e7509546e596a9d4edb076758f/app/dist/.gitkeep -------------------------------------------------------------------------------- /app/dist/fonts/MaterialIcons-Regular.012cf6a.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidroyer/pocketvue/a63fb5ea5a1383e7509546e596a9d4edb076758f/app/dist/fonts/MaterialIcons-Regular.012cf6a.woff -------------------------------------------------------------------------------- /app/dist/fonts/MaterialIcons-Regular.570eb83.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidroyer/pocketvue/a63fb5ea5a1383e7509546e596a9d4edb076758f/app/dist/fonts/MaterialIcons-Regular.570eb83.woff2 -------------------------------------------------------------------------------- /app/dist/fonts/MaterialIcons-Regular.a37b0c0.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidroyer/pocketvue/a63fb5ea5a1383e7509546e596a9d4edb076758f/app/dist/fonts/MaterialIcons-Regular.a37b0c0.ttf -------------------------------------------------------------------------------- /app/dist/fonts/MaterialIcons-Regular.e79bfd8.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidroyer/pocketvue/a63fb5ea5a1383e7509546e596a9d4edb076758f/app/dist/fonts/MaterialIcons-Regular.e79bfd8.eot -------------------------------------------------------------------------------- /app/dist/imgs/pocket-icon.34cbd22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidroyer/pocketvue/a63fb5ea5a1383e7509546e596a9d4edb076758f/app/dist/imgs/pocket-icon.34cbd22.png -------------------------------------------------------------------------------- /app/dist/styles.css: -------------------------------------------------------------------------------- 1 | .md-avatar{width:40px;min-width:40px;height:40px;min-height:40px;margin:auto;display:inline-block;overflow:hidden;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;position:relative;border-radius:40px;vertical-align:middle}.md-avatar.md-large{width:64px;min-width:64px;height:64px;min-height:64px;border-radius:64px}.md-avatar.md-large .md-icon{width:40px;min-width:40px;height:40px;min-height:40px;font-size:40px;line-height:40px}.md-avatar.md-avatar-icon{background-color:rgba(0,0,0,.38)}.md-avatar.md-avatar-icon .md-icon{color:#fff}.md-avatar .md-icon{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.md-avatar img{display:block}.md-avatar .md-ink-ripple{border-radius:50%}.md-avatar .md-ink-ripple .md-ripple.md-active{animation-duration:.9s}.md-avatar .md-tooltip.md-tooltip-top{margin-top:-8px}.md-avatar .md-tooltip.md-tooltip-right{margin-left:8px}.md-avatar .md-tooltip.md-tooltip-bottom{margin-top:8px}.md-avatar .md-tooltip.md-tooltip-left{margin-left:-8px}.md-bottom-bar{width:100%;min-width:100%;height:56px;-ms-flex-pack:center;justify-content:center;box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12);transition:all .4s cubic-bezier(.25,.8,.25,1)}.md-bottom-bar,.md-bottom-bar-item{position:relative;display:-ms-flexbox;display:flex}.md-bottom-bar-item{max-width:168px;min-width:80px;height:100%;padding:8px 12px 10px;-ms-flex-flow:column nowrap;flex-flow:column;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between;-ms-flex:1;flex:1;cursor:pointer;border:none;background:transparent;transform:translateZ(0);color:currentColor;font-family:inherit;font-size:14px;line-height:1em}.md-bottom-bar-item.md-active{padding-top:6px}.md-bottom-bar-item.md-active .md-text{transform:scale(1) translateZ(0)}.md-bottom-bar-item.md-active .md-icon,.md-bottom-bar-item.md-active .md-text{color:currentColor}.md-bottom-bar.md-shift .md-bottom-bar-item{min-width:56px;max-width:96px;position:static;-ms-flex:1 1 32px;flex:1 1 32px;transition:.4s cubic-bezier(.25,.8,.25,1);transition-property:flex,min-width,max-width;transition-property:flex,min-width,max-width,-ms-flex}.md-bottom-bar.md-shift .md-bottom-bar-item .md-icon{transform:translate3d(0,8px,0)}.md-bottom-bar.md-shift .md-bottom-bar-item .md-text{opacity:0;transform:scale(1) translate3d(0,6px,0)}.md-bottom-bar.md-shift .md-bottom-bar-item.md-active{min-width:96px;max-width:168px;-ms-flex:1 1 72px;flex:1 1 72px}.md-bottom-bar.md-shift .md-bottom-bar-item.md-active .md-icon,.md-bottom-bar.md-shift .md-bottom-bar-item.md-active .md-text{opacity:1}.md-bottom-bar.md-shift .md-bottom-bar-item.md-active .md-icon{transform:scale(1) translateZ(0)}.md-bottom-bar.md-shift .md-bottom-bar-item.md-active .md-text{transform:scale(1) translate3d(0,2px,0)}.md-bottom-bar-item .md-text{transform:scale(.8571) translateY(2px);transition:all .4s cubic-bezier(.25,.8,.25,1),color .08s linear,opacity .08s linear}.md-bottom-bar-item .md-icon{transition:all .4s cubic-bezier(.25,.8,.25,1),color .08s linear}.md-button{min-width:88px;min-height:36px;margin:6px 8px;padding:0 16px;display:inline-block;position:relative;overflow:hidden;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;cursor:pointer;background:none;border:0;border-radius:2px;transition:all .4s cubic-bezier(.25,.8,.25,1);color:currentColor;font-family:inherit;font-size:14px;font-style:inherit;font-variant:inherit;font-weight:500;line-height:36px;text-align:center;text-transform:uppercase;text-decoration:none;vertical-align:top;white-space:nowrap}.md-button,.md-button:focus{outline:none}.md-button:hover:not([disabled]):not(.md-raised){background-color:hsla(0,0%,60%,.2);text-decoration:none}.md-button:hover:not([disabled]).md-raised{background-color:rgba(0,0,0,.12)}.md-button:active:not([disabled]){background-color:hsla(0,0%,60%,.4)}.md-button.md-raised:not([disabled]){box-shadow:0 1px 5px rgba(0,0,0,.2),0 2px 2px rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.12)}.md-button.md-dense{min-height:32px;line-height:32px;font-size:13px}.md-button.md-fab .md-icon,.md-button.md-icon-button .md-icon{margin-top:1px;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}.md-button.md-icon-button{width:40px;min-width:40px;height:40px;margin:0 6px;padding:8px;border-radius:50%;line-height:24px}.md-button.md-icon-button:not([disabled]):hover{background:none}.md-button.md-icon-button.md-dense{width:32px;min-width:32px;height:32px;min-height:32px;padding:4px;line-height:32px}.md-button.md-icon-button .md-tooltip.md-tooltip-top{margin-top:-8px}.md-button.md-icon-button .md-tooltip.md-tooltip-right{margin-left:8px}.md-button.md-icon-button .md-tooltip.md-tooltip-bottom{margin-top:8px}.md-button.md-icon-button .md-tooltip.md-tooltip-left{margin-left:-8px}.md-button.md-icon-button .md-ink-ripple{border-radius:50%}.md-button.md-icon-button .md-ink-ripple .md-ripple{top:0!important;right:0!important;bottom:0!important;left:0!important}.md-button.md-icon-button .md-ripple.md-active{animation-duration:.9s}.md-button.md-fab{width:56px;height:56px;min-width:0;overflow:hidden;box-shadow:0 1px 5px rgba(0,0,0,.2),0 2px 2px rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.12);border-radius:56px;line-height:56px;background-clip:padding-box;transition:all .3s cubic-bezier(.55,0,.55,.2);transition-property:background-color,box-shadow,transform}.md-button.md-fab:focus,.md-button.md-fab:hover{box-shadow:0 3px 5px -1px rgba(0,0,0,.2),0 5px 8px rgba(0,0,0,.14),0 1px 14px rgba(0,0,0,.12)}.md-button.md-fab.md-fab-top-right{position:absolute;top:16px;right:16px}.md-button.md-fab.md-fab-top-left{position:absolute;top:16px;left:16px}.md-button.md-fab.md-fab-bottom-right{position:absolute;right:16px;bottom:16px}.md-button.md-fab.md-fab-bottom-left{position:absolute;left:16px;bottom:16px}.md-button.md-fab.md-mini{width:40px;height:40px;line-height:40px}.md-button.md-fab .md-ink-ripple{border-radius:56px}.md-button[disabled]{color:rgba(0,0,0,.26);cursor:default}.md-button[disabled].md-fab,.md-button[disabled].md-raised{background-color:rgba(0,0,0,.12)}.md-button[disabled].md-fab{box-shadow:none}.md-button:after{transition:all .4s cubic-bezier(.25,.8,.25,1)}.md-button .md-ink-ripple{border-radius:2px;background-clip:padding-box;overflow:hidden}.md-button-group{width:auto;display:-ms-flexbox;display:flex}.md-button-group>.md-button{margin:0;overflow:hidden;border-width:1px 0 1px 1px;border-radius:0;text-align:center;text-overflow:ellipsis;white-space:nowrap}.md-button-group>.md-button:first-child{border-radius:2px 0 0 2px}.md-button-group>.md-button:last-child{border-right-width:1px;border-radius:0 2px 2px 0}.md-button-group>.md-button .md-ink-ripple{border-radius:2px}.md-button.md-fab md-icon,.md-button.md-icon-button md-icon{display:block}.md-button-toggle .md-button:not([disabled]){color:rgba(0,0,0,.54)}.md-button-toggle .md-button:not([disabled]):hover:not(.md-toggle):not(.md-raised){background-color:hsla(0,0%,60%,.2);text-decoration:none}.md-card{overflow:auto;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;position:relative;z-index:1;border-radius:2px;box-shadow:0 1px 5px rgba(0,0,0,.2),0 2px 2px rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.12)}.md-card.md-with-hover{cursor:pointer;transition:all .4s cubic-bezier(.25,.8,.25,1);transition-property:box-shadow}.md-card.md-with-hover:hover{z-index:2;box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12)}.md-card .md-card-media{position:relative}.md-card .md-card-media.md-16-9{overflow:hidden}.md-card .md-card-media.md-16-9:before{width:100%;padding-top:56.25%;display:block;content:" "}.md-card .md-card-media.md-16-9 img{position:absolute;top:50%;right:0;left:0;transform:translateY(-50%)}.md-card .md-card-media.md-4-3{overflow:hidden}.md-card .md-card-media.md-4-3:before{width:100%;padding-top:75%;display:block;content:" "}.md-card .md-card-media.md-4-3 img{position:absolute;top:50%;right:0;left:0;transform:translateY(-50%)}.md-card .md-card-media.md-1-1{overflow:hidden}.md-card .md-card-media.md-1-1:before{width:100%;padding-top:100%;display:block;content:" "}.md-card .md-card-media.md-1-1 img{position:absolute;top:50%;right:0;left:0;transform:translateY(-50%)}.md-card .md-card-media+.md-card-header{padding-top:24px}.md-card .md-card-media+.md-card-content:last-child{padding-bottom:16px}.md-card .md-card-media img{width:100%}.md-card .md-card-header{padding:16px}.md-card .md-card-header.md-card-header-flex{display:-ms-flexbox;display:flex;-ms-flex-pack:justify;justify-content:space-between}.md-card .md-card-header+.md-card-content{padding-top:0}.md-card .md-card-header+.md-card-actions:not(:last-child){padding:0 8px}.md-card .md-card-header .md-avatar{margin-right:16px;float:left}.md-card .md-card-header .md-avatar~.md-title{font-size:14px}.md-card .md-card-header .md-avatar~.md-subhead,.md-card .md-card-header .md-avatar~.md-title{font-weight:500;line-height:20px}.md-card .md-card-header .md-button{margin:0}.md-card .md-card-header .md-button:last-child{margin-right:-4px}.md-card .md-card-header .md-button+.md-button{margin-left:8px}.md-card .md-card-header .md-card-header-text{-ms-flex:1;flex:1}.md-card .md-card-header .md-card-media{width:80px;-ms-flex:0 0 80px;flex:0 0 80px;height:80px;margin-left:16px}.md-card .md-card-header .md-card-media.md-medium{width:120px;-ms-flex:0 0 120px;flex:0 0 120px;height:120px}.md-card .md-card-header .md-card-media.md-big{width:160px;-ms-flex:0 0 160px;flex:0 0 160px;height:160px}.md-card .md-subhead,.md-card .md-subheading,.md-card .md-title{margin:0;font-weight:400}.md-card .md-subhead{opacity:.54;font-size:14px;letter-spacing:.01em;line-height:20px}.md-card .md-subhead+.md-title{margin-top:4px}.md-card .md-title{font-size:24px;letter-spacing:0;line-height:32px}.md-card .md-card-media-actions{padding:16px;display:-ms-flexbox;display:flex;-ms-flex-pack:justify;justify-content:space-between}.md-card .md-card-media-actions .md-card-media{width:240px;-ms-flex:0 0 240px;flex:0 0 240px;height:240px}.md-card .md-card-media-actions .md-card-actions{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:start;justify-content:flex-start;-ms-flex-align:center;align-items:center}.md-card .md-card-media-actions .md-card-actions .md-button+.md-button{margin:8px 0 0}.md-card .md-card-content{padding:16px;font-size:14px;line-height:22px}.md-card .md-card-content:last-child{padding-bottom:24px}.md-card .md-card-actions{padding:8px;display:-ms-flexbox;display:flex;-ms-flex-pack:end;justify-content:flex-end}.md-card .md-card-actions .md-button{margin:0}.md-card .md-card-actions .md-button:first-child{margin-left:0}.md-card .md-card-actions .md-button:last-child{margin-right:0}.md-card .md-card-actions .md-button+.md-button{margin-left:4px}.md-card .md-card-area,.md-card>.md-card-area:not(:last-child){position:relative}.md-card>.md-card-area:not(:last-child):after{height:1px;position:absolute;bottom:0;content:" "}.md-card>.md-card-area:not(:last-child):not(.md-inset):after{right:0;left:0}.md-card>.md-card-area:not(:last-child).md-inset:after{right:16px;left:16px}.md-card .md-card-media-cover{position:relative;color:#fff}.md-card .md-card-media-cover.md-text-scrim .md-backdrop{position:absolute;top:0;right:0;bottom:0;left:0;z-index:1}.md-card .md-card-media-cover .md-card-area{position:absolute;right:0;bottom:0;left:0;z-index:2}.md-card .md-card-media-cover .md-card-header+.md-card-actions{padding-top:0}.md-card .md-card-media-cover .md-subhead{opacity:1}.md-card .md-card-expand{overflow:hidden}.md-card .md-card-expand.md-active [md-expand-trigger]{transform:rotate(180deg) translate3D(0,0,0)}.md-card .md-card-expand.md-active .md-card-content{margin-top:0!important;opacity:1}.md-card .md-card-expand .md-card-actions{padding-top:0;position:relative;z-index:2}.md-card .md-card-expand .md-card-content,.md-card .md-card-expand [md-expand-trigger]{transition:all .4s cubic-bezier(.25,.8,.25,1)}.md-card .md-card-expand .md-card-content{padding-top:4px;position:relative;z-index:1;opacity:0;transform:translate3D(0,0,0)}.md-checkbox{width:auto;margin:16px 8px 16px 0;display:-ms-inline-flexbox;display:inline-flex;position:relative;transform:translate3D(0,0,0)}.md-checkbox .md-checkbox-container{width:20px;height:20px;position:relative;border-radius:2px;border:2px solid rgba(0,0,0,.54);transition:all .4s cubic-bezier(.25,.8,.25,1)}.md-checkbox .md-checkbox-container:after{width:6px;height:13px;position:absolute;top:0;left:5px;border:2px solid #fff;border-top:0;border-left:0;opacity:0;transform:rotate(45deg) scale3D(.15,.15,1);transition:all .3s cubic-bezier(.55,0,.55,.2);content:" "}.md-checkbox .md-checkbox-container input{position:absolute;left:-999em}.md-checkbox .md-checkbox-container .md-ink-ripple{top:-16px;right:-16px;bottom:-16px;left:-16px;border-radius:50%;color:rgba(0,0,0,.54)}.md-checkbox .md-checkbox-container .md-ink-ripple .md-ripple{width:48px!important;height:48px!important;top:0!important;right:0!important;bottom:0!important;left:0!important}.md-checkbox .md-checkbox-label{height:20px;padding-left:8px;line-height:20px}.md-checkbox.md-checked .md-checkbox-container:after{opacity:1;transform:rotate(45deg) scale3D(1,1,1);transition:all .4s cubic-bezier(.25,.8,.25,1)}.md-ink-ripple{pointer-events:none;overflow:hidden;position:absolute;top:0;right:0;bottom:0;left:0;-webkit-mask-image:radial-gradient(circle,#fff 100%,#000 0);mask-image:radial-gradient(circle,#fff 100%,#000 0);transition:all .3s cubic-bezier(.55,0,.55,.2)}.md-ripple{position:absolute;transform:scale(0);background-color:currentColor;opacity:.26;border-radius:50%}.md-ripple.md-active{animation:ripple 1s cubic-bezier(.25,.8,.25,1)}@keyframes ripple{to{transform:scale(1.5);opacity:0}}.md-divider{height:1px;margin:0;padding:0;display:block;border:0;background-color:rgba(0,0,0,.12)}.md-divider.md-inset{margin-left:72px}.md-icon{width:24px;min-width:24px;height:24px;min-height:24px;margin:auto;display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;fill:currentColor;vertical-align:middle}.md-input-container{min-height:48px;margin:4px 0 24px;padding-top:16px;position:relative}.md-input-container:after{height:1px;right:0;bottom:0;background-color:rgba(0,0,0,.12);content:" "}.md-input-container:after,.md-input-container label{position:absolute;left:0;transition:all .4s cubic-bezier(.25,.8,.25,1)}.md-input-container label{top:23px;pointer-events:none;transition-duration:.3s;color:rgba(0,0,0,.54);font-size:16px;line-height:20px}.md-input-container input,.md-input-container textarea{width:100%;height:32px;padding:0;display:block;border:none;background:none;transition:all .4s cubic-bezier(.25,.8,.25,1);transition-property:font-size;color:rgba(0,0,0,.54);font-family:inherit;font-size:1px;line-height:32px}.md-input-container input:focus,.md-input-container textarea:focus{outline:none}.md-input-container input::-webkit-input-placeholder,.md-input-container textarea::-webkit-input-placeholder{color:rgba(0,0,0,.54);font-size:16px;text-shadow:none;-webkit-text-fill-color:initial}.md-input-container textarea{min-height:32px;max-height:230px;padding:5px 0;line-height:1.3em;resize:none}.md-input-container .md-error{height:20px;display:block!important;position:absolute;opacity:0;transform:translate3d(0,-8px,0);transition:all .3s cubic-bezier(.55,0,.55,.2);font-size:12px}.md-input-container .md-count{height:20px;position:absolute;right:0;font-size:12px}.md-input-container.md-input-placeholder label{pointer-events:auto;top:10px;opacity:0;font-size:12px}.md-input-container.md-input-placeholder input,.md-input-container.md-input-placeholder textarea{font-size:16px}.md-input-container.md-has-value label,.md-input-container.md-input-focused label{pointer-events:auto;top:0;opacity:1;font-size:12px}.md-input-container.md-has-value input,.md-input-container.md-has-value textarea,.md-input-container.md-input-focused input,.md-input-container.md-input-focused textarea{font-size:16px}.md-input-container.md-has-value input,.md-input-container.md-has-value textarea{color:rgba(0,0,0,.87)}.md-input-container.md-input-inline label{pointer-events:none}.md-input-container.md-input-inline.md-input-focused label{top:23px;font-size:16px}.md-input-container.md-input-inline.md-has-value label{opacity:0}.md-input-container.md-input-disabled:after{background:0 100% repeat-x;background-image:linear-gradient(90deg,rgba(0,0,0,.38),rgba(0,0,0,.38) 33%,transparent 0);background-size:4px 1px}.md-input-container.md-input-disabled input,.md-input-container.md-input-disabled label,.md-input-container.md-input-disabled textarea{color:rgba(0,0,0,.38)}.md-input-container.md-has-password.md-input-focused .md-toggle-password{color:rgba(0,0,0,.54)}.md-input-container.md-has-password .md-toggle-password{margin:0;position:absolute;right:0;bottom:-2px;color:rgba(0,0,0,.38)}.md-input-container.md-has-password .md-toggle-password .md-ink-ripple{color:rgba(0,0,0,.87)}.md-input-container.md-input-invalid .md-error{opacity:1;transform:translateZ(0)}.md-input-container.md-input-required label:after{position:absolute;top:2px;right:0;transform:translateX(calc(100% + 2px));content:"*";font-size:12px;line-height:1em;vertical-align:top}.md-input-container.md-has-select:hover .md-select:after{color:rgba(0,0,0,.87)}.md-list{margin:0;padding:8px 0;display:-ms-flexbox;display:flex;-ms-flex-flow:column nowrap;flex-flow:column;position:relative;list-style:none}.md-list.md-dense{padding:4px 0}.md-list.md-dense .md-list-item.md-inset .md-list-item-container{padding-left:72px}.md-list.md-dense .md-list-item .md-list-item-container{min-height:40px;font-size:13px}.md-list.md-dense .md-list-item .md-list-item-container .md-avatar:first-child{margin-right:24px}.md-list.md-dense .md-avatar{width:32px;min-width:32px;height:32px;min-height:32px}.md-list.md-dense .md-list-item-expand{min-height:40px}.md-list.md-double-line.md-dense .md-list-item .md-list-item-container{min-height:60px}.md-list.md-double-line.md-dense .md-list-item .md-avatar{width:36px;min-width:36px;height:36px;min-height:36px}.md-list.md-double-line.md-dense .md-list-item .md-avatar:first-child{margin-right:20px}.md-list.md-double-line.md-dense .md-list-text-container>:nth-child(1),.md-list.md-double-line.md-dense .md-list-text-container>:nth-child(2){font-size:13px}.md-list.md-double-line .md-list-item .md-list-item-container{min-height:72px}.md-list.md-triple-line.md-dense .md-list-item .md-list-item-container{min-height:76px}.md-list.md-triple-line.md-dense .md-list-item .md-avatar{width:36px;min-width:36px;height:36px;min-height:36px}.md-list.md-triple-line.md-dense .md-list-item .md-avatar:first-child{margin-right:20px}.md-list.md-triple-line.md-dense .md-list-text-container>:nth-child(1),.md-list.md-triple-line.md-dense .md-list-text-container>:nth-child(2){font-size:13px}.md-list.md-triple-line .md-list-item .md-list-item-container{min-height:88px}.md-list.md-triple-line .md-avatar{margin:0}.md-list.md-triple-line .md-list-item-container{-ms-flex-align:start;align-items:flex-start}.md-list .md-subheader.md-inset{padding-left:72px}.md-list>.md-subheader:first-of-type{margin-top:-8px}.md-list-item{height:auto;position:relative}.md-list-item.md-inset .md-list-item-container{padding-left:72px}.md-list-item .md-list-item-holder{display:-ms-flexbox;display:flex;-ms-flex-flow:row nowrap;flex-flow:row;-ms-flex-align:center;align-items:center;-ms-flex:1;flex:1}.md-list-item .md-list-item-holder>.md-ink-ripple{border-radius:0}.md-list-item .md-list-item-holder>.md-icon:first-child{margin-right:32px}.md-list-item .md-list-item-holder .md-avatar:first-child{margin-right:16px}.md-list-item .md-list-item-holder .md-list-action{margin:0 -2px 0 0}.md-list-item .md-list-item-holder .md-list-action:nth-child(3){margin:0 -2px 0 16px}.md-list-item .md-list-item-container{width:100%;min-height:48px;margin:0;padding:0 16px;position:relative;border-radius:0;font-size:16px;font-weight:400;text-align:left;text-transform:none}.md-list-item .md-divider{position:absolute;bottom:0;right:0;left:0}.md-list-item .md-avatar,.md-list-item .md-icon{margin:0}.md-list-item .md-avatar:first-of-type+*,.md-list-item .md-icon:first-of-type+*{-ms-flex:1 1 auto;flex:1 1 auto}.md-list-item .md-avatar{margin-top:8px;margin-bottom:8px}.md-list-item .md-icon{color:rgba(0,0,0,.54)}.md-list-item-expand{min-height:48px;-ms-flex-flow:column wrap;flex-flow:column wrap;overflow:hidden}.md-list-item-expand:after,.md-list-item-expand:before{height:1px;position:absolute;right:0;left:0;z-index:3;transition:all .4s cubic-bezier(.25,.8,.25,1);content:" "}.md-list-item-expand:before{top:0}.md-list-item-expand:after{bottom:0}.md-list-item-expand.md-active{position:relative}.md-list-item-expand.md-active:after,.md-list-item-expand.md-active:before{background-color:rgba(0,0,0,.12)}.md-list-item-expand.md-active:first-of-type:before,.md-list-item-expand.md-active:last-of-type:after{background:none}.md-list-item-expand.md-active>.md-list-item-container .md-list-expand-indicator{transform:rotate(180deg) translate3D(0,0,0)}.md-list-item-expand.md-active>.md-list-expand{margin-bottom:0!important}.md-list-item-expand>.md-list-item-container>.md-list-item-holder{position:relative;z-index:2}.md-list-item-expand>.md-list-item-container>.md-list-item-holder>span{-ms-flex:1;flex:1}.md-list-item-expand .md-expansion-indicator,.md-list-item-expand .md-icon,.md-list-item-expand .md-list-item-container{transition:all .4s cubic-bezier(.25,.8,.25,1)}.md-list-item-expand .md-list-expand{position:relative;z-index:1;transform:translate3D(0,0,0);transition:all .5s cubic-bezier(.35,0,.25,1)}.md-list-item-expand .md-list-expand.md-transition-off{transition:none}.md-list-item-expand .md-list-expand .md-list{padding:0}.md-list-text-container{display:-ms-flexbox;display:flex;-ms-flex-flow:column nowrap;flex-flow:column;-ms-flex:1;flex:1;overflow:hidden;line-height:1.25em;text-overflow:ellipsis;white-space:normal}.md-list-text-container>:nth-child(1){font-size:16px}.md-list-text-container>:nth-child(2),.md-list-text-container>:nth-child(3){margin:0;color:rgba(0,0,0,.54);font-size:14px}.md-list-text-container>:nth-child(2):not(:last-child){color:rgba(0,0,0,.87)}.md-radio{width:auto;margin:16px 8px 16px 0;display:-ms-inline-flexbox;display:inline-flex;position:relative}.md-radio .md-radio-container{width:20px;height:20px;position:relative;border-radius:50%;border:2px solid rgba(0,0,0,.54);transition:all .4s cubic-bezier(.25,.8,.25,1)}.md-radio .md-radio-container:after{position:absolute;top:3px;right:3px;bottom:3px;left:3px;border-radius:50%;opacity:0;transform:scale3D(.38,.38,1);transition:all .3s cubic-bezier(.55,0,.55,.2);content:" "}.md-radio .md-radio-container input{position:absolute;left:-999em}.md-radio .md-radio-container .md-ink-ripple{top:-16px;right:-16px;bottom:-16px;left:-16px;border-radius:50%;color:rgba(0,0,0,.54)}.md-radio .md-radio-container .md-ink-ripple .md-ripple{width:48px!important;height:48px!important;top:0!important;right:0!important;bottom:0!important;left:0!important}.md-radio .md-radio-label{height:20px;padding-left:8px;line-height:20px}.md-radio.md-checked .md-radio-container:after{opacity:1;transform:scale3D(1,1,1);transition:all .4s cubic-bezier(.25,.8,.25,1)}.md-select{width:100%;min-width:128px;height:32px;position:relative}.md-select:focus{outline:none}.md-select:after{margin-top:2px;position:absolute;top:50%;right:0;transform:translateY(-50%) scaleY(.45) scaleX(.85);transition:all .08s linear;color:rgba(0,0,0,.54);content:"\25BC"}.md-select.md-active .md-select-menu{top:-8px;pointer-events:auto;opacity:1;transform:translateY(-8px) scale3D(1,1,1);transform-origin:center top;transition:all .4s cubic-bezier(.25,.8,.25,1);transition-duration:.25s;transition-property:opacity,transform,top}.md-select.md-active .md-select-menu>*{opacity:1;transition:all .3s cubic-bezier(.55,0,.55,.2);transition-duration:.15s;transition-delay:.1s}.md-select select{position:absolute;left:-999em}.md-select .md-select-value{width:100%;height:100%;padding-right:24px;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;cursor:pointer;position:relative;z-index:2;font-size:16px;line-height:1.2em;text-overflow:ellipsis;white-space:nowrap}.md-select .md-select-menu{min-width:156px;max-width:100%;min-height:48px;max-height:256px;display:-ms-flexbox;display:flex;-ms-flex-flow:column;flex-flow:column;-ms-flex-pack:stretch;justify-content:stretch;-ms-flex-line-pack:stretch;align-content:stretch;pointer-events:none;position:absolute;top:-16px;left:-16px;z-index:7;background-color:#fff;border-radius:2px;box-shadow:0 1px 5px rgba(0,0,0,.2),0 2px 2px rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.12);opacity:0;transform:scale3D(.85,.7,1);transition:opacity .25s cubic-bezier(.55,0,.55,.2),top .25s cubic-bezier(.55,0,.55,.2),transform 0s cubic-bezier(.55,0,.55,.2) .25s;color:rgba(33,33,33,.87)}.md-select .md-select-menu>*{opacity:0;transition:all .4s cubic-bezier(.25,.8,.25,1);transition-duration:.25s}.md-select .md-select-menu-container{margin:0;padding:8px 0;display:-ms-flexbox;display:flex;-ms-flex-flow:column;flex-flow:column;-ms-flex-pack:stretch;justify-content:stretch;-ms-flex-line-pack:stretch;align-content:stretch;overflow-x:hidden;overflow-y:auto}.md-select .md-subheader{color:hsla(0,0%,46%,.87);text-transform:uppercase}.md-select .md-subheader:first-child{margin-top:-8px}.md-option{height:48px;min-height:48px;padding:0 4px 0 16px;display:-ms-flexbox;display:flex;-ms-flex-flow:column;flex-flow:column;-ms-flex-pack:center;justify-content:center;overflow:hidden;cursor:pointer;position:relative;transform:translate3D(0,0,0);transition:all .4s cubic-bezier(.25,.8,.25,1);font-size:16px;line-height:1.2em;text-overflow:ellipsis;white-space:nowrap}.md-option.md-highlighted{background-color:rgba(0,0,0,.12)}.md-option span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.md-sidenav.md-left .md-sidenav-content{left:0;transform:translate3d(-100%,0,0)}.md-sidenav.md-right .md-sidenav-content{right:0;transform:translate3d(100%,0,0)}.md-sidenav.md-fixed .md-backdrop,.md-sidenav.md-fixed .md-sidenav-content{position:fixed}.md-sidenav .md-sidenav-content{width:304px;position:absolute;top:0;bottom:0;z-index:100;pointer-events:none;overflow:auto;transition:all .4s cubic-bezier(.25,.8,.25,1)}.md-sidenav .md-backdrop{position:absolute;top:0;right:0;bottom:0;left:0;z-index:99;pointer-events:none;background-color:rgba(0,0,0,.54);transform:translateZ(0);opacity:0;transition:all .5s cubic-bezier(.35,0,.25,1)}.md-sidenav.md-active .md-sidenav-content{pointer-events:auto;box-shadow:0 8px 10px -5px rgba(0,0,0,.2),0 16px 24px 2px rgba(0,0,0,.14),0 6px 30px 5px rgba(0,0,0,.12);transform:translateZ(0)}.md-sidenav.md-active .md-backdrop{opacity:1;pointer-events:auto}.md-subheader{min-height:48px;padding:0 16px;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-flow:row wrap;flex-flow:row wrap;color:rgba(0,0,0,.54);font-size:14px;font-weight:500}.md-switch{width:auto;margin:16px 8px 16px 0;display:-ms-inline-flexbox;display:inline-flex;position:relative}.md-switch .md-switch-container{width:34px;height:14px;position:relative;border-radius:14px;transition:all .4s cubic-bezier(.25,.8,.25,1);background-color:rgba(0,0,0,.38)}.md-switch .md-switch-container .md-switch-thumb{width:20px;height:20px;position:absolute;top:50%;left:0;background-color:#fafafa;border-radius:50%;box-shadow:0 1px 3px rgba(0,0,0,.2),0 1px 1px rgba(0,0,0,.14),0 2px 1px -1px rgba(0,0,0,.12);transition:all .08s linear}.md-switch .md-switch-container input{position:absolute;left:-999em}.md-switch .md-switch-container .md-ink-ripple{top:-16px;right:-16px;bottom:-16px;left:-16px;border-radius:50%;color:rgba(0,0,0,.54)}.md-switch .md-switch-container .md-ink-ripple .md-ripple{width:48px!important;height:48px!important;top:0!important;right:0!important;bottom:0!important;left:0!important}.md-switch .md-switch-container .md-switch-holder{width:40px;height:40px;margin:0;padding:0;position:absolute;top:50%;left:50%;z-index:2;background:none;border:none;transform:translate(-50%,-50%)}.md-switch .md-switch-container .md-switch-holder:focus{outline:none}.md-switch .md-switch-label{height:14px;padding-left:8px;line-height:14px}.md-switch.md-dragging .md-switch-thumb{cursor:-webkit-grabbing;cursor:grabbing}.md-switch.md-disabled .md-switch-thumb{cursor:default}.md-tabs{width:100%;display:-ms-flexbox;display:flex;-ms-flex-flow:column;flex-flow:column;position:relative}.md-tabs.md-has-icon.md-has-label .md-tabs-navigation{min-height:72px}.md-tabs.md-has-icon.md-has-label .md-tabs-navigation .md-icon{margin-bottom:10px}.md-tabs.md-centered .md-tabs-navigation{-ms-flex-pack:center;justify-content:center}.md-tabs.md-fixed .md-tab-header{-ms-flex:1;flex:1}.md-tabs .md-tabs-navigation{height:48px;min-height:48px;z-index:1;display:-ms-flexbox;display:flex}.md-tabs .md-tab-header,.md-tabs .md-tabs-navigation{position:relative;transition:all .4s cubic-bezier(.25,.8,.25,1)}.md-tabs .md-tab-header{min-width:72px;max-width:264px;margin:0;padding:0 12px;display:inline-block;cursor:pointer;border:0;background:none;font-family:inherit;font-size:14px;font-weight:500;text-transform:uppercase}.md-tabs .md-tab-header.md-disabled{cursor:default;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-user-drag:none}.md-tabs .md-tab-header-container{display:-ms-flexbox;display:flex;-ms-flex-flow:column;flex-flow:column;-ms-flex-pack:center;justify-content:center;-ms-flex-align:center;align-items:center}.md-tabs .md-tab-header-container .md-icon{margin:0}.md-tabs .md-tab-indicator{height:2px;position:absolute;bottom:0;left:0;transform:translate3D(0,0,0)}.md-tabs .md-tab-indicator.md-to-right{transition:all .4s cubic-bezier(.25,.8,.25,1),left .3s cubic-bezier(.35,0,.25,1),right .15s cubic-bezier(.35,0,.25,1)}.md-tabs .md-tab-indicator.md-to-left{transition:all .4s cubic-bezier(.25,.8,.25,1),right .3s cubic-bezier(.35,0,.25,1),left .15s cubic-bezier(.35,0,.25,1)}.md-tabs .md-transition-off{transition:none!important}.md-tabs .md-tabs-content{width:100%;height:0;position:relative;overflow:hidden;transition:height .4s cubic-bezier(.25,.8,.25,1)}.md-tabs .md-tabs-wrapper{width:9999em;position:absolute;top:0;right:0;bottom:0;left:0;transform:translateZ(0);transition:transform .4s cubic-bezier(.25,.8,.25,1)}.md-tabs .md-tab{padding:16px;position:absolute;top:0;left:0;right:0;pointer-events:none;transform:translate3d(0,-100%,0);transition:transform 0s .4s}.md-tabs .md-tab.md-active{transform:translateZ(0);pointer-events:auto;transition:none}.md-toolbar{min-height:64px;padding:0 8px;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-line-pack:center;align-content:center;-ms-flex-flow:row wrap;flex-flow:row wrap;position:relative;transition:all .4s cubic-bezier(.25,.8,.25,1);transform:translate3D(0,0,0)}.md-toolbar.md-dense{min-height:48px}.md-toolbar.md-dense.md-medium{min-height:72px}.md-toolbar.md-dense.md-large{min-height:96px}.md-toolbar.md-dense .md-toolbar-container{height:48px}.md-toolbar.md-medium{min-height:88px}.md-toolbar.md-medium .md-toolbar-container:nth-child(2) .md-title:first-child{margin-left:56px}.md-toolbar.md-large{min-height:128px;-ms-flex-line-pack:inherit;align-content:inherit}.md-toolbar.md-large .md-toolbar-container:nth-child(2) .md-title:first-child{margin-left:56px}.md-toolbar.md-account-header{min-height:164px}.md-toolbar.md-account-header .md-ink-ripple{color:#fff}.md-toolbar.md-account-header .md-list-item-container:hover:not([disabled]){background-color:hsla(0,0%,100%,.12)}.md-toolbar.md-account-header .md-avatar-list{margin:16px 0 8px}.md-toolbar.md-account-header .md-avatar-list .md-list-item-container{-ms-flex-align:start;align-items:flex-start}.md-toolbar.md-account-header .md-avatar-list .md-avatar+.md-avatar{margin-left:16px}.md-toolbar .md-toolbar-container{width:100%;height:64px;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-item-align:start;align-self:flex-start}.md-toolbar .md-toolbar-container>.md-button:first-child{margin-left:0;margin-right:16px}.md-toolbar .md-toolbar-container>.md-button+.md-button{margin-left:0}.md-toolbar>.md-button:first-child{margin-left:0;margin-right:16px}.md-toolbar>.md-button+.md-button{margin-left:0}.md-toolbar .md-button:hover:not([disabled]):not(.md-raised):not(.md-icon-button):not(.md-fab){background-color:hsla(0,0%,100%,.1)}.md-toolbar .md-title{margin:0;font-weight:400}.md-toolbar .md-title:first-child{margin-left:8px}.md-toolbar .md-list{padding:0;margin:0 -8px;-ms-flex:1;flex:1}.md-tooltip{height:20px;padding:0 8px;position:fixed;z-index:200;pointer-events:none;background-color:rgba(97,97,97,.87);border-radius:2px;opacity:0;transform-origin:center top;transition:all .4s cubic-bezier(.25,.8,.25,1);transition-duration:.3s;transition-delay:0s;color:#fff;font-family:Roboto,Lato,sans-serif;font-size:10px;line-height:20px;text-transform:none;white-space:nowrap}.md-tooltip.md-active{opacity:1;transition:all .3s cubic-bezier(.55,0,.55,.2);transition-duration:.3s}.md-tooltip:not(.md-active){transition-delay:0s!important}.md-tooltip.md-tooltip-top{margin-top:-14px;transform:translate(-50%,8px)}.md-tooltip.md-tooltip-top.md-active{transform:translate(-50%)}.md-tooltip.md-tooltip-right{margin-left:14px;transform:translate(-8px,50%)}.md-tooltip.md-tooltip-right.md-active{transform:translateY(50%)}.md-tooltip.md-tooltip-bottom{margin-top:14px;transform:translate(-50%,-8px)}.md-tooltip.md-tooltip-bottom.md-active{transform:translate(-50%)}.md-tooltip.md-tooltip-left{margin-left:-14px;transform:translate(8px,50%)}.md-tooltip.md-tooltip-left.md-active{transform:translateY(50%)}.md-whiteframe{position:relative;z-index:1}.md-whiteframe-1dp{box-shadow:0 1px 3px rgba(0,0,0,.2),0 1px 1px rgba(0,0,0,.14),0 2px 1px -1px rgba(0,0,0,.12)}.md-whiteframe-2dp{box-shadow:0 1px 5px rgba(0,0,0,.2),0 2px 2px rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.12)}.md-whiteframe-3dp{box-shadow:0 1px 8px rgba(0,0,0,.2),0 3px 4px rgba(0,0,0,.14),0 3px 3px -2px rgba(0,0,0,.12)}.md-whiteframe-4dp{box-shadow:0 2px 4px -1px rgba(0,0,0,.2),0 4px 5px rgba(0,0,0,.14),0 1px 10px rgba(0,0,0,.12)}.md-whiteframe-5dp{box-shadow:0 3px 5px -1px rgba(0,0,0,.2),0 5px 8px rgba(0,0,0,.14),0 1px 14px rgba(0,0,0,.12)}.md-whiteframe-6dp{box-shadow:0 3px 5px -1px rgba(0,0,0,.2),0 6px 10px rgba(0,0,0,.14),0 1px 18px rgba(0,0,0,.12)}.md-whiteframe-7dp{box-shadow:0 4px 5px -2px rgba(0,0,0,.2),0 7px 10px 1px rgba(0,0,0,.14),0 2px 16px 1px rgba(0,0,0,.12)}.md-whiteframe-8dp{box-shadow:0 5px 5px -3px rgba(0,0,0,.2),0 8px 10px 1px rgba(0,0,0,.14),0 3px 14px 2px rgba(0,0,0,.12)}.md-whiteframe-9dp{box-shadow:0 5px 6px -3px rgba(0,0,0,.2),0 9px 12px 1px rgba(0,0,0,.14),0 3px 16px 2px rgba(0,0,0,.12)}.md-whiteframe-10dp{box-shadow:0 6px 6px -3px rgba(0,0,0,.2),0 10px 14px 1px rgba(0,0,0,.14),0 4px 18px 3px rgba(0,0,0,.12)}.md-whiteframe-11dp{box-shadow:0 6px 7px -4px rgba(0,0,0,.2),0 11px 15px 1px rgba(0,0,0,.14),0 4px 20px 3px rgba(0,0,0,.12)}.md-whiteframe-12dp{box-shadow:0 7px 8px -4px rgba(0,0,0,.2),0 12px 17px 2px rgba(0,0,0,.14),0 5px 22px 4px rgba(0,0,0,.12)}.md-whiteframe-13dp{box-shadow:0 7px 8px -4px rgba(0,0,0,.2),0 13px 19px 2px rgba(0,0,0,.14),0 5px 24px 4px rgba(0,0,0,.12)}.md-whiteframe-14dp{box-shadow:0 7px 9px -4px rgba(0,0,0,.2),0 14px 21px 2px rgba(0,0,0,.14),0 5px 26px 4px rgba(0,0,0,.12)}.md-whiteframe-15dp{box-shadow:0 8px 9px -5px rgba(0,0,0,.2),0 15px 22px 2px rgba(0,0,0,.14),0 6px 28px 5px rgba(0,0,0,.12)}.md-whiteframe-16dp{box-shadow:0 8px 10px -5px rgba(0,0,0,.2),0 16px 24px 2px rgba(0,0,0,.14),0 6px 30px 5px rgba(0,0,0,.12)}.md-whiteframe-17dp{box-shadow:0 8px 11px -5px rgba(0,0,0,.2),0 17px 26px 2px rgba(0,0,0,.14),0 6px 32px 5px rgba(0,0,0,.12)}.md-whiteframe-18dp{box-shadow:0 9px 11px -5px rgba(0,0,0,.2),0 18px 28px 2px rgba(0,0,0,.14),0 7px 34px 6px rgba(0,0,0,.12)}.md-whiteframe-19dp{box-shadow:0 9px 12px -6px rgba(0,0,0,.2),0 19px 29px 2px rgba(0,0,0,.14),0 7px 36px 6px rgba(0,0,0,.12)}.md-whiteframe-20dp{box-shadow:0 10px 13px -6px rgba(0,0,0,.2),0 20px 31px 3px rgba(0,0,0,.14),0 8px 38px 7px rgba(0,0,0,.12)}.md-whiteframe-21dp{box-shadow:0 10px 13px -6px rgba(0,0,0,.2),0 21px 33px 3px rgba(0,0,0,.14),0 8px 40px 7px rgba(0,0,0,.12)}.md-whiteframe-22dp{box-shadow:0 10px 14px -6px rgba(0,0,0,.2),0 22px 35px 3px rgba(0,0,0,.14),0 8px 42px 7px rgba(0,0,0,.12)}.md-whiteframe-23dp{box-shadow:0 11px 14px -7px rgba(0,0,0,.2),0 23px 36px 3px rgba(0,0,0,.14),0 9px 44px 8px rgba(0,0,0,.12)}.md-whiteframe-24dp{box-shadow:0 11px 15px -7px rgba(0,0,0,.2),0 24px 38px 3px rgba(0,0,0,.14),0 9px 46px 8px rgba(0,0,0,.12)}@font-face{font-family:Material Icons;font-style:normal;font-weight:400;src:url(fonts/MaterialIcons-Regular.e79bfd8.eot);src:local('Material Icons'),local('MaterialIcons-Regular'),url(fonts/MaterialIcons-Regular.570eb83.woff2) format('woff2'),url(fonts/MaterialIcons-Regular.012cf6a.woff) format('woff'),url(fonts/MaterialIcons-Regular.a37b0c0.ttf) format('truetype')}.material-icons{font-family:Material Icons;font-weight:400;font-style:normal;font-size:24px;display:inline-block;line-height:1;text-transform:none;letter-spacing:normal;word-wrap:normal;white-space:nowrap;direction:ltr;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;-moz-osx-font-smoothing:grayscale;font-feature-settings:'liga'} -------------------------------------------------------------------------------- /app/dist/viewer.html: -------------------------------------------------------------------------------- 1 | Viewer
-------------------------------------------------------------------------------- /app/electron.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const electron = require('electron') 3 | const { app, BrowserWindow, ipcMain, dialog, Menu} = require('electron') 4 | const path = require('path') 5 | const UserStore = require('./UserStore.js') 6 | const template = [ 7 | { 8 | label: 'Window', 9 | submenu: [ 10 | { 11 | label: 'Minimize', 12 | accelerator: 'CmdOrCtrl+M', 13 | role: 'minimize' 14 | }, 15 | { 16 | label: 'Close', 17 | accelerator: 'CmdOrCtrl+W', 18 | role: 'close' 19 | } 20 | ] 21 | }, 22 | { 23 | label: 'Edit', 24 | submenu: [ 25 | { 26 | label: 'Undo', 27 | accelerator: 'CmdOrCtrl+Z', 28 | role: 'undo' 29 | }, 30 | { 31 | label: 'Redo', 32 | accelerator: 'Shift+CmdOrCtrl+Z', 33 | role: 'redo' 34 | }, 35 | { 36 | type: 'separator' 37 | }, 38 | { 39 | label: 'Cut', 40 | accelerator: 'CmdOrCtrl+X', 41 | role: 'cut' 42 | }, 43 | { 44 | label: 'Copy', 45 | accelerator: 'CmdOrCtrl+C', 46 | role: 'copy' 47 | }, 48 | { 49 | label: 'Paste', 50 | accelerator: 'CmdOrCtrl+V', 51 | role: 'paste' 52 | }, 53 | { 54 | label: 'Select All', 55 | accelerator: 'CmdOrCtrl+A', 56 | role: 'selectall' 57 | } 58 | ] 59 | }, 60 | { 61 | label: 'Developer', 62 | submenu: [ 63 | { 64 | label: 'Toggle Developer Tools', 65 | accelerator: process.platform === 'darwin' 66 | ? 'Alt+Command+I' 67 | : 'Ctrl+Shift+I', 68 | click () { mainWindow.webContents.toggleDevTools() } 69 | } 70 | ] 71 | } 72 | ] 73 | 74 | if (process.platform === 'darwin') { 75 | const name = app.getName() 76 | template.unshift({ 77 | label: name, 78 | submenu: [ 79 | { 80 | label: 'About ' + name, 81 | role: 'about' 82 | }, 83 | { 84 | type: 'separator' 85 | }, 86 | { 87 | label: 'Services', 88 | role: 'services', 89 | submenu: [] 90 | }, 91 | { 92 | type: 'separator' 93 | }, 94 | { 95 | label: 'Hide ' + name, 96 | accelerator: 'Command+H', 97 | role: 'hide' 98 | }, 99 | { 100 | label: 'Hide Others', 101 | accelerator: 'Command+Alt+H', 102 | role: 'hideothers' 103 | }, 104 | { 105 | label: 'Show All', 106 | role: 'unhide' 107 | }, 108 | { 109 | type: 'separator' 110 | }, 111 | { 112 | label: 'Quit', 113 | accelerator: 'Command+Q', 114 | click () { app.quit() } 115 | } 116 | ] 117 | }) 118 | } 119 | 120 | let mainWindow 121 | let viewerWindow = null 122 | let config = {} 123 | let authUrl = "http://www.google.com"; 124 | let viewerPath 125 | 126 | if (process.env.NODE_ENV === 'development') { 127 | config = require('../config') 128 | config.url = `http://localhost:${config.port}` 129 | viewerPath = path.join('file://', __dirname, './viewer.html') 130 | } else { 131 | config.devtron = false 132 | config.url = `file://${__dirname}/dist/index.html` 133 | viewerPath = path.join('file://', __dirname, '/dist/viewer.html') 134 | } 135 | 136 | function createWindow() { 137 | /** 138 | * Initial window options 139 | */ 140 | 141 | mainWindow = new BrowserWindow({ 142 | webPreferences: { 143 | webSecurity: false 144 | }, 145 | height: 850, 146 | width: 600, 147 | titleBarStyle: 'hidden-inset', 148 | backgroundColor: '#ccc' 149 | }) 150 | 151 | mainWindow.loadURL(config.url) 152 | 153 | if (process.env.NODE_ENV === 'development') { 154 | BrowserWindow.addDevToolsExtension(path.join(__dirname, '../node_modules/devtron')) 155 | 156 | let installExtension = require('electron-devtools-installer') 157 | 158 | installExtension.default(installExtension.VUEJS_DEVTOOLS) 159 | .then((name) => mainWindow.webContents.openDevTools()) 160 | .catch((err) => console.log('An error occurred: ', err)) 161 | } 162 | 163 | mainWindow.on('closed', (e) => { 164 | mainWindow = null 165 | }) 166 | 167 | mainWindow.on('resize', () => {}); 168 | 169 | const windowID = mainWindow.id 170 | } 171 | 172 | app.on('ready', () => { 173 | const menu = Menu.buildFromTemplate(template) 174 | Menu.setApplicationMenu(menu) 175 | createWindow() 176 | }) 177 | 178 | app.on('window-all-closed', () => { 179 | if (process.platform !== 'darwin') { 180 | app.quit() 181 | } 182 | }) 183 | 184 | app.on('activate', () => { 185 | if (mainWindow === null) { 186 | createWindow() 187 | } 188 | }) 189 | 190 | app.on('before-quit', () => { 191 | }) 192 | 193 | ipcMain.on('openArticleWindow', (event, url) => { 194 | var electronScreen = electron.screen; 195 | var size = electronScreen.getPrimaryDisplay().workAreaSize; 196 | let windowID = mainWindow.id 197 | 198 | if (viewerWindow !== null) { 199 | if (viewerWindow.isMinimized()) { 200 | viewerWindow.webContents.send('openArticleTrigged', url, windowID) 201 | viewerWindow.restore() 202 | } else { 203 | // viewerWindow.loadURL(viewerPath) 204 | viewerWindow.webContents.send('openArticleTrigged', url, windowID) 205 | viewerWindow.show() 206 | } 207 | 208 | } else { 209 | var winOptions = { 210 | width: 600, 211 | height: size.height, 212 | show: false, 213 | x: 1440 - 600, 214 | y: 0, 215 | titleBarStyle: 'hidden-inset', 216 | // nodeIntegration: false, 217 | webPreferences: { 218 | // nodeIntegration: false, 219 | webSecurity: false 220 | // allowDisplayingInsecureContent: true, 221 | // allowRunningInsecureContent: true, 222 | // plugins: true 223 | }, 224 | backgroundColor: '#ccc' 225 | } 226 | viewerWindow = new BrowserWindow(winOptions); 227 | let webContents = viewerWindow.webContents 228 | 229 | viewerWindow.loadURL(viewerPath); 230 | viewerWindow.show(); 231 | 232 | viewerWindow.webContents.on('did-finish-load', function() { 233 | viewerWindow.webContents.send('openArticleTrigged', url, windowID) 234 | }); 235 | 236 | } 237 | 238 | viewerWindow.on('closed', function(e) { 239 | viewerWindow = null 240 | }); 241 | }); 242 | -------------------------------------------------------------------------------- /app/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidroyer/pocketvue/a63fb5ea5a1383e7509546e596a9d4edb076758f/app/icons/icon.icns -------------------------------------------------------------------------------- /app/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidroyer/pocketvue/a63fb5ea5a1383e7509546e596a9d4edb076758f/app/icons/icon.ico -------------------------------------------------------------------------------- /app/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%= htmlWebpackPlugin.options.title %> 6 | 7 | 8 |
9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pocketvue", 3 | "version": "0.0.0", 4 | "description": "An electron-vue project", 5 | "main": "electron.js", 6 | "dependencies": { 7 | "article-extractor": "^1.0.2", 8 | "axios": "^0.15.2", 9 | "electron-config": "^0.2.1", 10 | "electron-data": "^1.2.0", 11 | "electron-settings": "^2.2.2", 12 | "lodash": "^4.16.4", 13 | "material-design-icons": "^3.0.1", 14 | "request": "^2.76.0", 15 | "titlebar": "^1.4.0", 16 | "vue": "^2.0.1", 17 | "vue-axios": "^1.1.2", 18 | "vue-electron": "^1.0.0", 19 | "vue-material": "^0.2.0", 20 | "vue-resource": "^1.0.3", 21 | "wysiwyg.css": "0.0.2" 22 | }, 23 | "devDependencies": { 24 | "electron-settings": "^2.2.2", 25 | "vue-multiselect": "^2.0.0-beta.10", 26 | "vue-resource": "^1.0.3" 27 | }, 28 | "author": "David Royer " 29 | } 30 | -------------------------------------------------------------------------------- /app/src/App.vue: -------------------------------------------------------------------------------- 1 | 80 | 81 | 103 | 104 | 149 | -------------------------------------------------------------------------------- /app/src/SharedStore.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import axios from 'axios' 3 | const pocketConfig = require('./config/AppSettings') 4 | 5 | const UserStore = require('../UserStore.js') 6 | const key = pocketConfig.key 7 | console.log(key); 8 | const redirectURL = pocketConfig.redirectURL 9 | import _ from 'lodash' 10 | 11 | const userStore = new UserStore({ 12 | configName: 'user-settings', 13 | defaults: { 14 | requestToken: null, 15 | accessToken: null, 16 | loggedIn: false 17 | } 18 | }) 19 | 20 | var PocketAPI = axios.create({ 21 | baseURL: 'https://getpocket.com/v3', 22 | headers: { 23 | "content-type": "application/json", 24 | "X-Accept": "application/json" 25 | } 26 | }); 27 | 28 | export const store = { 29 | state: { 30 | pocketList: null, 31 | users: null, 32 | authCode: null, 33 | hasRequestToken: null, 34 | hasAccessToken: true, 35 | isAuthorized: false, 36 | loggedIn: false, 37 | view: 'Login', 38 | showWebView: false, 39 | webViewUrl: null, 40 | fullList: null, 41 | tagFetchResult: null 42 | }, 43 | 44 | switchToMainView: function () { 45 | this.state.isAuthorized = true 46 | this.state.loggedIn = true 47 | this.state.view = 'LandingPage' 48 | }, 49 | 50 | checkForAccessToken: function () { 51 | if (userStore.get('accessToken') !== null) { 52 | store.switchToMainView() 53 | } 54 | }, 55 | 56 | addTags: function () { 57 | let actions = [ { action: 'tags_add', tags: 'addedfromPocket', item_id: '1435741427' } ] 58 | actions = JSON.stringify(actions) 59 | 60 | PocketAPI.post('/modify', { actions, consumer_key: key, access_token: accessToken }).then((response) => { 61 | console.log(response) 62 | 63 | }).catch((response) => { 64 | alert(error); 65 | }); 66 | }, 67 | 68 | addArticle: function (url) { 69 | 70 | PocketAPI.post('/add', { url: url, consumer_key: key, access_token: userStore.get('accessToken') }) 71 | .then((response) => { 72 | alert('Your article has been saved!') 73 | console.log(response) 74 | 75 | }).catch((response) => { 76 | alert(error); 77 | }); 78 | }, 79 | 80 | runNodeAuth: function () { 81 | PocketAPI.post('/oauth/request', { consumer_key: key, redirect_uri: redirectURL } 82 | 83 | ).then(function (response) { 84 | store.userAuthorizationApproval(response.data.code) 85 | 86 | }).catch(function (error) { 87 | alert(error); 88 | }) 89 | }, 90 | 91 | userAuthorizationApproval: function (code) { 92 | userStore.set('requestToken', code); 93 | this.state.webViewUrl = 'https://getpocket.com/auth/authorize?request_token='+code+'&redirect_uri='+redirectURL 94 | this.state.showWebView = true 95 | }, 96 | 97 | runGetAccessToken: function () { 98 | let state = this.state 99 | if (userStore.get('accessToken') !== null) { 100 | store.switchToMainView() 101 | 102 | } else { 103 | var requestToken = userStore.get('requestToken') 104 | 105 | PocketAPI.post('/oauth/authorize', { consumer_key: key, code: requestToken 106 | 107 | }).then(function (response) { 108 | userStore.set('accessToken', response.data.access_token) 109 | store.switchToMainView() 110 | 111 | }).catch(function (error) { 112 | alert(error); 113 | }) 114 | } 115 | 116 | }, 117 | 118 | fetchPostsFromUserToken: function() { 119 | var accessToken = userStore.get('accessToken') 120 | let currentState = this.state 121 | 122 | PocketAPI.post('/get', { consumer_key: key, access_token: accessToken, count: "300", detailType: "complete" }).then((response) => { 123 | currentState.pocketList = response.data.list 124 | 125 | }).catch((response) => { 126 | alert(error); 127 | }); 128 | 129 | }, 130 | 131 | fetchFullList: function () { 132 | var accessToken = userStore.get('accessToken') 133 | let currentState = this.state 134 | 135 | PocketAPI.post('/get', { consumer_key: key, access_token: accessToken, detailType: "complete" }) 136 | 137 | .then((response) => { 138 | currentState.fullList = response.data.list 139 | }) 140 | 141 | .catch((response) => { 142 | alert(error); 143 | }); 144 | }, 145 | 146 | fetchForTag: function (tag) { 147 | var accessToken = userStore.get('accessToken') 148 | let currentState = this.state 149 | 150 | PocketAPI.post('/get', { consumer_key: key, access_token: accessToken, tag: tag, detailType: "complete" }) 151 | 152 | .then((response) => { 153 | currentState.tagFetchResult = response.data.list 154 | }) 155 | 156 | .catch((response) => { 157 | alert(error); 158 | }); 159 | }, 160 | 161 | resetConfig: function () { 162 | userStore.nullify('accessToken') 163 | userStore.nullify('requestToken') 164 | this.state.isAuthorized = false 165 | this.state.loggedIn = false 166 | }, 167 | 168 | hasRequestToken: function () { 169 | this.state.hasRequestToken = true 170 | this.state.isAuthorized = true 171 | 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /app/src/assets/pocket-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidroyer/pocketvue/a63fb5ea5a1383e7509546e596a9d4edb076758f/app/src/assets/pocket-icon.png -------------------------------------------------------------------------------- /app/src/bus.js: -------------------------------------------------------------------------------- 1 | var Vue = require('vue'); 2 | 3 | let bus = new Vue(); 4 | 5 | module.exports = bus; 6 | -------------------------------------------------------------------------------- /app/src/components/LandingPageView.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 157 | 158 | 304 | -------------------------------------------------------------------------------- /app/src/components/LandingPageView/PocketItem.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 45 | 46 | 198 | -------------------------------------------------------------------------------- /app/src/components/LandingPageView/Tag.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 56 | 57 | 78 | -------------------------------------------------------------------------------- /app/src/components/Login.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 86 | 87 | 159 | -------------------------------------------------------------------------------- /app/src/config/AppSettings.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | key: '58661-bde889e092272515b109406c', 3 | redirectURL: 'https://google.com' 4 | } 5 | -------------------------------------------------------------------------------- /app/src/config/pocketApiConfig.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | pocketUrl: { 3 | request : "https://getpocket.com/v3/oauth/request", 4 | authorize : "https://getpocket.com/v3/oauth/authorize", 5 | get: "https://getpocket.com/v3/get", 6 | add: "https://getpocket.com/v3/add", 7 | modify: "https://getpocket.com/v3/send" 8 | }, 9 | headers: { 10 | "content-type": "application/x-www-form-urlencoded", 11 | "X-Accept": "application/json" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Electron from 'vue-electron' 3 | import Resource from 'vue-resource' 4 | import _ from 'lodash' 5 | import VueMaterial from 'vue-material' 6 | import 'vue-material/dist/vue-material.css' 7 | require('../node_modules/material-design-icons/iconfont/material-icons.css') 8 | 9 | Vue.use(VueMaterial) 10 | Vue.material.theme.register('default', { 11 | primary: 'indigo', 12 | accent: 'pink' 13 | }) 14 | 15 | Vue.use(Electron) 16 | Vue.use(Resource) 17 | Vue.config.debug = true 18 | 19 | import App from './App' 20 | import {store} from './SharedStore' 21 | /* eslint-disable no-new */ 22 | new Vue({ 23 | data () { 24 | return { 25 | sharedState: store.state 26 | } 27 | }, 28 | 29 | created () { 30 | }, 31 | 32 | methods: { 33 | runTokenCheck: function () { 34 | store.checkForAccessToken() 35 | } 36 | }, 37 | 38 | mounted: function () { 39 | this.runTokenCheck() 40 | }, 41 | 42 | ...App 43 | }).$mount('#app') 44 | -------------------------------------------------------------------------------- /app/src/viewer.js: -------------------------------------------------------------------------------- 1 | alert('hello'); 2 | -------------------------------------------------------------------------------- /app/viewer-backup.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Viewer 6 | 34 | 35 | 36 | 37 |
38 | 41 |
42 | 43 | 44 |
45 |
46 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /app/viewer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Viewer 6 | 61 | 62 | 63 | 64 |
65 | 68 |
69 | 70 |
71 |
72 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /builds/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidroyer/pocketvue/a63fb5ea5a1383e7509546e596a9d4edb076758f/builds/.gitkeep -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('path') 4 | 5 | let config = { 6 | // Name of electron app 7 | // Will be used in production builds 8 | name: 'PocketVue', 9 | 10 | // webpack-dev-server port 11 | port: 9080, 12 | 13 | // electron-packager options 14 | // Docs: https://simulatedgreg.gitbooks.io/electron-vue/content/docs/building_your_app.html 15 | building: { 16 | arch: 'x64', 17 | asar: true, 18 | dir: path.join(__dirname, 'app'), 19 | icon: path.join(__dirname, 'app/icons/icon'), 20 | ignore: /node_modules|src|index.ejs|icons/, 21 | out: path.join(__dirname, 'builds'), 22 | overwrite: true, 23 | platform: process.env.PLATFORM_TARGET || 'all', 24 | protocols: [ 25 | { 26 | name: 'pocketvue', 27 | schemes: ["pocketvue", "pocketapp"] 28 | } 29 | ] 30 | } 31 | } 32 | 33 | config.building.name = config.name 34 | 35 | module.exports = config 36 | -------------------------------------------------------------------------------- /icons-backup/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidroyer/pocketvue/a63fb5ea5a1383e7509546e596a9d4edb076758f/icons-backup/icon.icns -------------------------------------------------------------------------------- /icons-backup/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davidroyer/pocketvue/a63fb5ea5a1383e7509546e596a9d4edb076758f/icons-backup/icon.ico -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pocketvue", 3 | "version": "1.0.3-alpha", 4 | "description": "An electron-vue project", 5 | "scripts": { 6 | "build": "node tasks/release.js", 7 | "build:clean": "cross-env PLATFORM_TARGET=clean node tasks/release.js", 8 | "build:darwin": "cross-env PLATFORM_TARGET=darwin node tasks/release.js", 9 | "build:linux": "cross-env PLATFORM_TARGET=linux node tasks/release.js", 10 | "build:mas": "cross-env PLATFORM_TARGET=mas node tasks/release.js", 11 | "build:win32": "cross-env PLATFORM_TARGET=win32 node tasks/release.js", 12 | "dev": "node tasks/runner.js", 13 | "pack": "cross-env NODE_ENV=production webpack -p --progress --colors", 14 | "postinstall": "cd app && npm install" 15 | }, 16 | "author": "Greg Holguin ", 17 | "license": "MIT", 18 | "devDependencies": { 19 | "babel-core": "^6.8.0", 20 | "babel-loader": "^6.2.4", 21 | "babel-plugin-transform-runtime": "^6.8.0", 22 | "babel-preset-es2015": "^6.6.0", 23 | "babel-preset-stage-0": "^6.5.0", 24 | "babel-runtime": "^6.6.1", 25 | "cross-env": "^1.0.7", 26 | "css-loader": "^0.23.1", 27 | "del": "^2.2.1", 28 | "devtron": "^1.1.0", 29 | "electron": "^1.3.1", 30 | "electron-devtools-installer": "^1.1.4", 31 | "electron-packager": "^8.0.0", 32 | "electron-rebuild": "^1.1.3", 33 | "extract-text-webpack-plugin": "^1.0.1", 34 | "file-loader": "^0.8.5", 35 | "html-webpack-plugin": "^2.16.1", 36 | "json-loader": "^0.5.4", 37 | "node-sass": "^3.10.1", 38 | "sass-loader": "^4.0.2", 39 | "style-loader": "^0.13.1", 40 | "tree-kill": "^1.1.0", 41 | "url-loader": "^0.5.7", 42 | "vue-hot-reload-api": "^1.3.2", 43 | "vue-html-loader": "^1.2.2", 44 | "vue-loader": "^9.5.1", 45 | "vue-style-loader": "^1.0.0", 46 | "webpack": "^1.13.0", 47 | "webpack-dev-server": "^1.14.1" 48 | }, 49 | "dependencies": { 50 | "axios": "^0.15.2", 51 | "vue-axios": "^1.1.2" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tasks/release.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const exec = require('child_process').exec 4 | const packager = require('electron-packager') 5 | 6 | if (process.env.PLATFORM_TARGET === 'clean') { 7 | require('del').sync(['builds/*', '!.gitkeep']) 8 | console.log('\x1b[33m`builds` directory cleaned.\n\x1b[0m') 9 | } else pack() 10 | 11 | /** 12 | * Build webpack in production 13 | */ 14 | function pack () { 15 | console.log('\x1b[33mBuilding webpack in production mode...\n\x1b[0m') 16 | let pack = exec('npm run pack') 17 | 18 | pack.stdout.on('data', data => console.log(data)) 19 | pack.stderr.on('data', data => console.error(data)) 20 | pack.on('exit', code => build()) 21 | } 22 | 23 | 24 | /** 25 | * Use electron-packager to build electron app 26 | */ 27 | function build () { 28 | let options = require('../config').building 29 | 30 | console.log('\x1b[34mBuilding electron app(s)...\n\x1b[0m') 31 | packager(options, (err, appPaths) => { 32 | if(err) { 33 | console.error('\x1b[31mError from `electron-packager` when building app...\x1b[0m') 34 | console.error(err) 35 | } else { 36 | console.log('Build(s) successful!') 37 | console.log(appPaths) 38 | 39 | console.log('\n\x1b[34mDONE\n\x1b[0m') 40 | } 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /tasks/runner.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Credits to https://github.com/bradstewart/electron-boilerplate-vue/blob/master/build/dev-runner.js 3 | */ 4 | 'use strict' 5 | 6 | const config = require('../config') 7 | const exec = require('child_process').exec 8 | const treeKill = require('tree-kill') 9 | 10 | let YELLOW = '\x1b[33m' 11 | let BLUE = '\x1b[34m' 12 | let END = '\x1b[0m' 13 | 14 | let isElectronOpen = false 15 | 16 | function format (command, data, color) { 17 | return color + command + END + 18 | ' ' + // Two space offset 19 | data.toString().trim().replace(/\n/g, '\n' + repeat(' ', command.length + 2)) + 20 | '\n' 21 | } 22 | 23 | function repeat (str, times) { 24 | return (new Array(times + 1)).join(str) 25 | } 26 | 27 | let children = [] 28 | 29 | function run (command, color, name) { 30 | let child = exec(command) 31 | 32 | child.stdout.on('data', data => { 33 | console.log(format(name, data, color)) 34 | 35 | /** 36 | * Start electron after VALID build 37 | * (prevents electron from opening a blank window that requires refreshing) 38 | * 39 | * NOTE: needs more testing for stability 40 | */ 41 | if (/VALID/g.test(data.toString().trim().replace(/\n/g, '\n' + repeat(' ', command.length + 2))) && !isElectronOpen) { 42 | console.log(`${BLUE}Starting electron...\n${END}`) 43 | run('cross-env NODE_ENV=development electron app/electron.js', BLUE, 'electron') 44 | isElectronOpen = true 45 | } 46 | }) 47 | 48 | child.stderr.on('data', data => console.error(format(name, data, color))) 49 | child.on('exit', code => exit(code)) 50 | 51 | children.push(child) 52 | } 53 | 54 | function exit (code) { 55 | children.forEach(child => { 56 | treeKill(child.pid) 57 | }) 58 | } 59 | 60 | console.log(`${YELLOW}Starting webpack-dev-server...\n${END}`) 61 | run(`webpack-dev-server --inline --hot --colors --port ${config.port} --content-base app/dist`, YELLOW, 'webpack') 62 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const path = require('path') 4 | const pkg = require('./app/package.json') 5 | const settings = require('./config.js') 6 | const webpack = require('webpack') 7 | 8 | const ExtractTextPlugin = require('extract-text-webpack-plugin') 9 | const HtmlWebpackPlugin = require('html-webpack-plugin') 10 | 11 | let config = { 12 | devtool: '#eval-source-map', 13 | entry: { 14 | build: path.join(__dirname, 'app/src/main.js') 15 | }, 16 | module: { 17 | loaders: [ 18 | { 19 | test: /\.css$/, 20 | loader: ExtractTextPlugin.extract('style-loader', 'css-loader') 21 | }, 22 | { 23 | test: /\.html$/, 24 | loader: 'vue-html-loader' 25 | }, 26 | { 27 | test: /\.js$/, 28 | loader: 'babel-loader', 29 | exclude: /node_modules/ 30 | }, 31 | { 32 | test: /\.json$/, 33 | loader: 'json-loader' 34 | }, 35 | { 36 | test: /\.vue$/, 37 | loader: 'vue-loader' 38 | }, 39 | { 40 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 41 | loader: 'url-loader', 42 | query: { 43 | limit: 10000, 44 | name: 'imgs/[name].[hash:7].[ext]' 45 | } 46 | }, 47 | { 48 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 49 | loader: 'url-loader', 50 | query: { 51 | limit: 10000, 52 | name: 'fonts/[name].[hash:7].[ext]' 53 | } 54 | } 55 | ] 56 | }, 57 | plugins: [ 58 | new ExtractTextPlugin('styles.css'), 59 | new HtmlWebpackPlugin({ 60 | filename: 'index.html', 61 | template: './app/index.ejs', 62 | title: settings.name 63 | }), 64 | new HtmlWebpackPlugin({ 65 | filename: 'viewer.html', 66 | template: './app/viewer.html', 67 | title: settings.name 68 | }), 69 | new webpack.NoErrorsPlugin() 70 | ], 71 | output: { 72 | filename: '[name].js', 73 | path: path.join(__dirname, 'app/dist') 74 | }, 75 | resolve: { 76 | alias: { 77 | 'components': path.join(__dirname, 'app/src/components'), 78 | 'src': path.join(__dirname, 'app/src') 79 | }, 80 | extensions: ['', '.js', '.vue', '.json', '.css'], 81 | fallback: [path.join(__dirname, 'app/node_modules')] 82 | }, 83 | resolveLoader: { 84 | root: path.join(__dirname, 'node_modules') 85 | }, 86 | target: 'electron-renderer', 87 | vue: { 88 | loaders: { 89 | sass: 'vue-style-loader!css-loader!sass-loader?indentedSyntax=1', 90 | scss: 'vue-style-loader!css-loader!sass-loader' 91 | } 92 | } 93 | } 94 | 95 | /** 96 | * Adjust config for production settings 97 | */ 98 | if (process.env.NODE_ENV === 'production') { 99 | config.devtool = '' 100 | 101 | config.plugins.push( 102 | new webpack.DefinePlugin({ 103 | 'process.env.NODE_ENV': '"production"' 104 | }), 105 | new webpack.optimize.OccurenceOrderPlugin(), 106 | new webpack.optimize.UglifyJsPlugin({ 107 | compress: { 108 | warnings: false 109 | } 110 | }) 111 | ) 112 | } 113 | 114 | module.exports = config 115 | --------------------------------------------------------------------------------