├── .appveyor.yml
├── .babelrc
├── .deblist
├── .editorconfig
├── .eslintignore
├── .eslintrc
├── .github
├── CONTRIBUTING.md
├── ISSUE_TEMPLATE.md
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .rpmlist
├── .travis.yml
├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── RELEASENOTES.json
├── app
├── fonts
│ ├── MaterialIcons-Regular.eot
│ ├── MaterialIcons-Regular.svg
│ ├── MaterialIcons-Regular.ttf
│ ├── MaterialIcons-Regular.woff
│ ├── MaterialIcons-Regular.woff2
│ ├── Roboto-Light.ttf
│ ├── Roboto-Light.woff
│ ├── Roboto-Light.woff2
│ ├── photon-entypo.eot
│ ├── photon-entypo.svg
│ ├── photon-entypo.ttf
│ └── photon-entypo.woff
├── html
│ ├── controller.html
│ ├── overlay.html
│ └── preferences.html
├── images
│ ├── darwin-tray-icon-default-Template.png
│ ├── darwin-tray-icon-default-Template@2x.png
│ ├── darwin-tray-icon-translucent-Template.png
│ ├── darwin-tray-icon-translucent-Template@2x.png
│ ├── linux-tray-icon-default.png
│ ├── linux-tray-icon-translucent.png
│ ├── win32-tray-icon-default.png
│ └── win32-tray-icon-translucent.png
├── scripts
│ ├── main
│ │ ├── components
│ │ │ ├── application.js
│ │ │ ├── globals.js
│ │ │ └── menubar.js
│ │ ├── managers
│ │ │ ├── configuration-manager.js
│ │ │ └── overlay-manager.js
│ │ ├── menus
│ │ │ └── tray-menu.js
│ │ ├── providers
│ │ │ └── dialog-provider.js
│ │ ├── services
│ │ │ ├── debug-service.js
│ │ │ ├── power-service.js
│ │ │ └── updater-service.js
│ │ └── windows
│ │ │ ├── overlay-configuration.js
│ │ │ ├── overlay-window.js
│ │ │ └── preferences-window.js
│ └── renderer
│ │ ├── controller.js
│ │ ├── overlay.js
│ │ └── preferences.js
├── styles
│ └── styles.css
└── vendor
│ ├── material-icons.css
│ ├── photon.css
│ └── roboto.css
├── bin
└── cli.js
├── gulpfile.babel.js
├── icons
├── darwin
│ ├── background-setup.png
│ ├── icon-setup.icns
│ └── icon.icns
├── linux
│ ├── 1024x1024.png
│ ├── 128x128.png
│ ├── 16x16.png
│ ├── 24x24.png
│ ├── 256x256.png
│ ├── 32x32.png
│ ├── 48x48.png
│ ├── 512x512.png
│ ├── 64x64.png
│ ├── 96x96.png
│ ├── icon-setup.png
│ └── icon.png
└── win32
│ ├── background-setup.bmp
│ ├── header-setup.bmp
│ ├── icon-setup.ico
│ └── icon.ico
├── package-lock.json
├── package.json
├── resources
├── graphics
│ ├── icon.ai
│ └── icon.png
└── screenshots
│ ├── screenshot-macos.png
│ └── screenshot-win32.png
└── yarn.lock
/.appveyor.yml:
--------------------------------------------------------------------------------
1 | os: Visual Studio 2015
2 |
3 | platform:
4 | - x64
5 |
6 | branches:
7 | only:
8 | - master
9 |
10 | version: '{build}-{branch}'
11 |
12 | cache:
13 | - "node_modules"
14 | - "%LOCALAPPDATA%\\Yarn"
15 |
16 | init:
17 | - cmd: echo 🚦 Authorizing Build
18 | - ps: if ($env:OS -eq "Windows_NT" -And $env:DEPLOY_WINDOWS -eq "0") { $host.SetShouldExit(0) }
19 | - cmd: git config --global core.autocrlf input
20 |
21 | install:
22 | - cmd: echo 🔧 Setting up Node
23 | - ps: Install-Product node 9
24 | - cmd: npm --global update npm
25 | - cmd: npm --global install yarn
26 |
27 | before_build:
28 | - cmd: echo 📥 Installing Dependencies
29 | - cmd: yarn install
30 |
31 | build_script:
32 | - cmd: echo 📦 Building
33 | - cmd: npm run build --metadata
34 |
35 | deploy_script:
36 | - cmd: echo 📮 Deploying to GitHub
37 | - cmd: npm run deploy
38 |
39 | artifacts:
40 | - path: build\output\*.exe
41 |
42 | notifications:
43 | - provider: Webhook
44 | url: https://webhooks.gitter.im/e/a1a9de50af1c703bfe6c
45 | method: GET
46 | on_build_success: true
47 | on_build_failure: true
48 | on_build_status_changed: true
49 |
50 | test: off
51 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["electron"]
3 | }
4 |
--------------------------------------------------------------------------------
/.deblist:
--------------------------------------------------------------------------------
1 | linuxmint/sarah
2 | linuxmint/sonya
3 | linuxmint/serena
4 | elementaryos/freya
5 | debian/jessie
6 | debian/stretch
7 | debian/buster
8 | ubuntu/xenial
9 | ubuntu/yakkety
10 | ubuntu/zesty
11 | ubuntu/artful
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # http://editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 4
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [.eslintrc]
13 | indent_size = 2
14 |
15 | [*.json]
16 | indent_size = 2
17 |
18 | [*.md]
19 | trim_trailing_whitespace = false
20 |
21 | [*.yml]
22 | indent_size = 2
23 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | resources/**
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es6": true,
5 | "node": true
6 | },
7 | "globals": {
8 | "global": false,
9 | "getEventListeners": false
10 | },
11 | "rules": {
12 | "no-bitwise": "off",
13 | "camelcase": "off",
14 | "curly": "error",
15 | "eqeqeq": "error",
16 | "wrap-iife": "off",
17 | "indent": [
18 | "error",
19 | 4,
20 | {
21 | "SwitchCase": 1
22 | }
23 | ],
24 | "no-use-before-define": [
25 | "error",
26 | {
27 | "functions": false
28 | }
29 | ],
30 | "no-caller": "error",
31 | "quotes": [
32 | "error",
33 | "single",
34 | {
35 | "allowTemplateLiterals": true
36 | }
37 | ],
38 | "no-undef": "error",
39 | "no-unused-vars": "error",
40 | "strict": [
41 | "error",
42 | "global"
43 | ],
44 | "dot-notation": "off",
45 | "no-with": "error",
46 | "no-mixed-spaces-and-tabs": "error",
47 | "no-multi-str": "error",
48 | "key-spacing": [
49 | "error",
50 | {
51 | "beforeColon": false,
52 | "afterColon": true
53 | }
54 | ],
55 | "object-curly-spacing": [
56 | "error",
57 | "always"
58 | ],
59 | "space-unary-ops": [
60 | "error",
61 | {
62 | "words": false,
63 | "nonwords": false
64 | }
65 | ],
66 | "space-before-function-paren": [
67 | "error",
68 | {
69 | "anonymous": "ignore",
70 | "named": "never"
71 | }
72 | ],
73 | "space-in-parens": [
74 | "error",
75 | "never"
76 | ],
77 | "comma-dangle": [
78 | "error",
79 | "never"
80 | ],
81 | "no-trailing-spaces": "error",
82 | "comma-style": [
83 | "error",
84 | "last"
85 | ],
86 | "eol-last": "error",
87 | "space-infix-ops": "error",
88 | "keyword-spacing": [
89 | "error",
90 | {}
91 | ],
92 | "space-before-blocks": [
93 | "error",
94 | "always"
95 | ],
96 | "linebreak-style": [
97 | "error",
98 | "unix"
99 | ],
100 | "valid-jsdoc": [
101 | "warn",
102 | {
103 | "requireReturn": false,
104 | "requireReturnDescription": false
105 | }
106 | ]
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | ## Creating Issues
4 |
5 | To file bug reports and feature suggestions, use the [issues page](../../issues?q=is%3Aissue).
6 |
7 | 1. Make sure the issue has not been filed before.
8 |
9 | 1. [Create a new Issue](../../issues/new) by filling out the form.
10 |
11 | 1. If an issue requires more information and receives no further input, it will be closed.
12 |
13 |
14 | ## Creating Pull Requests
15 |
16 | To create pull requests, use the [Pull Requests page](../../pulls).
17 |
18 | 1. [Create a new Issue](#creating-issues) describing the Bug or Feature you are addressing, to let others know you are working on it.
19 |
20 | 1. If a related issue exists, add a comment to let others know that you'll submit a pull request.
21 |
22 | 1. [Create a new Pull Request](../../pulls/new) by filling out the form.
23 |
24 |
25 | ### Setup
26 |
27 | 1. Fork the repository.
28 | 1. Clone your fork.
29 | 1. Make a branch for your change.
30 | 1. Run `npm install`.
31 |
32 | ## Commit Message
33 |
34 | Use the AngularJS commit message format:
35 |
36 | ```
37 | type(scope): subject
38 | ```
39 |
40 | #### type
41 | - `feat` New feature
42 | - `fix` A bugfix
43 | - `refactor` Code changes which are neither bugfix nor feature
44 | - `docs`: Documentation changes
45 | - `test`: New tests or changes to existing tests
46 | - `chore`: Changes to tooling or library changes
47 |
48 | #### scope
49 | The context of the changes, e.g. `preferences-window` or `compiler`. Use consistent names.
50 |
51 | #### subject
52 | A **brief, yet descriptive** description of the changes, using the following format:
53 |
54 | - present tense
55 | - lowercase
56 | - no period at the end
57 | - describe what the commit does
58 | - reference to issues via their id – e.g. `(#1337)`
59 |
60 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## 🤷🏽♂️ Current Behaviour
4 |
8 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua.
9 |
10 | ## 🎯 Expected Behaviour
11 |
15 | At vero eos et accusam et justo duo dolores et ea rebum.
16 |
17 | ## 👟 Steps to Reproduce (S2R)
18 |
21 | 1. At vero eos et accusam,
22 | 2. justo duo dolores et ea rebum,
23 | 3. stet clita kasd gubergren,
24 | 3. no sea takimata sanctus est.
25 |
26 | ## 🏡 Environmental Context
27 |
30 | **App Version**
31 | v0.0.1
32 | **Installation Type**
33 | Setup
34 | **Operating System**
35 | Windows 10 Enterprise x64 (15042.00)
36 |
37 |
38 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## 📋 Description
4 |
7 | Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum.
8 |
9 | ## 🗂 Type
10 |
13 | - [ ] 🍾 Feature
14 | - [ ] 🚨 Bugfix
15 | - [ ] 📒 Documentation
16 | - [ ] 👷 Internals
17 |
18 | ## 🔥 Severity
19 |
22 | - [ ] 💎 Non-Breaking Changes
23 | - [ ] 💔 Breaking Changes
24 |
25 | ## 🖥 Platforms
26 |
29 | - [x] 🍏 macOS
30 | - [x] 💾 Windows
31 | - [x] 🐧 Linux
32 |
33 | ## 🛃 Tests
34 |
37 | - [ ] My changes have been tested manually.
38 | - [ ] My changes are covered by automated testing methods.
39 |
40 | ## 👨🎓 Miscellaneous
41 |
44 | - [ ] My changes follow the style guide.
45 | - [ ] My changes require updates to the documentation.
46 |
47 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## BASELINE
2 | # macOS
3 | .DS_Store
4 |
5 | # Logs
6 | logs
7 | *.log
8 |
9 | # Temporary
10 | temp
11 | tmp
12 |
13 | # Cache
14 | .cache
15 | cache
16 | .sass-cache
17 |
18 | # JetBrains
19 | .idea
20 | *.sln.iml
21 |
22 | # VSCode
23 | .vscode
24 |
25 | # nodeJS
26 | node_modules
27 | jspm_packages
28 | bower_components
29 | pids
30 | *.pid
31 | *.seed
32 | lib-cov
33 | coverage
34 | .grunt
35 |
36 | # Compiled
37 | build
38 |
39 | # Generated documentation
40 | RELEASENOTES.md
41 |
--------------------------------------------------------------------------------
/.rpmlist:
--------------------------------------------------------------------------------
1 | el/7
2 | fedora/26
3 | opensuse/42.3
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | matrix:
2 | include:
3 | - os: linux
4 | sudo: required
5 | dist: trusty
6 | group: edge
7 | compiler: clang
8 |
9 | language: c
10 |
11 | branches:
12 | only:
13 | - master
14 |
15 | cache:
16 | directories:
17 | - "./node_modules"
18 | - "${HOME}/.yarn-cache"
19 |
20 | addons:
21 | apt:
22 | packages:
23 | - bsdtar
24 | - g++-multilib
25 | - gcc-multilib
26 | - graphicsmagick
27 | - icnsutils
28 | - rpm
29 | - xz-utils
30 |
31 | before_install:
32 | - echo "🚦 Authorizing Build"
33 | - echo "🔧 Setting up Node"
34 | - curl -o- https://raw.githubusercontent.com/creationix/nvm/master/install.sh | NVM_DIR="${HOME}"/.nvm sh
35 | - source "${HOME}"/.nvm/nvm.sh && nvm install 9.1.0 && nvm use 9.1.0
36 | - npm --global update npm
37 | - npm --global install yarn
38 |
39 | install:
40 | - echo "📥 Installing Dependencies"
41 | - yarn install
42 |
43 | script:
44 | - echo "📦 Building"
45 | - npm run build --metadata
46 |
47 | after_success:
48 | - echo "📮 Deploying to Github"
49 | - npm run deploy
50 |
51 | notifications:
52 | webhooks:
53 | on_failure: always
54 | on_start: never
55 | on_success: change
56 | urls:
57 | - https://webhooks.gitter.im/e/a1a9de50af1c703bfe6c
58 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | ## Creating Issues
4 |
5 | To file bug reports and feature suggestions, use the ["Issues"](https://github.com/sidneys/pb-for-desktop/issues?q=is%3Aissue) page.
6 |
7 | 1. Make sure the issue has not been filed before.
8 | 1. Create a new issue by filling out [the issue form](https://github.com/sidneys/pb-for-desktop/issues/new).
9 | 1. If an issue requires more information and receives no further input, it will be closed.
10 |
11 |
12 | ## Creating Pull Requests
13 |
14 | To create pull requests, use the ["Pull Requests"](https://github.com/sidneys/pb-for-desktop/pulls) page.
15 |
16 | 1. [Create a new Issue](#creating-issues) describing the Bug or Feature you are addressing, to let others know you are working on it.
17 | 1. If a related issue exists, add a comment to let others know that you'll submit a pull request.
18 | 1. Create a new pull request by filling out [the pull request form](https://github.com/sidneys/pb-for-desktop/pulls/compare).
19 |
20 |
21 | ### Setup
22 |
23 | 1. Fork the repository.
24 | 1. Clone your fork.
25 | 1. Make a branch for your change.
26 | 1. Run `npm install`.
27 |
28 | ## Commit Message
29 |
30 | Use the AngularJS commit message format:
31 |
32 | ```
33 | type(scope): subject
34 | ```
35 |
36 | #### type
37 | - `feat` New feature
38 | - `fix` A bugfix
39 | - `refactor` Code changes which are neither bugfix nor feature
40 | - `docs`: Documentation changes
41 | - `test`: New tests or changes to existing tests
42 | - `chore`: Changes to tooling or library changes
43 |
44 | #### scope
45 | The context of the changes, e.g. `preferences-window` or `compiler`. Use consistent names.
46 |
47 | #### subject
48 | A **brief, yet descriptive** description of the changes, using the following format:
49 |
50 | - present tense
51 | - lowercase
52 | - no period at the end
53 | - describe what the commit does
54 | - reference to issues via their id – e.g. `(#1337)`
55 |
56 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | CC0 1.0 Universal
2 | ==================
3 |
4 | Statement of Purpose
5 | ---------------------
6 |
7 | The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work").
8 |
9 | Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others.
10 |
11 | For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights.
12 |
13 | 1. Copyright and Related Rights.
14 | --------------------------------
15 | A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following:
16 |
17 | i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work;
18 | ii. moral rights retained by the original author(s) and/or performer(s);
19 | iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work;
20 | iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below;
21 | v. rights protecting the extraction, dissemination, use and reuse of data in a Work;
22 | vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and
23 | vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof.
24 |
25 | 2. Waiver.
26 | -----------
27 | To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose.
28 |
29 | 3. Public License Fallback.
30 | ----------------------------
31 | Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose.
32 |
33 | 4. Limitations and Disclaimers.
34 | --------------------------------
35 |
36 | a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document.
37 | b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law.
38 | c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work.
39 | d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work.
40 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Desktop Dimmer []() [](https://travis-ci.org/sidneys/desktop-dimmer) [](https://ci.appveyor.com/project/sidneys/desktop-dimmer) [](https://npmjs.com/package/desktop-dimmer) [](https://npmjs.com/package/desktop-dimmer) [](https://npmjs.com/package/desktop-dimmer)
2 |
3 |
4 |
5 | Enable darker-than-dark dimming for internal and external screens.
6 | Available for macOS, Windows and Linux (Beta).
7 |
8 |
9 |
10 | ------
11 |
12 | 
13 | 
14 |
15 | ------
16 |
17 | > **Cross-Platform**
18 |
19 | Tested on macOS Sierra, Windows 10 Anniversary. Beta support for Ubuntu 16.10.
20 |
21 | > **Lean**
22 |
23 | Small resource footprint, minimal User Interface.
24 |
25 | > **Unobstrusive**
26 |
27 | Settings are persisted and restored per-Display without any configuration.
28 |
29 | > **Smart**
30 |
31 | Heading out? Disconnecting and reconnecting external displays are handled seamlessly.
32 |
33 | > **Open Source**
34 |
35 | GitHub-based workflow, MIT licensed.
36 |
37 |
38 | ## Contents
39 |
40 | 1. [Installation](#installation)
41 | 2. [Developers](#development)
42 | 3. [Continuous Integration](#continuous-integration)
43 | 4. [Up Next](#up-next)
44 | 5. [Contact](#contact)
45 | 6. [Author](#author)
46 |
47 |
48 | ## Installation
49 |
50 | ### Standard Installation
51 |
52 | Download the latest version of Desktop Dimmer on the [Releases](https://github.com/sidneys/desktop-dimmer/releases) page.
53 |
54 | ### Installation as Commandline Tool
55 |
56 | ```bash
57 | npm install --global desktop-dimmer # Installs the node CLI module
58 | desktop-dimmer # Runs it
59 | ```
60 |
61 |
62 | ## Developers
63 |
64 | ### Sources
65 |
66 | Clone the repo and install dependencies.
67 |
68 | ```shell
69 | git clone https://github.com/sidneys/desktop-dimmer.git desktop-dimmer
70 | cd desktop-dimmer
71 | npm install
72 | ```
73 |
74 | ### Scripts
75 |
76 | #### npm run **start**
77 |
78 | Run the app with integrated Electron.
79 |
80 | ```bash
81 | npm run start
82 | npm run start:dev # with Debugging Tools
83 | npm run start:livereload # with Debugging Tools and Livereload
84 | ```
85 |
86 | #### npm run **localsetup**
87 |
88 | Install the app in the System app folder and start it.
89 |
90 | ```bash
91 | npm run localsetup
92 | npm run localsetup:rebuild # Build before installation
93 | npm run localsetup:rebuild:dev # Build before installation, use Developer Tools
94 | ```
95 |
96 | #### npm run **build**
97 |
98 | Build the app and create installers (see [requirements](#build-requirements)).
99 |
100 | ```bash
101 | npm run build # build all available platforms
102 | npm run build macos windows # build specific platforms (macos/linux/windows)
103 | ```
104 |
105 | ### Build Requirements
106 |
107 | * Building for Windows requires [`wine`](https://winehq.org) and [`mono`](https://nsis.sourceforge.net/Docs/Chapter3.htm) (on macOS, Linux)
108 | * Building for Linux requires [`fakeroot`](https://wiki.debian.org/FakeRoot) and [`dpkg `](https://wiki.ubuntuusers.de/dpkg/) (on macOS, Windows)
109 | * Only macOS can build for other platforms.
110 |
111 | #### macOS Build Setup
112 |
113 | Install [Homebrew](https://brew.sh), then run:
114 |
115 | ```bash
116 | brew install wine mono fakeroot dpkg
117 | ```
118 |
119 | #### Linux Build Setup
120 |
121 | ```bash
122 | sudo apt-get install wine mono fakeroot dpkg
123 | ```
124 |
125 |
126 | ## Continuous Integration
127 |
128 | > Turnkey **build-in-the-cloud** for Windows 10, macOS and Linux.
129 |
130 | The process is managed by a custom layer of node scripts and Electron-optimized configuration templates.
131 | Completed Installation packages are deployed to [GitHub Releases](https://github.com/sidneys/desktop-dimmer/releases). Builds for all platforms and architectures take about 5 minutes.
132 | Backed by the open-source-friendly guys at [Travis](https://travis-ci.org/) and [AppVeyor](https://ci.appveyor.com/) and running [electron-packager](https://github.com/electron-userland/electron-packager) under the hood.
133 |
134 | ### Setup
135 |
136 | 1. [Fork](https://github.com/sidneys/desktop-dimmer/fork) the repo
137 | 2. Generate your GitHub [Personal Access Token](https://github.com/settings/tokens) using "repo" as scope. Copy it to the clipboard.
138 | 3. **macOS + Linux**
139 | 1. Sign in to [Travis](https://travis-ci.org/) using GitHub.
140 | 2. Open your [Travis Profile](https://travis-ci.org/profile), click "Sync Account" and wait for the process to complete.
141 | 3. Find this repository in the list, enable it and click "⚙" to open its settings.
142 | 4. Create a new Environment Variable named **GITHUB_TOKEN**. Paste your Token from step 2 as *value*.
143 | 4. **Windows**
144 | 1. Sign in to [AppVeyor](https://ci.appveyor.com/) using GitHub.
145 | 2. Click on ["New Project"](https://ci.appveyor.com/projects/new), select "GitHub", look up this repo in the list and click "Add".
146 | 3. After import navigate to the *Settings* > *Environment* subsection
147 | 4. Select "Add Variable", insert **GITHUB_TOKEN** for *name*, paste your Token as *value*. Save.
148 |
149 | ### Triggering Builds
150 |
151 | 1. Add a new Tag to start the build process:
152 |
153 | ```shell
154 | git tag -a v1.0.1
155 | git push --tags
156 | ```
157 | The builds are started in parallel and added to the "Releases" page of the GitHub repo (in draft mode).
158 |
159 | 2. Use the editing feature to publish the new app version.
160 |
161 | 3. There is no step 3
162 |
163 |
164 | ## Up Next 
165 |
166 | - [ ] Colored Shades
167 | - [ ] In-App Updates (Squirrel)
168 | - [ ] Signed binaries
169 | - [ ] E2E Testing ([Spectron](https://github.com/electron/spectron))
170 |
171 |
172 | ## Contact 
173 |
174 | * [Gitter](https://gitter.im/sidneys/desktop-dimmer) Developer Chat
175 | * [Issues](https://github.com/sidneys/desktop-dimmer/issues) File, track and discuss features and issues
176 | * [Wiki](https://github.com/sidneys/desktop-dimmer/wiki) Read or contribute to the project Wiki
177 |
178 |
179 | ## Author
180 |
181 | [sidneys](https://sidneys.github.io) 2016
182 |
--------------------------------------------------------------------------------
/RELEASENOTES.json:
--------------------------------------------------------------------------------
1 | {
2 | "4.0.4": {
3 | "🍾 features": [
4 | "Adds preference: application auto updates (#26)"
5 | ],
6 | "👷 internals": [
7 | "Upgrades `node_modules`",
8 | "Upgrades `services`"
9 | ]
10 | },
11 | "4.0.3": {
12 | "🍾 features": [
13 | "Per-display enabling and disabling of Desktop Dimmer",
14 | "Improved user interface performance",
15 | "Extended range for dimming factor",
16 | "Updated application icon"
17 | ],
18 | "💎 improved": [
19 | "Augmentations to configuration storage strategy",
20 | "Various stability and performance improvements"
21 | ],
22 | "🚨 fixed": [
23 | "Upgraded to work with Apple macOS High Sierra (macOS)",
24 | "Upgraded to work with latest Gnome-based desktop environments (Linux) (#22)",
25 | "Upgraded to work with latest Ubuntu-based distributions (Linux) (#22)"
26 | ],
27 | "📒 documentation": [
28 | "Expanded `README.md`",
29 | "Extended JSDoc coverage"
30 | ],
31 | "👷 internals": [
32 | "Rollback `Electron` to `v1.4.16` upstream issue (https://github.com/electron/electron/issues/2170)",
33 | "Extensive refactoring",
34 | "Upgrades `node_modules`",
35 | "Upgrades `services`",
36 | "Upgrades `lib`"
37 | ]
38 | }
39 | }
--------------------------------------------------------------------------------
/app/fonts/MaterialIcons-Regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/app/fonts/MaterialIcons-Regular.eot
--------------------------------------------------------------------------------
/app/fonts/MaterialIcons-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/app/fonts/MaterialIcons-Regular.ttf
--------------------------------------------------------------------------------
/app/fonts/MaterialIcons-Regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/app/fonts/MaterialIcons-Regular.woff
--------------------------------------------------------------------------------
/app/fonts/MaterialIcons-Regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/app/fonts/MaterialIcons-Regular.woff2
--------------------------------------------------------------------------------
/app/fonts/Roboto-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/app/fonts/Roboto-Light.ttf
--------------------------------------------------------------------------------
/app/fonts/Roboto-Light.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/app/fonts/Roboto-Light.woff
--------------------------------------------------------------------------------
/app/fonts/Roboto-Light.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/app/fonts/Roboto-Light.woff2
--------------------------------------------------------------------------------
/app/fonts/photon-entypo.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/app/fonts/photon-entypo.eot
--------------------------------------------------------------------------------
/app/fonts/photon-entypo.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/app/fonts/photon-entypo.ttf
--------------------------------------------------------------------------------
/app/fonts/photon-entypo.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/app/fonts/photon-entypo.woff
--------------------------------------------------------------------------------
/app/html/controller.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/html/overlay.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/html/preferences.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/images/darwin-tray-icon-default-Template.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/app/images/darwin-tray-icon-default-Template.png
--------------------------------------------------------------------------------
/app/images/darwin-tray-icon-default-Template@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/app/images/darwin-tray-icon-default-Template@2x.png
--------------------------------------------------------------------------------
/app/images/darwin-tray-icon-translucent-Template.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/app/images/darwin-tray-icon-translucent-Template.png
--------------------------------------------------------------------------------
/app/images/darwin-tray-icon-translucent-Template@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/app/images/darwin-tray-icon-translucent-Template@2x.png
--------------------------------------------------------------------------------
/app/images/linux-tray-icon-default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/app/images/linux-tray-icon-default.png
--------------------------------------------------------------------------------
/app/images/linux-tray-icon-translucent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/app/images/linux-tray-icon-translucent.png
--------------------------------------------------------------------------------
/app/images/win32-tray-icon-default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/app/images/win32-tray-icon-default.png
--------------------------------------------------------------------------------
/app/images/win32-tray-icon-translucent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/app/images/win32-tray-icon-translucent.png
--------------------------------------------------------------------------------
/app/scripts/main/components/application.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 |
4 | /**
5 | * Modules
6 | * Node
7 | * @constant
8 | */
9 | const events = require('events');
10 | const path = require('path');
11 |
12 | /**
13 | * Modules
14 | * External
15 | * @constant
16 | */
17 | const appRootPath = require('app-root-path');
18 | const platformTools = require('@sidneys/platform-tools');
19 |
20 | /**
21 | * Modules
22 | * Configuration
23 | */
24 | appRootPath['setPath'](path.join(__dirname, '..', '..', '..', '..'));
25 | events.EventEmitter.defaultMaxListeners = Infinity;
26 | if (platformTools.isLinux) {
27 | process.env.XDG_CURRENT_DESKTOP = 'Unity';
28 | }
29 |
30 | /**
31 | * Modules
32 | * Internal
33 | * @constant
34 | */
35 | /* eslint-disable no-unused-vars */
36 | const globals = require(path.join(appRootPath['path'], 'app', 'scripts', 'main', 'components', 'globals'));
37 | const menubar = require(path.join(appRootPath['path'], 'app', 'scripts', 'main', 'components', 'menubar'));
38 | const updaterService = require(path.join(appRootPath['path'], 'app', 'scripts', 'main', 'services', 'updater-service'));
39 | const debugService = require(path.join(appRootPath['path'], 'app', 'scripts', 'main', 'services', 'debug-service'));
40 | const configurationManager = require(path.join(appRootPath['path'], 'app', 'scripts', 'main', 'managers', 'configuration-manager'));
41 | const overlayManager = require(path.join(appRootPath.path, 'app', 'scripts', 'main', 'managers', 'overlay-manager'));
42 | const trayMenu = require(path.join(appRootPath['path'], 'app', 'scripts', 'main', 'menus', 'tray-menu'));
43 | const preferencesWindow = require(path.join(appRootPath.path, 'app', 'scripts', 'main', 'windows', 'preferences-window'));
44 | /* eslint-enable */
45 |
--------------------------------------------------------------------------------
/app/scripts/main/components/globals.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 |
4 | /**
5 | * Modules
6 | * Node
7 | * @constant
8 | */
9 | const path = require('path');
10 |
11 | /**
12 | * Modules
13 | * External
14 | * @constant
15 | */
16 | const appRootPath = require('app-root-path');
17 |
18 | /**
19 | * Modules
20 | * Internal
21 | * @constant
22 | */
23 | const packageJson = require(path.join(appRootPath['path'], 'package.json'));
24 |
25 |
26 | /**
27 | * Manifest
28 | * @global
29 | * @constant
30 | */
31 | global.manifest = {
32 | homepage: packageJson.homepage,
33 | name: packageJson.name,
34 | productName: packageJson.productName || packageJson.name,
35 | version: packageJson.version
36 | };
37 |
38 | /**
39 | * State
40 | * @global
41 | */
42 | global.state = {
43 | isQuitting: false
44 | };
45 |
46 |
--------------------------------------------------------------------------------
/app/scripts/main/components/menubar.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 |
4 | /**
5 | * Modules
6 | * Node
7 | * @constant
8 | */
9 | const path = require('path');
10 | const url = require('url');
11 |
12 | /**
13 | * Modules
14 | * Electron
15 | * @constant
16 | */
17 | const electron = require('electron');
18 | const { systemPreferences } = electron;
19 |
20 | /**
21 | * Modules
22 | * External
23 | * @constant
24 | */
25 | const appRootPath = require('app-root-path')['path'];
26 | const electronMenubar = require('menubar');
27 |
28 | /**
29 | * Modules
30 | * Internal
31 | * @constant
32 | */
33 | const isDebug = require('@sidneys/is-env')('debug');
34 | const logger = require('@sidneys/logger')({ write: false });
35 | const platformTools = require('@sidneys/platform-tools');
36 |
37 |
38 | /**
39 | * Filesystem
40 | * @constant
41 | * @default
42 | */
43 | const windowHtml = path.join(appRootPath, 'app', 'html', 'controller.html');
44 |
45 | /**
46 | * Application
47 | * @constant
48 | * @default
49 | */
50 | const windowUrl = url.format({ protocol: 'file:', pathname: windowHtml });
51 |
52 |
53 | /**
54 | * Tray icons
55 | * @constant
56 | */
57 | const trayIconDefault = path.join(appRootPath, 'app', 'images', `${platformTools.type}-tray-icon-default${platformTools.templateImageExtension(platformTools.type)}`);
58 |
59 |
60 | /**
61 | * Adapt BrowserWindow to OS theme
62 | * @param {BrowserWindow} browserWindow - Target window
63 | */
64 | let handleThemeChange = (browserWindow) => {
65 | logger.debug('handleThemeChange');
66 |
67 | const isDarkTheme = systemPreferences.isDarkMode();
68 |
69 | isDarkTheme ? browserWindow.setVibrancy('dark') : browserWindow.setVibrancy('light');
70 | };
71 |
72 | /**
73 | * @class Menubar
74 | * @property {electronMenubar} menubar - electronMenubar
75 | * @property {Electron.App} app - Menubar App
76 | */
77 | class Menubar {
78 | /**
79 | * @constructor
80 | */
81 | constructor() {
82 | logger.debug('constructor');
83 |
84 | this.menubar = electronMenubar({
85 | alwaysOnTop: isDebug,
86 | backgroundColor: platformTools.isMacOS ? void 0 : '#404040',
87 | frame: false,
88 | hasShadow: false,
89 | height: 48,
90 | maxWidth: 256,
91 | minHeight: 48,
92 | minWidth: 256,
93 | resizable: false,
94 | transparent: Boolean(platformTools.isMacOS),
95 | vibrancy: platformTools.isMacOS ? 'dark' : void 0,
96 | width: 256,
97 | icon: trayIconDefault,
98 | index: windowUrl,
99 | preloadWindow: true,
100 | showDockIcon: isDebug
101 | });
102 |
103 | this.app = this.menubar.app;
104 |
105 | this.init();
106 | }
107 |
108 | /**
109 | * Init
110 | */
111 | init() {
112 | logger.debug('init');
113 |
114 | /**
115 | * Linux
116 | */
117 | if (platformTools.isLinux) {
118 | this.app.commandLine.appendSwitch('enable-transparent-visuals');
119 | this.app.commandLine.appendSwitch('disable-gpu');
120 | }
121 |
122 | /**
123 | * @listens Menubar.menubar#create-window
124 | */
125 | this.menubar.on('create-window', () => {
126 | logger.debug('Menubar.menubar#create-window');
127 | });
128 |
129 | /**
130 | * @listens Electron.App#before-quit
131 | */
132 | this.app.on('before-quit', () => {
133 | logger.debug('app#before-quit');
134 |
135 | global.state.isQuitting = true;
136 | });
137 |
138 | /**
139 | * @listens Electron.App#Event:ready
140 | */
141 | this.app.once('ready', () => {
142 | logger.debug('app#ready');
143 | });
144 |
145 | /**
146 | * @listens Menubar.menubar#after-create-window
147 | */
148 | this.menubar.on('after-create-window', () => {
149 | logger.debug('Menubar#after-create-window');
150 |
151 | if (isDebug) {
152 | this.menubar.window.openDevTools({ mode: 'undocked' });
153 | }
154 |
155 | /**
156 | * @listens Menubar.window#show
157 | */
158 | this.menubar.window.on('show', () => {
159 | logger.debug('Menubar.window#show');
160 |
161 | /**
162 | * @fires Menubar.window.webContents#controller-show
163 | */
164 | this.menubar.window.webContents.send('controller-show');
165 |
166 | /**
167 | * Linux
168 | */
169 | if (platformTools.isLinux) {
170 | const cursorPosition = electron.screen.getCursorScreenPoint();
171 | const targetPosition = {
172 | x: cursorPosition.x - (this.menubar.window.getBounds().width + 20),
173 | y: cursorPosition.y
174 | };
175 |
176 | this.menubar.window.setPosition(targetPosition.x, targetPosition.y);
177 | }
178 | });
179 |
180 | /**
181 | * @listens Menubar.window#hide
182 | */
183 | this.menubar.window.on('hide', () => {
184 | logger.debug('Menubar.window#hide');
185 |
186 | /**
187 | * @fires Menubar.window.webContents#controller-hide
188 | */
189 | this.menubar.window.webContents.send('controller-hide');
190 | });
191 |
192 | /**
193 | * @listens Menubar.window.webContents#dom-ready
194 | */
195 | this.menubar.window.webContents.on('dom-ready', () => {
196 | logger.debug('Menubar.window.webContents#dom-ready');
197 |
198 | if (isDebug) {
199 | this.menubar.window.openDevTools({ mode: 'undocked' });
200 | }
201 | });
202 | });
203 |
204 | /**
205 | * @listens systemPreferences:AppleInterfaceThemeChangedNotification
206 | */
207 | if (platformTools.isMacOS) {
208 |
209 | systemPreferences.subscribeNotification('AppleInterfaceThemeChangedNotification', () => {
210 | logger.debug('systemPreferences#AppleInterfaceThemeChangedNotification');
211 |
212 | handleThemeChange(this.menubar.window);
213 | });
214 | }
215 | }
216 | }
217 |
218 | /**
219 | * Init
220 | */
221 | let init = () => {
222 | logger.debug('init');
223 |
224 | // Ensure single instance
225 | if (!global.menubar) {
226 | global.menubar = new Menubar();
227 | }
228 | };
229 |
230 |
231 | init();
232 |
233 |
234 | /**
235 | * @exports
236 | */
237 | module.exports = global.menubar;
238 |
--------------------------------------------------------------------------------
/app/scripts/main/managers/configuration-manager.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 |
4 | /**
5 | * Modules
6 | * Node
7 | * @constant
8 | */
9 | const path = require('path');
10 |
11 | /**
12 | * Modules
13 | * External
14 | * @constant
15 | */
16 | const _ = require('lodash');
17 | const Appdirectory = require('appdirectory');
18 | const AutoLaunch = require('auto-launch');
19 | const electronSettings = require('electron-settings');
20 |
21 | /**
22 | * Modules
23 | * Internal
24 | * @constant
25 | */
26 | const logger = require('@sidneys/logger')({ write: true });
27 |
28 |
29 | /**
30 | * Application
31 | * @constant
32 | * @default
33 | */
34 | const appName = global.manifest.name;
35 | const appVersion = global.manifest.version;
36 |
37 | /**
38 | * Filesystem
39 | * @constant
40 | * @default
41 | */
42 | const appSettingsFilepath = path.join(path.dirname(electronSettings.file()), `${appName}.json`);
43 | const appLogDirectory = (new Appdirectory(appName)).userLogs();
44 |
45 | /**
46 | * Modules
47 | * Configuration
48 | */
49 | const app = global.menubar.menubar.app;
50 | const autoLauncher = new AutoLaunch({ name: appName, mac: { useLaunchAgent: true } });
51 | electronSettings.setPath(appSettingsFilepath);
52 | /** @namespace Electron */
53 | /** @namespace electronSettings.delete */
54 | /** @namespace electronSettings.deleteAll */
55 | /** @namespace electronSettings.file */
56 | /** @namespace electronSettings.get */
57 | /** @namespace electronSettings.getAll */
58 | /** @namespace electronSettings.set */
59 | /** @namespace electronSettings.setAll */
60 | /** @namespace electronSettings.setPath */
61 |
62 | /**
63 | * Configuration Items
64 | */
65 | let configurationItems = {
66 | /**
67 | * appAutoUpdate
68 | */
69 | appAutoUpdate: {
70 | keypath: 'appAutoUpdate',
71 | default: true,
72 | init() {
73 | logger.debug(this.keypath, 'init');
74 | },
75 | get() {
76 | logger.debug(this.keypath, 'get');
77 |
78 | return electronSettings.get(this.keypath);
79 | },
80 | set(value) {
81 | logger.debug(this.keypath, 'set');
82 |
83 | electronSettings.set(this.keypath, value);
84 | }
85 | },
86 | /**
87 | * appChangelog
88 | */
89 | appChangelog: {
90 | keypath: 'appChangelog',
91 | default: '',
92 | init() {
93 | logger.debug(this.keypath, 'init');
94 | },
95 | get() {
96 | logger.debug(this.keypath, 'get');
97 |
98 | return electronSettings.get(this.keypath);
99 | },
100 | set(value) {
101 | logger.debug(this.keypath, 'set');
102 |
103 | electronSettings.set(this.keypath, value);
104 | }
105 | },
106 | /**
107 | * appLastVersion
108 | */
109 | appLastVersion: {
110 | keypath: 'appLastVersion',
111 | default: appVersion,
112 | init() {
113 | logger.debug(this.keypath, 'init');
114 | },
115 | get() {
116 | logger.debug(this.keypath, 'get');
117 |
118 | return electronSettings.get(this.keypath);
119 | },
120 | set(value) {
121 | logger.debug(this.keypath, 'set');
122 |
123 | electronSettings.set(this.keypath, value);
124 | }
125 | },
126 | /**
127 | * appLaunchOnStartup
128 | */
129 | appLaunchOnStartup: {
130 | keypath: 'appLaunchOnStartup',
131 | default: true,
132 | init() {
133 | logger.debug(this.keypath, 'init');
134 |
135 | this.implement(this.get());
136 | },
137 | get() {
138 | logger.debug(this.keypath, 'get');
139 |
140 | return electronSettings.get(this.keypath);
141 | },
142 | set(value) {
143 | logger.debug(this.keypath, 'set', value);
144 |
145 | this.implement(value);
146 | electronSettings.set(this.keypath, value);
147 | },
148 | implement(value) {
149 | logger.debug(this.keypath, 'implement', value);
150 |
151 | if (value) {
152 | autoLauncher.enable();
153 | } else {
154 | autoLauncher.disable();
155 | }
156 | }
157 | },
158 | /**
159 | * appLogFile
160 | */
161 | appLogFile: {
162 | keypath: 'appLogFile',
163 | default: path.join(appLogDirectory, appName + '.log'),
164 | init() {
165 | logger.debug(this.keypath, 'init');
166 | },
167 | get() {
168 | logger.debug(this.keypath, 'get');
169 |
170 | return electronSettings.get(this.keypath);
171 | },
172 | set(value) {
173 | logger.debug(this.keypath, 'set');
174 |
175 | electronSettings.set(this.keypath, value);
176 | }
177 | },
178 | /**
179 | * overlayConfigurationDatabase
180 | */
181 | overlayConfigurationDatabase: {
182 | keypath: 'overlayConfigurationDatabase',
183 | default: [],
184 | init() {
185 | logger.debug(this.keypath, 'init');
186 | },
187 | get() {
188 | logger.debug(this.keypath, 'get');
189 |
190 | return electronSettings.get(this.keypath);
191 | },
192 | set(value) {
193 | logger.debug(this.keypath, 'set', value);
194 |
195 | electronSettings.set(this.keypath, value);
196 | }
197 | }
198 | };
199 |
200 | /**
201 | * Access single item
202 | * @param {String} playlistItemId - Configuration item identifier
203 | * @returns {Object|void}
204 | */
205 | let getItem = (playlistItemId) => {
206 | //logger.debug('getConfigurationItem', playlistItemId);
207 |
208 | if (configurationItems.hasOwnProperty(playlistItemId)) {
209 | return configurationItems[playlistItemId];
210 | }
211 | };
212 |
213 | /**
214 | * Get defaults of all items
215 | * @returns {Object}
216 | */
217 | let getConfigurationDefaults = () => {
218 | logger.debug('getConfigurationDefaults');
219 |
220 | let defaults = {};
221 | for (let item of Object.keys(configurationItems)) {
222 | defaults[item] = getItem(item).default;
223 | }
224 |
225 | return defaults;
226 | };
227 |
228 | /**
229 | * Delete all settings
230 | * @param {function(*)} callback - Callback
231 | */
232 | let deleteAll = (callback = () => {}) => {
233 | logger.debug('deleteAll');
234 |
235 | electronSettings.deleteAll();
236 |
237 | callback();
238 | };
239 |
240 | /**
241 | * Set defaults
242 | * @param {function(*)} callback - Callback
243 | */
244 | let applyAllDefaults = (callback = () => {}) => {
245 | logger.debug('applyAllDefaults');
246 |
247 | electronSettings.setAll(_.defaultsDeep(electronSettings.getAll(), getConfigurationDefaults()));
248 |
249 | callback();
250 | };
251 |
252 | /**
253 | * Reset all to defaults
254 | * @param {function(*)} callback - Callback
255 | */
256 | let resetAllToDefaults = (callback = () => {}) => {
257 | logger.debug('resetAllToDefaults');
258 |
259 | deleteAll(() => applyAllDefaults(() => callback()));
260 | };
261 |
262 | /**
263 | * Run init() method of all configuration items
264 | * @param {function(*)} callback - Callback
265 | * @function
266 | */
267 | let runItemInitializers = (callback = () => {}) => {
268 | logger.debug('runItemInitializers');
269 |
270 | let configurationItemList = Object.keys(configurationItems);
271 |
272 | configurationItemList.forEach((item, itemIndex) => {
273 | getItem(item).init();
274 |
275 | // Last item
276 | if (configurationItemList.length === (itemIndex + 1)) {
277 | logger.debug('initConfigurationItems', 'complete');
278 | callback();
279 | }
280 | });
281 | };
282 |
283 | /**
284 | * Remove unknown items
285 | * @param {function(*)} callback - Callback
286 | * @function
287 | */
288 | let removeLegacyItems = (callback = () => {}) => {
289 | logger.debug('removeLegacyItems');
290 |
291 | let savedSettings = electronSettings.getAll();
292 | let savedSettingsList = Object.keys(savedSettings);
293 |
294 | savedSettingsList.forEach((item, itemIndex) => {
295 | if (!configurationItems.hasOwnProperty(item)) {
296 | electronSettings.delete(item);
297 | logger.debug('cleanConfiguration', 'deleted', item);
298 | }
299 |
300 | // Last item
301 | if (savedSettingsList.length === (itemIndex + 1)) {
302 | logger.debug('cleanConfiguration', 'complete');
303 | callback();
304 | }
305 | });
306 | };
307 |
308 |
309 | /**
310 | * Init
311 | */
312 | let init = () => {
313 | logger.debug('init');
314 |
315 | applyAllDefaults(() => runItemInitializers(() => removeLegacyItems(() => logger.debug('init complete'))));
316 | };
317 |
318 |
319 | /**
320 | * @listens Electron.App#Event:ready
321 | */
322 | app.once('ready', () => {
323 | logger.debug('app#ready');
324 |
325 | init();
326 | });
327 |
328 |
329 | /**
330 | * @listens Electron.App#before-quit
331 | */
332 | app.on('quit', () => {
333 | logger.debug('app#quit');
334 |
335 | // Prettify
336 | // electronSettings.setAll(electronSettings.getAll(), { prettify: true });
337 |
338 | logger.debug('appSettingsFilepath', electronSettings.file());
339 | logger.debug('appSettings', electronSettings.getAll());
340 | });
341 |
342 | /**
343 | * @exports
344 | */
345 | module.exports = getItem;
346 | module.exports.resetAllToDefaults = resetAllToDefaults;
347 |
--------------------------------------------------------------------------------
/app/scripts/main/managers/overlay-manager.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Modules
5 | * Node
6 | * @constant
7 | */
8 | const events = require('events');
9 | const path = require('path');
10 |
11 | /**
12 | * Modules
13 | * Electron
14 | * @constant
15 | */
16 | const electron = require('electron');
17 | const { BrowserWindow } = electron || electron.remote;
18 |
19 | /**
20 | * Modules
21 | * Configuration
22 | */
23 | const app = global.menubar.app;
24 |
25 | /**
26 | * Modules
27 | * External
28 | * @constant
29 | */
30 | const _ = require('lodash');
31 | const appRootPath = require('app-root-path').path;
32 | const logger = require('@sidneys/logger')({ write: true });
33 |
34 |
35 | /**
36 | * Modules
37 | * Internal
38 | * @constant
39 | */
40 | const configurationManager = require(path.join(appRootPath, 'app', 'scripts', 'main', 'managers', 'configuration-manager'));
41 | const OverlayWindow = require(path.join(appRootPath, 'app', 'scripts', 'main', 'windows', 'overlay-window'));
42 |
43 |
44 | /**
45 | * Retrieve OverlayConfiguration
46 | * @param {OverlayId} id - Overlay id
47 | * @return {OverlayConfiguration} - Overlay configuration
48 | */
49 | let retrieveOverlayConfiguration = (id) => {
50 | const map = new Map(configurationManager('overlayConfigurationDatabase').get());
51 |
52 | return map.get(id);
53 | };
54 |
55 | /**
56 | * Store OverlayConfiguration
57 | * @param {OverlayId} id - Overlay id
58 | * @param {OverlayConfiguration} configuration - Overlay configuration
59 | */
60 | let storeOverlayConfiguration = (id, configuration) => {
61 | const map = new Map(configurationManager('overlayConfigurationDatabase').get());
62 | map.set(id, configuration);
63 |
64 | configurationManager('overlayConfigurationDatabase').set([...map]);
65 | };
66 |
67 |
68 | /**
69 | * @class OverlayManager
70 | */
71 | class OverlayManager extends events.EventEmitter {
72 | /**
73 | * @constructor
74 | */
75 | constructor() {
76 | logger.debug('constructor');
77 |
78 | super();
79 | this.init();
80 | }
81 |
82 | /**
83 | * Init
84 | */
85 | init() {
86 | logger.debug('init');
87 |
88 | /**
89 | * @listens Electron.App#before-quit
90 | */
91 | app.on('before-quit', () => {
92 | logger.debug('app#before-quit');
93 |
94 | this.storeConfigurations();
95 | });
96 |
97 | /**
98 | * @listens Electron.Screen
99 | */
100 | const eventList = ['display-metrics-changed', 'display-added', 'display-removed'];
101 | eventList.forEach((eventName) => {
102 | electron.screen.on(eventName, () => {
103 | logger.debug(`Electron.Screen#${eventName}`);
104 |
105 | this.resetAll();
106 | });
107 | });
108 |
109 | this.createAll();
110 | this.retrieveConfigurations();
111 | }
112 |
113 | /**
114 | * Get all overlayWindows
115 | * @return {Array}
116 | */
117 | getAll() {
118 | logger.debug('getAll');
119 |
120 | return BrowserWindow.getAllWindows().filter((browserWindow) => {
121 | if (!browserWindow.constructor.name) { return; }
122 |
123 | return browserWindow.constructor.name === 'OverlayWindow';
124 | });
125 | }
126 |
127 | /**
128 | * Check whether any overlays are visible
129 | * @return {Boolean}
130 | */
131 | isVisible() {
132 | logger.debug('isVisible');
133 |
134 | const overlayWindowList = this.getAll();
135 | const visibleOverlayWindowList = _.filter(overlayWindowList, overlayWindow => overlayWindow.overlayConfiguration.visibility === true);
136 |
137 | return visibleOverlayWindowList.length > 0;
138 | }
139 |
140 | /**
141 | * Retrieve all overlay configurations
142 | */
143 | retrieveConfigurations() {
144 | logger.debug('retrieveConfigurations');
145 |
146 | const overlayWindowList = this.getAll();
147 |
148 | overlayWindowList.forEach((overlayWindow) => {
149 | const overlayConfiguration = retrieveOverlayConfiguration(overlayWindow.overlayId);
150 | if (!overlayConfiguration) {
151 | return;
152 | }
153 |
154 | overlayWindow.setConfiguration(overlayConfiguration);
155 | });
156 | };
157 |
158 | /**
159 | * Store all overlay configurations
160 | */
161 | storeConfigurations() {
162 | logger.debug('storeConfigurations');
163 |
164 | const overlayWindowList = this.getAll();
165 |
166 | overlayWindowList.forEach((overlayWindow) => {
167 | storeOverlayConfiguration(overlayWindow.overlayId, overlayWindow.overlayConfiguration);
168 | });
169 | };
170 |
171 | /**
172 | * Remove all overlay windows
173 | * @param {Boolean} restart - relaunch app after removal
174 | */
175 | removeAll(restart) {
176 | logger.debug('removeAll');
177 |
178 | this.storeConfigurations();
179 |
180 | const overlayWindowList = this.getAll();
181 |
182 | overlayWindowList.forEach((overlayWindow, overlayWindowIndex, overlayWindowList) => {
183 | // Guard
184 | if (overlayWindow.isDestroyed()) {
185 | return;
186 | }
187 |
188 | /**
189 | * @listens OverlayWindow#closed
190 | */
191 | overlayWindow.on('closed', () => {
192 | logger.debug('overlayWindow#closed');
193 |
194 | // Remove reference
195 | overlayWindow = null;
196 | delete overlayWindowList[overlayWindowIndex];
197 |
198 | // Last window
199 | if (overlayWindowIndex === overlayWindowList.length - 1) {
200 | // Restart
201 | if (restart) {
202 | app.relaunch();
203 | app.exit();
204 | }
205 | }
206 | });
207 |
208 | overlayWindow.close();
209 | });
210 | };
211 |
212 | /**
213 | * Create all overlays
214 | */
215 | createAll() {
216 | logger.debug('createAll');
217 |
218 | electron.screen.getAllDisplays().forEach((electronDisplay) => {
219 | new OverlayWindow(electronDisplay);
220 | });
221 | };
222 |
223 | /**
224 | * Reset all overlays
225 | */
226 | resetAll() {
227 | logger.debug('resetAll');
228 |
229 | this.removeAll(true);
230 | };
231 | }
232 |
233 | /**
234 | * Init
235 | */
236 | let init = () => {
237 | logger.debug('init');
238 |
239 | // Ensure single instance
240 | if (!global.overlayManager) {
241 | global.overlayManager = new OverlayManager();
242 | }
243 | };
244 |
245 |
246 | /**
247 | * @listens Electron.App#on
248 | */
249 | app.on('ready', () => {
250 | logger.debug('app#ready');
251 |
252 | init();
253 | });
254 |
255 |
256 | /**
257 | * @exports
258 | */
259 | module.exports = global.overlayManager;
260 |
--------------------------------------------------------------------------------
/app/scripts/main/menus/tray-menu.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 |
4 | /**
5 | * Modules
6 | * Node
7 | * @constant
8 | */
9 | const path = require('path');
10 |
11 | /**
12 | * Modules
13 | * Electron
14 | * @constant
15 | */
16 | const electron = require('electron');
17 | const { Menu } = electron || electron.remote;
18 |
19 | /**
20 | * Modules
21 | * Configuration
22 | */
23 | const app = global.menubar.menubar.app;
24 |
25 | /**
26 | * Modules
27 | * External
28 | * @constant
29 | */
30 | const appRootPath = require('app-root-path')['path'];
31 |
32 | /**
33 | * Modules
34 | * Internal
35 | * @constant
36 | */
37 | const logger = require('@sidneys/logger')({ write: true });
38 | const platformTools = require('@sidneys/platform-tools');
39 |
40 |
41 | /**
42 | * Application
43 | * @constant
44 | */
45 | const appCurrentVersion = global.manifest.version;
46 | const appProductName = global.manifest.productName;
47 |
48 |
49 | /**
50 | * Get controller window
51 | * @return {Electron.BrowserWindow}
52 | */
53 | let getControllerWindow = () => global.menubar.menubar.window;
54 |
55 | /**
56 | * Get tray
57 | * @return {Electron.Tray}
58 | */
59 | let getTray = () => global.menubar.menubar.tray;
60 |
61 |
62 | /**
63 | * Tray, Dock Menu Template
64 | * @return {Electron.MenuItemConstructorOptions[]}
65 | */
66 | let createTrayMenuTemplate = () => {
67 | return [
68 | {
69 | id: 'appProductName',
70 | label: `Show ${appProductName}`,
71 | click() {
72 | getControllerWindow().show();
73 | }
74 | },
75 | {
76 | id: 'appCurrentVersion',
77 | label: `v${appCurrentVersion}`,
78 | type: 'normal',
79 | enabled: false
80 | },
81 | {
82 | type: 'separator'
83 | },
84 | {
85 | label: `Quit ${appProductName}`,
86 | click() {
87 | app.quit();
88 | }
89 | }
90 | ];
91 | };
92 |
93 | /**
94 | * @class TrayMenu
95 | * @property {Electron.MenuItemConstructorOptions[]} template - Template
96 | * @property {Electron.Tray} tray - Tray
97 | * @property {Electron.Menu} menu - Menu
98 | */
99 | class TrayMenu {
100 | /**
101 | * @param {Electron.MenuItemConstructorOptions[]} template - Menu template
102 | * @constructor
103 | */
104 | constructor(template) {
105 | logger.debug('constructor');
106 |
107 | this.template = template;
108 | this.tray = getTray();
109 | this.menu = Menu.buildFromTemplate(this.template);
110 |
111 | this.init();
112 | }
113 |
114 | /**
115 | * Init
116 | */
117 | init() {
118 | logger.debug('init');
119 |
120 | this.tray.setContextMenu(this.menu);
121 |
122 | /**
123 | * @listens Electron.Tray#click
124 | */
125 | this.tray.on('click', () => {
126 | logger.debug('TrayMenu#click');
127 |
128 | const controllerWindow = getControllerWindow();
129 |
130 | if (!controllerWindow) { return; }
131 |
132 | controllerWindow.isVisible() ? controllerWindow.hide() : controllerWindow.show();
133 | });
134 | }
135 | }
136 |
137 |
138 | /**
139 | * Init
140 | */
141 | let init = () => {
142 | logger.debug('init');
143 |
144 | // Ensure single instance
145 | if (!global.trayMenu) {
146 | global.trayMenu = new TrayMenu(createTrayMenuTemplate());
147 | }
148 | };
149 |
150 |
151 | /**
152 | * @listens Electron.App#Event:ready
153 | */
154 | app.once('ready', () => {
155 | logger.debug('app#ready');
156 |
157 | if (!platformTools.isLinux) { return; }
158 |
159 | init();
160 | });
161 |
162 |
163 | /**
164 | * @exports
165 | */
166 | module.exports = global.trayMenu;
167 |
--------------------------------------------------------------------------------
/app/scripts/main/providers/dialog-provider.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 |
4 | /**
5 | * Modules
6 | * Node
7 | * @constant
8 | */
9 | const fs = require('fs-extra');
10 | const os = require('os');
11 | const path = require('path');
12 |
13 | /**
14 | * Modules
15 | * Electron
16 | * @constant
17 | */
18 | const { app } = require('menubar');
19 | const electron = require('electron');
20 | const { BrowserWindow, dialog } = electron || electron.remote;
21 |
22 | /**
23 | * Modules
24 | * External
25 | * @constant
26 | */
27 | const logger = require('@sidneys/logger')({ write: true });
28 | const platformTools = require('@sidneys/platform-tools');
29 |
30 |
31 | /**
32 | * Application
33 | * @constant
34 | * @default
35 | */
36 | const appProductName = global.manifest.productName;
37 |
38 |
39 | /**
40 | * Wrapper for dialog.showMessageBox
41 | * @param {String} title - Title
42 | * @param {String} message - Message
43 | * @param {Array} buttonList - Buttons
44 | * @param {String} type - Type
45 | * @param {function(*)} callback - Callback
46 | *
47 | * @private
48 | */
49 | let showMessage = (title = appProductName, message = title, buttonList = ['OK'], type = 'info', callback = () => {}) => {
50 | logger.debug('showMessage');
51 |
52 | logger.debug('showMessage', 'title', title, 'message', message, 'buttonList', buttonList, 'type', type);
53 |
54 | dialog.showMessageBox(BrowserWindow.getFocusedWindow(), {
55 | type: type,
56 | title: title,
57 | message: title,
58 | detail: message,
59 | buttons: buttonList,
60 | defaultId: 0
61 | }, (response) => {
62 | logger.debug('showMessage', `title: '${title}' message: '${message}' response: '${response} (${buttonList[response]})'`);
63 | callback(response);
64 | });
65 | };
66 |
67 |
68 | /** @namespace detectedType.ext */
69 | /** @namespace detectedType.mime */
70 | /** @namespace fs.copy */
71 | /** @namespace fs.exists */
72 | /** @namespace fs.stat */
73 |
74 | /**
75 | * Validate file exists
76 | * @param {String|String[]} filePath - Path
77 | * @param {function(*)} callback - Callback
78 | *
79 | * @private
80 | */
81 | let validateFile = (filePath, callback = () => {}) => {
82 | logger.debug('validateFile');
83 |
84 | filePath = path.normalize(filePath.toString());
85 |
86 | fs.stat(filePath, (error) => {
87 | if (error) {
88 | callback(error);
89 | return;
90 | }
91 |
92 | callback(null, filePath);
93 | });
94 | };
95 |
96 |
97 | /**
98 | * File Dialog
99 | * @param {String=} dialogTitle - Title
100 | * @param {Array=} extensionList - Accepted file extensions
101 | * @param {String=} initialFolder - Initial lookup folder
102 | * @param {function(*)} callback - Callback
103 | *
104 | * @private
105 | */
106 | let file = (dialogTitle = appProductName, extensionList = ['*'], initialFolder = app.getPath(name), callback = () => {}) => {
107 | logger.debug('file');
108 |
109 | dialog.showOpenDialog({
110 | title: dialogTitle,
111 | properties: ['openFile', 'showHiddenFiles', 'treatPackageAsDirectory'],
112 | defaultPath: initialFolder,
113 | filters: [{ name: 'Filter', extensions: extensionList }]
114 | }, (filePath) => {
115 |
116 | if (!filePath) {
117 | return callback(new Error(`filepath required`));
118 | }
119 |
120 | validateFile(filePath, (error, filePathVerified) => {
121 | if (error) {
122 | showMessage(`File not found.${os.EOL}`, ['Dismiss'], 'warning', (error) => {
123 | callback(error);
124 | });
125 | return;
126 | }
127 |
128 | callback(null, filePathVerified);
129 | });
130 | });
131 | };
132 |
133 |
134 | /**
135 | * Error Dialog
136 | * @param {String=} message - Message
137 | * @param {function(*)} callback - Callback
138 | *
139 | * @public
140 | */
141 | let error = (message, callback = () => {}) => {
142 | logger.debug('error');
143 |
144 | if (platformTools.isMacOS) {
145 | app.dock.bounce('critical');
146 | }
147 |
148 | app.focus();
149 |
150 | showMessage('Error', message, ['Dismiss', 'Quit', 'Reload'], 'error', (response, checkboxChecked) => {
151 | if (response === 1) {
152 | app.quit();
153 | return;
154 | } else if (response === 2) {
155 | BrowserWindow.getFocusedWindow().reload();
156 | return;
157 | }
158 |
159 | callback(response, checkboxChecked);
160 | });
161 | };
162 |
163 | /**
164 | * Info Dialog
165 | * @param {String=} title - Title
166 | * @param {String=} message - Message
167 | * @param {function(*)} callback - Callback
168 | *
169 | * @public
170 | */
171 | let info = (title, message, callback = () => {}) => {
172 | logger.debug('info');
173 |
174 | showMessage(title, message, ['Dismiss'], 'info', callback);
175 | };
176 |
177 | /**
178 | * Question Show
179 | * @param {String=} title - Title
180 | * @param {String=} message - Message
181 | * @param {function(*)} callback - Callback
182 | *
183 | * @public
184 | */
185 | let question = (title, message, callback = () => {}) => {
186 | logger.debug('question');
187 |
188 | app.focus();
189 |
190 | showMessage(title, message, ['Yes', 'No'], 'question', callback);
191 | };
192 |
193 |
194 | /**
195 | * @exports
196 | */
197 | module.exports = {
198 | error: error,
199 | file: file,
200 | info: info,
201 | question: question
202 | };
203 |
--------------------------------------------------------------------------------
/app/scripts/main/services/debug-service.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 |
4 | /**
5 | * Modules
6 | * Electron
7 | * @constant
8 | */
9 | const electron = require('electron');
10 | const { webContents } = electron || electron.remote;
11 |
12 | /**
13 | * Modules
14 | * Configuration
15 | */
16 | const app = global.menubar.menubar.app;
17 |
18 | /**
19 | * Modules
20 | * External
21 | * @constant
22 | */
23 | const isDebug = require('@sidneys/is-env')('debug');
24 | const isLivereload = require('@sidneys/is-env')('livereload');
25 | const logger = require('@sidneys/logger')({ write: true });
26 | /* eslint-disable no-unused-vars */
27 | const filesize = require('filesize');
28 | const tryRequire = require('try-require');
29 | /* eslint-enable */
30 |
31 |
32 | /**
33 | * @constant
34 | * @default
35 | */
36 | const defaultTimeout = 5000;
37 |
38 | /**
39 | * Init
40 | */
41 | let init = () => {
42 | logger.debug('init');
43 |
44 | let timeout = setTimeout(() => {
45 | webContents.getAllWebContents().forEach((contents) => {
46 | /**
47 | * Open Developer Tools
48 | */
49 | if (isDebug) {
50 | logger.info('opening developer tools:', `"${contents.getURL()}"`);
51 |
52 | contents.openDevTools();
53 | }
54 |
55 | /**
56 | * Start Live Reload
57 | */
58 | if (isLivereload) {
59 | logger.info('starting live reload:', `"${contents.getURL()}"`);
60 |
61 | tryRequire('electron-connect')['client'].create();
62 | }
63 | });
64 | clearTimeout(timeout);
65 | }, defaultTimeout);
66 | };
67 |
68 |
69 | /**
70 | * @listens Electron.App#Event:ready
71 | */
72 | app.once('ready', () => {
73 | logger.debug('app#ready');
74 |
75 | init();
76 | });
77 |
--------------------------------------------------------------------------------
/app/scripts/main/services/power-service.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 |
4 | /**
5 | * Modules
6 | * Electron
7 | * @constant
8 | */
9 | const { app } = require('menubar');
10 | const electron = require('electron');
11 | const { powerMonitor } = electron || electron.remote;
12 |
13 | /**
14 | * Modules
15 | * External
16 | * @constant
17 | */
18 | const logger = require('@sidneys/logger')({ write: true });
19 |
20 |
21 | /**
22 | * @constant
23 | * @default
24 | */
25 | const defaultTimeout = 5000;
26 |
27 |
28 | /**
29 | * Init
30 | */
31 | let init = () => {
32 | logger.debug('init');
33 |
34 | /**
35 | * @listens Electron.powerMonitor#suspend
36 | */
37 | powerMonitor.on('suspend', () => {
38 | logger.debug('electron.powerMonitor#suspend');
39 | });
40 |
41 | /**
42 | * @listens Electron.powerMonitor#resume
43 | */
44 | powerMonitor.on('resume', () => {
45 | logger.debug('electron.powerMonitor#resume');
46 |
47 | let timeout = setTimeout(() => {
48 | logger.debug('electron.powerMonitor#resume', 'relaunching app');
49 |
50 | clearTimeout(timeout);
51 |
52 | app.relaunch();
53 | app.exit();
54 | }, defaultTimeout);
55 | });
56 | };
57 |
58 |
59 | /**
60 | * @listens Electron.App#ready
61 | */
62 | app.once('ready', () => {
63 | logger.debug('app#ready');
64 |
65 | init();
66 | });
67 |
--------------------------------------------------------------------------------
/app/scripts/main/services/updater-service.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 |
4 | /**
5 | * Modules
6 | * Node
7 | * @constant
8 | */
9 | const os = require('os');
10 | const path = require('path');
11 |
12 | /**
13 | * Modules
14 | * Electron
15 | * @constant
16 | */
17 | const electron = require('electron');
18 | const { app, BrowserWindow } = electron;
19 |
20 | /**
21 | * Modules
22 | * External
23 | * @constant
24 | */
25 | const appRootPath = require('app-root-path')['path'];
26 | const removeMarkdown = require('remove-markdown');
27 | const semverCompare = require('semver-compare');
28 | const logger = require('@sidneys/logger')({ write: true });
29 | const platformTools = require('@sidneys/platform-tools');
30 | const { autoUpdater } = require('electron-updater');
31 |
32 | /**
33 | * Modules
34 | * Internal
35 | * @constant
36 | */
37 | const dialogProvider = require(path.join(appRootPath, 'app', 'scripts', 'main', 'providers', 'dialog-provider'));
38 | //const notificationProvider = require(path.join(appRootPath, 'app', 'scripts', 'main', 'providers', 'notification-provider'));
39 | const configurationManager = require(path.join(appRootPath, 'app', 'scripts', 'main', 'managers', 'configuration-manager'));
40 |
41 | /**
42 | * Application
43 | * @constant
44 | * @default
45 | */
46 | const appProductName = global.manifest.productName;
47 | const appCurrentVersion = global.manifest.version;
48 |
49 |
50 | /**
51 | * Get mainWindow
52 | * @return {Electron.BrowserWindow}
53 | */
54 | let getMainWindow = () => global.mainWindow;
55 |
56 |
57 | /**
58 | * Retrieve appAutoUpdate
59 | * @return {Boolean} - Yes / no
60 | */
61 | let retrieveAppAutoUpdate = () => configurationManager('appAutoUpdate').get();
62 |
63 | /**
64 | * Retrieve appChangelog
65 | * @return {String} - changelog
66 | */
67 | let retrieveAppChangelog = () => configurationManager('appChangelog').get();
68 |
69 | /**
70 | * Store appChangelog
71 | * @param {String} changelog - Changelog
72 | * @return {void}
73 | */
74 | let storeAppChangelog = (changelog) => configurationManager('appChangelog').set(changelog);
75 |
76 | /**
77 | * Retrieve appLastVersion
78 | * @return {String} - Version
79 | */
80 | let retrieveAppLastVersion = () => configurationManager('appLastVersion').get();
81 |
82 | /**
83 | * Store appLastVersion
84 | * @param {String} version - Version
85 | * @return {void}
86 | */
87 | let storeAppLastVersion = (version) => configurationManager('appLastVersion').set(version);
88 |
89 |
90 | /**
91 | * @class UpdaterService
92 | * @property {AppUpdater} autoUpdater - Auto updater instance
93 | * @property {Boolean} isUpdating - App is currently updating
94 | */
95 | class UpdaterService {
96 | /**
97 | * @constructor
98 | */
99 | constructor() {
100 | logger.debug('constructor');
101 |
102 | // Do not run with debug Electron application
103 | if (process.defaultApp) { return; }
104 |
105 | // Do not run on Linux
106 | if (platformTools.isLinux) { return; }
107 |
108 | this.autoUpdater = autoUpdater;
109 | this.isUpdating = false;
110 |
111 | this.init();
112 | }
113 |
114 | /**
115 | * Init
116 | */
117 | init() {
118 | logger.debug('init');
119 |
120 | this.bumpAppLastVersion();
121 | this.registerAutoUpdater();
122 | }
123 |
124 | /**
125 | * Register Electron.AutoUpdater Singleton
126 | */
127 | registerAutoUpdater() {
128 | logger.debug('registerAutoUpdater');
129 |
130 | /**
131 | * Add Logger
132 | */
133 | this.autoUpdater.logger = logger;
134 |
135 | /**
136 | * @listens Electron.AutoUpdater#error
137 | */
138 | this.autoUpdater.on('error', (error) => {
139 | logger.error('autoUpdater#error', error.message);
140 |
141 | this.isUpdating = false;
142 | });
143 |
144 | /**
145 | * @listens Electron.AutoUpdater#checking-for-update
146 | */
147 | this.autoUpdater.on('checking-for-update', () => {
148 | logger.debug('UpdaterService#checking-for-update');
149 |
150 | this.isUpdating = true;
151 | });
152 |
153 | /**
154 | * @listens Electron.AutoUpdater#update-available
155 | */
156 | this.autoUpdater.on('update-available', (info) => {
157 | logger.debug('UpdaterService#update-available', info);
158 |
159 | this.isUpdating = true;
160 |
161 | //const notification = notificationProvider.create({ title: `Update available for ${appProductName}`, subtitle: info.version });
162 | //notification.show();
163 | });
164 |
165 | /**
166 | * @listens Electron.AutoUpdater#update-not-available
167 | */
168 | this.autoUpdater.on('update-not-available', (info) => {
169 | logger.debug('UpdaterService#update-not-available', info);
170 |
171 | this.isUpdating = false;
172 | });
173 |
174 | /**
175 | * @listens Electron.AutoUpdater#download-progress
176 | */
177 | this.autoUpdater.on('download-progress', (progress) => {
178 | logger.debug('UpdaterService#download-progress');
179 |
180 | logger.info('application update', 'progress', `${progress['percent'].toFixed(2)}%`);
181 |
182 | /**
183 | * Show update progress (Windows)
184 | */
185 | if (platformTools.isWindows) {
186 | const mainWindow = getMainWindow();
187 | if (!mainWindow) { return; }
188 |
189 | mainWindow.setProgressBar(progress['percent'] / 100);
190 | }
191 | });
192 |
193 | /**
194 | * @listens Electron.AutoUpdater#update-downloaded
195 | */
196 | this.autoUpdater.on('update-downloaded', (info) => {
197 | logger.info('UpdaterService#update-downloaded', info);
198 |
199 | this.isUpdating = true;
200 |
201 | //const notification = notificationProvider.create({ title: `Update ready to install for ${appProductName}`, subtitle: info.version });
202 | //notification.show();
203 |
204 | if (!!info.releaseNotes) {
205 | const releaseNotesPlaintext = removeMarkdown(info.releaseNotes);
206 |
207 | logger.info('UpdaterService#update-downloaded', 'releaseNotesPlaintext', releaseNotesPlaintext);
208 |
209 | storeAppChangelog(releaseNotesPlaintext);
210 | }
211 |
212 | dialogProvider.question(
213 | `Update successfully installed`,
214 | `${appProductName} has been updated successfully.${os.EOL}${os.EOL}` +
215 | `To apply the changes and complete the updating process, the app needs to be restarted.${os.EOL}${os.EOL}` +
216 | `Restart now?`, (response) => {
217 | if (response === 0) {
218 | BrowserWindow.getAllWindows().forEach((browserWindow) => browserWindow.destroy());
219 | this.autoUpdater.quitAndInstall();
220 | }
221 | if (response === 1) { return true; }
222 |
223 | return true;
224 | });
225 | });
226 |
227 | this.checkForUpdates();
228 | }
229 |
230 | /**
231 | * Bump last app version
232 | * @static
233 | */
234 | bumpAppLastVersion() {
235 | const appLastVersion = retrieveAppLastVersion();
236 |
237 | // Initialize version
238 | if (!appLastVersion) {
239 | storeAppLastVersion(appCurrentVersion);
240 |
241 | return;
242 | }
243 |
244 | // Compare internal/current version
245 | let wasUpdated = Boolean(semverCompare(appCurrentVersion, appLastVersion) === 1);
246 |
247 | // Update internal version
248 | if (wasUpdated) {
249 | storeAppLastVersion(appCurrentVersion);
250 |
251 | const changelogPlaintext = retrieveAppChangelog();
252 |
253 | logger.debug('bumpAppLastVersion', 'changelogPlaintext', changelogPlaintext);
254 |
255 | if (!!changelogPlaintext) {
256 | dialogProvider.info(`${appProductName} has been updated to ${appCurrentVersion}`, changelogPlaintext);
257 | } else {
258 | dialogProvider.info(`${appProductName} has been updated to ${appCurrentVersion}`, `Update completed successfully.`);
259 | }
260 |
261 | //const notification = notificationProvider.create({ title: `Update installed for ${appProductName}`, subtitle: appCurrentVersion });
262 | //notification.show();
263 | }
264 | }
265 |
266 | /**
267 | * Check if updater is ready
268 | * @return {Boolean} - Yes / no
269 | */
270 | isAvailable() {
271 | logger.debug('isAvailable');
272 |
273 | return Boolean(this.autoUpdater);
274 | }
275 |
276 | /**
277 | * Check if currently updating
278 | * @return {Boolean} - Yes / no
279 | */
280 | isActive() {
281 | logger.debug('isActive');
282 |
283 | return Boolean(this.isUpdating);
284 | }
285 |
286 | /**
287 | * Check for updates
288 | */
289 | checkForUpdates() {
290 | logger.debug('checkForUpdates');
291 |
292 | if (!this.isAvailable()) { return; }
293 | if (this.isActive()) { return; }
294 |
295 | if (!retrieveAppAutoUpdate()) { return; }
296 |
297 | this.autoUpdater.checkForUpdates();
298 | }
299 | }
300 |
301 |
302 | /**
303 | * Init
304 | */
305 | let init = () => {
306 | logger.debug('init');
307 |
308 | // Ensure single instance
309 | if (!global.updaterService) {
310 | global.updaterService = new UpdaterService();
311 | }
312 | };
313 |
314 |
315 | /**
316 | * @listens Electron.App#browser-window-focus
317 | */
318 | app.on('browser-window-focus', () => {
319 | logger.debug('app#browser-window-focus');
320 |
321 | if (!global.updaterService) { return; }
322 |
323 | global.updaterService.checkForUpdates();
324 | });
325 |
326 | /**
327 | * @listens Electron.App#Event:ready
328 | */
329 | app.once('ready', () => {
330 | logger.debug('app#ready');
331 |
332 | init();
333 | });
334 |
335 |
336 | /**
337 | * @exports
338 | */
339 | module.exports = global.updaterService;
340 |
--------------------------------------------------------------------------------
/app/scripts/main/windows/overlay-configuration.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 |
4 | /**
5 | * Modules
6 | * External
7 | * @constant
8 | */
9 | const logger = require('@sidneys/logger')({ write: true });
10 |
11 |
12 | /**
13 | * @typedef {String} OverlayAlpha - Floating-point alpha value
14 | * @example '0.23'
15 | */
16 |
17 | /**
18 | * @typedef {String} OverlayColor - Overlay color hexadecimal
19 | * @example '#404040'
20 | */
21 |
22 | /**
23 | * @typedef {Boolean} OverlayVisibility - Overlay visibility
24 | * @example true
25 | */
26 |
27 |
28 | /**
29 | * @type {OverlayAlpha}
30 | * @const
31 | * @default
32 | */
33 | const defaultAlpha = '0.0';
34 |
35 | /**
36 | * @type {OverlayColor}
37 | * @const
38 | * @default
39 | */
40 | const defaultColor = '#000000';
41 |
42 | /**
43 | * @type {OverlayVisibility}
44 | * @const
45 | * @default
46 | */
47 | const defaultVisibility = true;
48 |
49 |
50 | /**
51 | * @class OverlayConfiguration
52 | * @property {OverlayAlpha} alpha
53 | * @property {OverlayColor} color
54 | * @property {OverlayVisibility} visibility
55 | */
56 | class OverlayConfiguration {
57 | /**
58 | * @param {OverlayAlpha=} alpha - Overlay alpha
59 | * @param {OverlayColor=} color - Overlay color
60 | * @param {OverlayVisibility=} visibility - Overlay visibility
61 | * @constructor
62 | */
63 | constructor(alpha = defaultAlpha, color = defaultColor, visibility = defaultVisibility) {
64 | logger.debug('constructor');
65 |
66 | this.alpha = alpha;
67 | this.color = color;
68 | this.visibility = visibility;
69 | }
70 | }
71 |
72 |
73 | /**
74 | * @exports
75 | */
76 | module.exports = OverlayConfiguration;
77 |
--------------------------------------------------------------------------------
/app/scripts/main/windows/overlay-window.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 |
4 | /**
5 | * Modules
6 | * Node
7 | * @constant
8 | */
9 | const path = require('path');
10 | const url = require('url');
11 |
12 | /**
13 | * Modules
14 | * Electron
15 | * @constant
16 | */
17 | const electron = require('electron');
18 | const { BrowserWindow } = electron || electron.remote;
19 |
20 | /**
21 | * Modules
22 | * External
23 | * @constant
24 | */
25 | const _ = require('lodash');
26 | const appRootPath = require('app-root-path').path;
27 | const isDebug = require('@sidneys/is-env')('debug');
28 | const logger = require('@sidneys/logger')({ write: true });
29 | const platformTools = require('@sidneys/platform-tools');
30 |
31 |
32 | /**
33 | * Modules
34 | * Internal
35 | * @constant
36 | */
37 | const OverlayConfiguration = require(path.join(appRootPath, 'app', 'scripts', 'main', 'windows', 'overlay-configuration'));
38 |
39 |
40 | /**
41 | * Tray icons
42 | * @constant
43 | */
44 | const trayIconDefault = path.join(appRootPath, 'app', 'images', `${platformTools.type}-tray-icon-default${platformTools.templateImageExtension(platformTools.type)}`);
45 | const trayIconTranslucent = path.join(appRootPath, 'app', 'images', `${platformTools.type}-tray-icon-translucent${platformTools.templateImageExtension(platformTools.type)}`);
46 |
47 | /**
48 | * Filesystem
49 | * @constant
50 | * @default
51 | */
52 | const windowHtml = path.join(appRootPath, 'app', 'html', 'overlay.html');
53 |
54 | /**
55 | * Application
56 | * @constant
57 | * @default
58 | */
59 | const windowUrl = url.format({ protocol: 'file:', pathname: windowHtml });
60 |
61 |
62 | /**
63 | * Get overlayManager
64 | * @return {OverlayManager}
65 | */
66 | let getOverlayManager = () => global.overlayManager;
67 |
68 | /**
69 | * Get TrayMenu
70 | * @return {TrayMenu}
71 | */
72 | let getTrayMenu = () => global.menubar.menubar.tray;
73 |
74 |
75 |
76 | /**
77 | * @typedef {Electron.Display} OverlayDisplay - Target display
78 | */
79 |
80 | /**
81 | * @typedef {Electron.Display.id} OverlayId - Unique identifier
82 | */
83 |
84 |
85 | /**
86 | * @class OverlayWindow
87 | * @property {OverlayDisplay} overlayDisplay
88 | * @property {OverlayId} overlayId
89 | * @property {OverlayConfiguration} overlayConfiguration
90 | * @extends Electron.BrowserWindow
91 | * @namespace Electron
92 | */
93 | class OverlayWindow extends BrowserWindow {
94 |
95 | /**
96 | * @param {Electron.Display} display - Display
97 | * @constructor
98 | */
99 | constructor(display) {
100 | logger.debug('constructor');
101 |
102 | super({
103 | enableLargerThanScreen: true,
104 | frame: false,
105 | focusable: false,
106 | hasShadow: false,
107 | height: 1,
108 | show: false,
109 | transparent: true,
110 | width: 1,
111 | x: 0,
112 | y: 0
113 | });
114 |
115 | // BrowserWindow
116 | this.setIgnoreMouseEvents(true);
117 |
118 | if (isDebug) {
119 | this.setAlwaysOnTop(false);
120 | } else {
121 | this.setAlwaysOnTop(true, 'screen-saver', 2);
122 | }
123 |
124 | // Display assignment
125 | this.overlayDisplay = display;
126 | this.overlayId = display.id;
127 |
128 | // Initial configuration
129 | this.overlayConfiguration = new OverlayConfiguration();
130 |
131 | this.init();
132 | }
133 |
134 | /**
135 | * Init
136 | */
137 | init() {
138 | logger.debug('init');
139 |
140 | /**
141 | * @listens OverlayWindow.WebContents#dom-ready
142 | */
143 | this.webContents.on('dom-ready', () => {
144 | logger.debug('OverlayWindow.WebContents#dom-ready');
145 |
146 | // Apply configuration
147 | this.applyConfiguration();
148 |
149 | this.show();
150 | });
151 |
152 | /**
153 | * @listens OverlayWindow#dom-ready
154 | */
155 | this.on('update-overlay', () => {
156 | logger.debug('overlay:update');
157 |
158 | const overlayManager = getOverlayManager();
159 | const isVisible = overlayManager.isVisible();
160 |
161 | isVisible ? getTrayMenu().setImage(trayIconDefault) : getTrayMenu().setImage(trayIconTranslucent);
162 | });
163 |
164 | this.loadURL(windowUrl);
165 | }
166 |
167 | /**
168 | * @fires OverlayWindow#Event:update-overlay
169 | */
170 | updateRenderer() {
171 | logger.debug('updateRenderer');
172 |
173 | this.emit('update-overlay', this.overlayId, this.overlayConfiguration);
174 | this.webContents.send('update-overlay', this.overlayConfiguration);
175 | }
176 |
177 | /**
178 | * applyAlpha
179 | *
180 | * @private
181 | */
182 | applyAlpha() {
183 | logger.debug('applyAlpha');
184 |
185 | let debounced = _.debounce(() => this.updateRenderer(), 150);
186 | debounced();
187 | }
188 |
189 | /**
190 | * applyColor
191 | *
192 | * @private
193 | */
194 | applyColor() {
195 | logger.debug('applyColor');
196 |
197 | let debounced = _.debounce(() => this.updateRenderer(), 150);
198 | debounced();
199 | }
200 |
201 | /**
202 | * applyVisibility
203 | *
204 | * @private
205 | */
206 | applyVisibility() {
207 | logger.debug('applyVisibility');
208 |
209 | const bounds = this.overlayDisplay.bounds;
210 |
211 | if (this.overlayConfiguration.visibility) {
212 | this.setBounds({
213 | x: bounds.x,
214 | y: bounds.y,
215 | width: isDebug ? Math.floor(bounds.width / 4) : (bounds.width + 1),
216 | height: isDebug ? Math.floor(bounds.height / 4) : (bounds.height + 1)
217 | }, false);
218 | } else {
219 | this.setBounds({ x: 0, y: 0, width: 1, height: 1 }, false);
220 | }
221 | }
222 |
223 | /**
224 | * Apply configuration
225 | *
226 | * @private
227 | */
228 | applyConfiguration() {
229 | logger.debug('applyConfiguration');
230 |
231 | this.applyAlpha();
232 | this.applyColor();
233 | this.applyVisibility();
234 | }
235 |
236 | /**
237 | * Set configuration
238 | * @param {OverlayConfiguration|Object} configuration - Overlay configuration
239 | *
240 | * @public
241 | */
242 | setConfiguration(configuration) {
243 | logger.debug('setConfiguration');
244 |
245 | this.overlayConfiguration = Object.assign({}, this.overlayConfiguration, configuration);
246 | this.applyConfiguration();
247 | }
248 | }
249 |
250 | /**
251 | * @exports
252 | */
253 | module.exports = OverlayWindow;
254 |
--------------------------------------------------------------------------------
/app/scripts/main/windows/preferences-window.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | /**
4 | * Modules
5 | * Node
6 | * @constant
7 | */
8 | const path = require('path');
9 | const url = require('url');
10 |
11 | /**
12 | * Modules
13 | * Electron
14 | * @constant
15 | */
16 | const electron = require('electron');
17 | const { BrowserWindow } = electron || electron.remote;
18 |
19 | /**
20 | * Modules
21 | * Configuration
22 | */
23 | const app = global.menubar.menubar.app;
24 |
25 |
26 | /**
27 | * Modules
28 | * External
29 | * @constant
30 | */
31 | const appRootPath = require('app-root-path').path;
32 | const logger = require('@sidneys/logger')({ write: true });
33 |
34 |
35 | /**
36 | * Filesystem
37 | * @constant
38 | * @default
39 | */
40 | const windowHtml = path.join(appRootPath, 'app', 'html', 'preferences.html');
41 |
42 | /**
43 | * Application
44 | * @constant
45 | * @default
46 | */
47 | const windowTitle = global.manifest.productName;
48 | const windowUrl = url.format({ protocol: 'file:', pathname: windowHtml });
49 |
50 |
51 | /**
52 | * @class PreferencesWindow
53 | * @extends Electron.BrowserWindow
54 | * @namespace Electron
55 | */
56 | class PreferencesWindow extends BrowserWindow {
57 | constructor() {
58 | super({
59 | acceptFirstMouse: true,
60 | alwaysOnTop: false,
61 | autoHideMenuBar: true,
62 | fullscreenable: false,
63 | maximizable: false,
64 | minimizable: false,
65 | height: 128,
66 | minHeight: 128,
67 | maxHeight: 256,
68 | resizable: false,
69 | show: false,
70 | title: `${windowTitle} Preferences`,
71 | type: 'textured',
72 | width: 320,
73 | minWidth: 320,
74 | maxWidth: 640
75 | });
76 |
77 | this.init();
78 | }
79 |
80 | /**
81 | * Init
82 | */
83 | init() {
84 | logger.debug('init');
85 |
86 | /**
87 | * @listens PreferencesWindow#close
88 | */
89 | this.on('close', (event) => {
90 | logger.debug('AppWindow#close');
91 |
92 | if (global.state.isQuitting === false) {
93 | event.preventDefault();
94 | this.hide();
95 | }
96 | });
97 |
98 |
99 | this.loadURL(windowUrl);
100 | }
101 | }
102 |
103 |
104 | /**
105 | * Init
106 | */
107 | let init = () => {
108 | logger.debug('init');
109 |
110 | // Ensure single instance
111 | if (!global.preferencesWindow) {
112 | global.preferencesWindow = new PreferencesWindow();
113 | }
114 | };
115 |
116 |
117 | /**
118 | * @listens Electron.App#on
119 | */
120 | app.once('ready', () => {
121 | logger.debug('app#ready');
122 |
123 | init();
124 | });
125 |
126 |
127 | /**
128 | * @exports
129 | */
130 | module.exports = global.preferencesWindow;
131 |
--------------------------------------------------------------------------------
/app/scripts/renderer/controller.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 |
4 | /**
5 | * Modules
6 | * Electron
7 | * @constant
8 | */
9 | const electron = require('electron');
10 | const { ipcRenderer, remote } = electron;
11 |
12 | /**
13 | * Modules
14 | * External
15 | * @constant
16 | */
17 | const domTools = require('@sidneys/dom-tools');
18 | const logger = require('@sidneys/logger')({ write: true });
19 |
20 |
21 | /**
22 | * Get controller window
23 | * @return {Electron.BrowserWindow}
24 | */
25 | let getControllerWindow = () => remote.getGlobal('menubar').menubar.window;
26 |
27 | /**
28 | * Get overlayManager
29 | * @return {OverlayManager}
30 | */
31 | let getOverlayManager = () => remote.getGlobal('overlayManager');
32 |
33 | /**
34 | * Get preferencesWindow
35 | * @return {PreferencesWindow}
36 | */
37 | let getPreferencesWindow = () => remote.getGlobal('preferencesWindow');
38 |
39 |
40 | /**
41 | * DOM Elements
42 | * @global
43 | */
44 | const contentElement = document.querySelector('.content');
45 | const controllerListElement = document.querySelector('.display-control-list');
46 | const exitButtonElement = document.querySelector('.window-controls .exit');
47 | /* eslint-disable no-unused-vars */
48 | const preferencesButtonElement = document.querySelector('.window-controls .settings');
49 | /* eslint-enable */
50 |
51 |
52 | /**
53 | * Get percentage string for fraction
54 | * @param {OverlayAlpha} alpha - Fraction
55 | * @return {String} percentage
56 | */
57 | let getPercentage = (alpha) => `${Math.floor(100 * parseFloat(alpha))} %`;
58 |
59 | /**
60 | * Pass content size changes to native wrapper window
61 | */
62 | let handleSizeChanges = () => {
63 | logger.debug('handleSizeChanges');
64 |
65 | const controllerWindow = getControllerWindow();
66 |
67 | let currentWidth = controllerWindow.getSize()[0];
68 | let currentHeight = controllerWindow.getSize()[1];
69 | let contentHeight = contentElement.getBoundingClientRect().height;
70 |
71 | if (contentHeight !== currentHeight) {
72 | controllerWindow.setSize(currentWidth, contentHeight);
73 | }
74 | };
75 |
76 | /**
77 | * Register Controllers
78 | */
79 | let registerControllers = () => {
80 | logger.debug('registerControllers');
81 |
82 | const overlayManager = getOverlayManager();
83 | const overlayWindowList = overlayManager.getAll();
84 |
85 | while (controllerListElement.firstChild) {
86 | controllerListElement.removeChild(controllerListElement.firstChild);
87 | }
88 |
89 | overlayWindowList.forEach((overlayWindow) => {
90 | const id = overlayWindow.overlayId;
91 |
92 | let displayControlElement = document.createElement('form');
93 | displayControlElement.classList.add('display-control-list__display-control');
94 | displayControlElement.dataset.displayId = id;
95 | controllerListElement.appendChild(displayControlElement);
96 |
97 | /**
98 | * Visibility
99 | */
100 | const visibility = overlayWindow.overlayConfiguration.visibility;
101 |
102 | const visibilityInputElement = document.createElement('input');
103 | visibilityInputElement.classList.add('visibility');
104 | visibilityInputElement.type = 'checkbox';
105 | visibilityInputElement.checked = visibility;
106 | displayControlElement.appendChild(visibilityInputElement);
107 |
108 | /**
109 | * @listens visibilityInputElement:MouseEvent#click
110 | */
111 | visibilityInputElement.addEventListener('click', () => {
112 | logger.debug('visibilityInputElement#click ');
113 |
114 | overlayWindow.setConfiguration({ visibility: visibilityInputElement.checked });
115 | });
116 |
117 | overlayWindow.setConfiguration({ visibility: visibility });
118 |
119 | /**
120 | * Alpha
121 | */
122 | const alpha = overlayWindow.overlayConfiguration.alpha;
123 |
124 | const alphaOutputElement = document.createElement('output');
125 | alphaOutputElement.classList.add('alpha');
126 | alphaOutputElement.value = getPercentage(alpha);
127 | displayControlElement.appendChild(alphaOutputElement);
128 |
129 | const alphaInputElement = document.createElement('input');
130 | alphaInputElement.classList.add('alpha');
131 | alphaInputElement.type = 'range';
132 | alphaInputElement.max = '0.92';
133 | alphaInputElement.min = '0.01';
134 | alphaInputElement.step = '0.01';
135 | alphaInputElement.value = alpha;
136 | displayControlElement.appendChild(alphaInputElement);
137 |
138 | /**
139 | * @listens alphaInputElement:MouseEvent#click
140 | */
141 | alphaInputElement.addEventListener('input', () => {
142 | logger.debug('alphaInputElement#input');
143 |
144 | alphaOutputElement.value = getPercentage(alphaInputElement.value);
145 | overlayWindow.setConfiguration({ alpha: alphaInputElement.value });
146 | });
147 |
148 | overlayWindow.setConfiguration({ alpha: alpha });
149 | });
150 |
151 | handleSizeChanges();
152 | controllerListElement.style.opacity = '1';
153 | };
154 |
155 |
156 | /**
157 | * @listens ipcRenderer#controller-show
158 | */
159 | ipcRenderer.on('controller-show', () => {
160 | logger.debug('ipcRenderer#controller-show');
161 |
162 | registerControllers();
163 | });
164 |
165 |
166 | /**
167 | * @listens exitButtonElement:MouseEvent#click
168 | */
169 | exitButtonElement.addEventListener('click', () => {
170 | logger.debug('exitButtonElement#click');
171 |
172 | remote.app.quit();
173 | });
174 |
175 | /**
176 | * @listens preferencesButtonElement:MouseEvent#click
177 | */
178 | preferencesButtonElement.addEventListener('click', () => {
179 | logger.debug('preferencesButtonElement#click');
180 |
181 | const preferencesWindow = getPreferencesWindow();
182 |
183 | preferencesWindow.isVisible() ? preferencesWindow.hide() : preferencesWindow.show();
184 | });
185 |
186 | /**
187 | * @listens document:DOMContentLoaded
188 | */
189 | document.addEventListener('DOMContentLoaded', () => {
190 | logger.debug('document#DOMContentLoaded');
191 |
192 | // Add platform name to
193 | domTools.addPlatformClass();
194 |
195 | // Add slider controls
196 | registerControllers();
197 | });
198 |
--------------------------------------------------------------------------------
/app/scripts/renderer/overlay.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 |
4 | /**
5 | * Modules
6 | * Electron
7 | * @constant
8 | */
9 | const electron = require('electron');
10 | const { ipcRenderer } = electron;
11 |
12 | /**
13 | * Modules
14 | * External
15 | * @constant
16 | */
17 | const tinycolor = require('tinycolor2');
18 |
19 | /**
20 | * Modules
21 | * Internal
22 | * @constant
23 | */
24 | const logger = require('@sidneys/logger')({ write: true });
25 |
26 |
27 | /**
28 | * @global
29 | */
30 | let dom = {
31 | container: document.querySelector('html')
32 | };
33 |
34 | /**
35 | * Get color
36 | * @return {tinycolor} Color
37 | */
38 | let getColor = () => tinycolor(dom.container.style.backgroundColor);
39 |
40 | /**
41 | * Get alpha
42 | * @return {OverlayAlpha} Alpha
43 | */
44 | let getAlpha = () => (getColor()).getAlpha();
45 |
46 |
47 | /**
48 | * @listens Electron:ipcRenderer#overlay-change
49 | */
50 | ipcRenderer.on('update-overlay', (event, overlayConfiguration) => {
51 | logger.debug('ipcRenderer#overlay-change');
52 |
53 | /**
54 | * Alpha
55 | */
56 | if (overlayConfiguration.alpha) {
57 | const targetColor = getColor();
58 | targetColor.setAlpha(parseFloat(overlayConfiguration.alpha));
59 | dom.container.style.backgroundColor = targetColor.toRgbString();
60 | }
61 |
62 | /**
63 | * Color
64 | */
65 | if (overlayConfiguration.color) {
66 | const targetColor = tinycolor(overlayConfiguration.color);
67 | targetColor.setAlpha(getAlpha());
68 | dom.container.style.backgroundColor = targetColor.toRgbString();
69 | }
70 | });
71 |
--------------------------------------------------------------------------------
/app/scripts/renderer/preferences.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 |
4 | /**
5 | * Modules
6 | * Node
7 | * @constant
8 | */
9 | const path = require('path');
10 |
11 | /**
12 | * Modules
13 | * Electron
14 | * @constant
15 | */
16 | const electron = require('electron');
17 | const { remote } = electron;
18 |
19 | /**
20 | * Modules
21 | * External
22 | * @constant
23 | */
24 | const _ = require('lodash');
25 | const appRootPath = require('app-root-path').path;
26 | const logger = require('@sidneys/logger')({ write: true });
27 |
28 | /**
29 | * Modules
30 | * Internal
31 | * @constant
32 | */
33 | const configurationManager = remote.require(path.join(appRootPath, 'app', 'scripts', 'main', 'managers', 'configuration-manager'));
34 |
35 |
36 | /**
37 | * App
38 | * @global
39 | * @constant
40 | */
41 | const appVersion = remote.getGlobal('manifest').version;
42 |
43 |
44 | /**
45 | * DOM Elements
46 | * @global
47 | */
48 | const preferencesListElement = document.querySelector('.preferencesList');
49 | const versionTextElement = document.querySelector('.version');
50 |
51 | /**
52 | * Set version
53 | * @param {String} version - Version
54 | * @return {void}
55 | */
56 | let setVersion = (version) => versionTextElement.innerText = version.trim();
57 |
58 |
59 | /**
60 | * Reads electron-settings and generates HTML form
61 | */
62 | let renderPreferences = () => {
63 | logger.debug('registerPreferences');
64 |
65 | let preferenceKeyList = [
66 | 'appAutoUpdate',
67 | 'appLaunchOnStartup'
68 | ];
69 |
70 | preferenceKeyList.forEach((keyName) => {
71 | const preferenceInputElement = document.createElement('input');
72 | preferenceInputElement.id = keyName;
73 | preferenceInputElement.type = 'checkbox';
74 | preferenceInputElement.checked = configurationManager(keyName).get();
75 |
76 | const preferenceLabelElement = document.createElement('label');
77 | preferenceLabelElement.appendChild(preferenceInputElement);
78 | preferenceLabelElement.appendChild(document.createTextNode(_.startCase(keyName)));
79 | preferencesListElement.appendChild(preferenceLabelElement);
80 |
81 | /**
82 | * @listens preferenceInputElement:MouseEvent#click
83 | */
84 | preferenceInputElement.addEventListener('click', () => {
85 | logger.debug('preferenceInputElement#click ');
86 |
87 | configurationManager(keyName).set(preferenceInputElement.checked);
88 | });
89 | });
90 | };
91 |
92 | /**
93 | * @listens window#load
94 | */
95 | window.addEventListener('load', () => {
96 | logger.debug('window#load');
97 |
98 | setVersion(appVersion);
99 | });
100 |
101 | /**
102 | * @listens document:DOMContentLoaded
103 | */
104 | document.addEventListener('DOMContentLoaded', () => {
105 | logger.debug('document#DOMContentLoaded');
106 |
107 | renderPreferences();
108 | });
109 |
--------------------------------------------------------------------------------
/app/styles/styles.css:
--------------------------------------------------------------------------------
1 | /* ==========================================================================
2 | GLOBAL
3 | ========================================================================== */
4 |
5 | /* html
6 | ========================================================================== */
7 |
8 | html,
9 | body
10 | {
11 | padding: 0;
12 | margin: 0;
13 | overflow: hidden;
14 | }
15 |
16 | html *
17 | {
18 | -webkit-font-smoothing: subpixel-antialiased;
19 | }
20 |
21 | textarea,
22 | input,
23 | button
24 | {
25 | outline: none;
26 | }
27 |
28 |
29 | /* draggable
30 | ========================================================================== */
31 |
32 | .linux .window-controls
33 | {
34 | -webkit-app-region: drag;
35 | }
36 |
37 | .linux .window-controls .button
38 | {
39 | -webkit-app-region: no-drag;
40 | }
41 |
42 | /* ==========================================================================
43 | OVERLAY
44 | ========================================================================== */
45 |
46 | /* html
47 | ========================================================================== */
48 |
49 | html.overlay
50 | {
51 | background-color: transparent;
52 | transition: all 250ms ease-in-out;
53 | }
54 |
55 | /* ==========================================================================
56 | CONTROLLER
57 | ========================================================================== */
58 |
59 | /* html
60 | ========================================================================== */
61 |
62 | html.controller
63 | {
64 | font-size: 62.5%;
65 | font-family: 'Roboto', sans-serif;
66 | font-weight: 300;
67 | }
68 |
69 | /* .window-controls
70 | ========================================================================== */
71 | .window-controls
72 | {
73 | display: block;
74 | text-align: right;
75 | position: relative;
76 | background-color: rgba(0, 0, 0, 0.15);
77 | transition: all 100ms 50ms ease-out;
78 | }
79 |
80 | .window-controls:hover
81 | {
82 | transform: translate(-2.4rem, -0.1rem) scale(1.2);
83 | }
84 |
85 | .window-controls .button
86 | {
87 | color: rgba(255, 255, 255, 0.9);
88 | position: relative;
89 | padding: 0 0.2rem;
90 | margin: 0;
91 | height: 2rem;
92 | font-style: normal;
93 | line-height: 2rem;
94 | font-size: 1.5rem;
95 | text-decoration: none;
96 |
97 | transition: all 250ms ease-in-out;
98 | }
99 |
100 | .window-controls .button:hover
101 | {
102 | color: rgba(255, 255, 255, 1.0);
103 | transform: scale(1.5);
104 | }
105 |
106 | .window-controls .hide
107 | {
108 | display: none;
109 | opacity: 0;
110 | visibility: hidden;
111 | }
112 |
113 | /* .display-control-list
114 | ========================================================================== */
115 |
116 | .display-control-list
117 | {
118 | padding: 0.5rem 1rem 0 1rem;
119 | opacity: 0;
120 | transition: all 200ms ease-out;
121 | }
122 |
123 | /* .display-control
124 | ========================================================================== */
125 |
126 | .display-control-list__display-control
127 | {
128 | margin: 0;
129 | height: 3rem;
130 | }
131 |
132 | .display-control-list__display-control:not(:last-child)
133 | {
134 | border-bottom: 1px solid rgba(255, 255, 255, 0.3);
135 | }
136 |
137 | /* .display-control__alpha_slider
138 | ========================================================================== */
139 |
140 | input.alpha
141 | {
142 | position: relative;
143 | display: block;
144 | float: right;
145 | width: 75%;
146 | height: 3rem;
147 | margin: 0;
148 | padding: 0;
149 | }
150 |
151 | /* .display-control__alpha-label
152 | ========================================================================== */
153 |
154 | output.alpha
155 | {
156 | color: rgba(255, 255, 255, 1);
157 | padding: 0 0 0 4%;
158 | width: 15%;
159 | margin: 0;
160 | line-height: 3rem;
161 | text-align: left;
162 | float: left;
163 | font-size: 1.2rem;
164 | }
165 |
166 | /* input
167 | ========================================================================== */
168 |
169 | input[type=range]
170 | {
171 | -webkit-appearance: none;
172 | background: transparent;
173 | }
174 |
175 | input[type=range]::-webkit-slider-runnable-track
176 | {
177 | width: 100%;
178 | height: 0.25rem;
179 | background-color: rgba(255, 255, 255, 0.3);
180 | border: none;
181 | }
182 |
183 | input[type=range]::-webkit-slider-thumb
184 | {
185 | background-color: rgb(255, 255, 255);
186 | -webkit-appearance: none;
187 | border: 1px solid rgba(0, 0, 0, 0.3);
188 | height: 1.2rem;
189 | width: 1.2rem;
190 | border-radius: 50%;
191 | padding: 0.2rem;
192 | margin-top: -0.44rem;
193 | }
194 |
195 | input[type=range]:focus
196 | {
197 | outline: none;
198 | }
199 |
200 | /* .display-control__visibility
201 | ========================================================================== */
202 |
203 | input.visibility
204 | {
205 | -webkit-appearance: none;
206 | background: transparent;
207 | border: rgba(255, 255, 255, 0.3) 0.1rem solid;
208 | padding: 0;
209 | width: 5%;
210 | margin: 0.9rem 0 0 0.2rem;
211 | height: 1.2rem;
212 | text-align: left;
213 | float: left;
214 | font-size: 1rem;
215 | color: rgba(255, 255, 255, 0.75);
216 | }
217 |
218 | input.visibility:checked:after
219 | {
220 | content: '\2715';
221 | position: relative;
222 | left: 0.12rem;
223 | top: -0.1rem;
224 | }
225 |
226 | /* .preferences
227 | ========================================================================== */
228 |
229 | .preferences .version
230 | {
231 | text-align: right;
232 | font-size: 0.7rem;
233 | line-height: 0.7rem;
234 | opacity: 0.5;
235 | }
236 |
--------------------------------------------------------------------------------
/app/vendor/material-icons.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Material Icons';
3 | font-style: normal;
4 | font-weight: 400;
5 | src: url('../fonts/MaterialIcons-Regular.woff2') format('woff2'),
6 | url('../fonts/MaterialIcons-Regular.woff') format('woff'),
7 | url('../fonts/MaterialIcons-Regular.ttf') format('truetype');
8 | }
9 |
10 | .material-icons, .button {
11 | font-family: 'Material Icons';
12 | font-weight: normal;
13 | font-style: normal;
14 | font-size: 24px; /* Preferred icon size */
15 | display: inline-block;
16 | line-height: 1;
17 | text-transform: none;
18 | letter-spacing: normal;
19 | word-wrap: normal;
20 | white-space: nowrap;
21 | direction: ltr;
22 |
23 | /* Support for all WebKit browsers. */
24 | -webkit-font-smoothing: antialiased;
25 | /* Support for Safari and Chrome. */
26 | text-rendering: optimizeLegibility;
27 |
28 | /* Support for Firefox. */
29 | -moz-osx-font-smoothing: grayscale;
30 |
31 | /* Support for IE. */
32 | font-feature-settings: 'liga';
33 | }
34 |
--------------------------------------------------------------------------------
/app/vendor/photon.css:
--------------------------------------------------------------------------------
1 | /*!
2 | * =====================================================
3 | * Photon v0.1.2
4 | * Copyright 2016 Connor Sears
5 | * Licensed under MIT (https://github.com/connors/proton/blob/master/LICENSE)
6 | *
7 | * v0.1.2 designed by @connors.
8 | * =====================================================
9 | */
10 |
11 | @charset "UTF-8";
12 | audio,
13 | canvas,
14 | progress,
15 | video {
16 | vertical-align: baseline;
17 | }
18 |
19 | audio:not([controls]) {
20 | display: none;
21 | }
22 |
23 | a:active,
24 | a:hover {
25 | outline: 0;
26 | }
27 |
28 | abbr[title] {
29 | border-bottom: 1px dotted;
30 | }
31 |
32 | b,
33 | strong {
34 | font-weight: bold;
35 | }
36 |
37 | dfn {
38 | font-style: italic;
39 | }
40 |
41 | h1 {
42 | font-size: 2em;
43 | margin: 0.67em 0;
44 | }
45 |
46 | small {
47 | font-size: 80%;
48 | }
49 |
50 | sub,
51 | sup {
52 | font-size: 75%;
53 | line-height: 0;
54 | position: relative;
55 | vertical-align: baseline;
56 | }
57 |
58 | sup {
59 | top: -0.5em;
60 | }
61 |
62 | sub {
63 | bottom: -0.25em;
64 | }
65 |
66 | pre {
67 | overflow: auto;
68 | }
69 |
70 | code,
71 | kbd,
72 | pre,
73 | samp {
74 | font-family: monospace, monospace;
75 | font-size: 1em;
76 | }
77 |
78 | button,
79 | input,
80 | optgroup,
81 | select,
82 | textarea {
83 | color: inherit;
84 | font: inherit;
85 | margin: 0;
86 | }
87 |
88 | input[type="number"]::-webkit-inner-spin-button,
89 | input[type="number"]::-webkit-outer-spin-button {
90 | height: auto;
91 | }
92 |
93 | input[type="search"] {
94 | -webkit-appearance: textfield;
95 | box-sizing: content-box;
96 | }
97 |
98 | input[type="search"]::-webkit-search-cancel-button,
99 | input[type="search"]::-webkit-search-decoration {
100 | -webkit-appearance: none;
101 | }
102 |
103 | fieldset {
104 | border: 1px solid #c0c0c0;
105 | margin: 0 2px;
106 | padding: 0.35em 0.625em 0.75em;
107 | }
108 |
109 | legend {
110 | border: 0;
111 | padding: 0;
112 | }
113 |
114 | table {
115 | border-collapse: collapse;
116 | border-spacing: 0;
117 | }
118 |
119 | td,
120 | th {
121 | padding: 0;
122 | }
123 |
124 | * {
125 | cursor: default;
126 | -webkit-user-select: none;
127 | }
128 |
129 | input,
130 | textarea {
131 | -webkit-user-select: text;
132 | }
133 |
134 | form,
135 | input,
136 | optgroup,
137 | select,
138 | textarea {
139 | -webkit-user-select: text;
140 | -webkit-app-region: no-drag;
141 | }
142 |
143 | * {
144 | -webkit-box-sizing: border-box;
145 | box-sizing: border-box;
146 | }
147 |
148 | html {
149 | height: 100%;
150 | width: 100%;
151 | overflow: hidden;
152 | }
153 |
154 | body {
155 | height: 100%;
156 | padding: 0;
157 | margin: 0;
158 | font-family: system, -apple-system, ".SFNSDisplay-Regular", "Helvetica Neue", Helvetica, "Segoe UI", sans-serif;
159 | font-size: 13px;
160 | line-height: 1.6;
161 | color: #333;
162 | background-color: transparent;
163 | }
164 |
165 | hr {
166 | margin: 15px 0;
167 | overflow: hidden;
168 | background: transparent;
169 | border: 0;
170 | border-bottom: 1px solid #ddd;
171 | }
172 |
173 | h1, h2, h3, h4, h5, h6 {
174 | margin-top: 20px;
175 | margin-bottom: 10px;
176 | font-weight: 500;
177 | white-space: nowrap;
178 | overflow: hidden;
179 | text-overflow: ellipsis;
180 | }
181 |
182 | h1 {
183 | font-size: 36px;
184 | }
185 |
186 | h2 {
187 | font-size: 30px;
188 | }
189 |
190 | h3 {
191 | font-size: 24px;
192 | }
193 |
194 | h4 {
195 | font-size: 18px;
196 | }
197 |
198 | h5 {
199 | font-size: 14px;
200 | }
201 |
202 | h6 {
203 | font-size: 12px;
204 | }
205 |
206 | .window {
207 | position: absolute;
208 | top: 0;
209 | right: 0;
210 | bottom: 0;
211 | left: 0;
212 | display: flex;
213 | flex-direction: column;
214 | background-color: #fff;
215 | }
216 |
217 | .window-content {
218 | position: relative;
219 | overflow-y: auto;
220 | display: flex;
221 | flex: 1;
222 | }
223 |
224 | .selectable-text {
225 | cursor: text;
226 | -webkit-user-select: text;
227 | }
228 |
229 | .text-center {
230 | text-align: center;
231 | }
232 |
233 | .text-right {
234 | text-align: right;
235 | }
236 |
237 | .text-left {
238 | text-align: left;
239 | }
240 |
241 | .pull-left {
242 | float: left;
243 | }
244 |
245 | .pull-right {
246 | float: right;
247 | }
248 |
249 | .padded {
250 | padding: 10px;
251 | }
252 |
253 | .padded-less {
254 | padding: 5px;
255 | }
256 |
257 | .padded-more {
258 | padding: 20px;
259 | }
260 |
261 | .padded-vertically {
262 | padding-top: 10px;
263 | padding-bottom: 10px;
264 | }
265 |
266 | .padded-vertically-less {
267 | padding-top: 5px;
268 | padding-bottom: 5px;
269 | }
270 |
271 | .padded-vertically-more {
272 | padding-top: 20px;
273 | padding-bottom: 20px;
274 | }
275 |
276 | .padded-horizontally {
277 | padding-right: 10px;
278 | padding-left: 10px;
279 | }
280 |
281 | .padded-horizontally-less {
282 | padding-right: 5px;
283 | padding-left: 5px;
284 | }
285 |
286 | .padded-horizontally-more {
287 | padding-right: 20px;
288 | padding-left: 20px;
289 | }
290 |
291 | .padded-top {
292 | padding-top: 10px;
293 | }
294 |
295 | .padded-top-less {
296 | padding-top: 5px;
297 | }
298 |
299 | .padded-top-more {
300 | padding-top: 20px;
301 | }
302 |
303 | .padded-bottom {
304 | padding-bottom: 10px;
305 | }
306 |
307 | .padded-bottom-less {
308 | padding-bottom: 5px;
309 | }
310 |
311 | .padded-bottom-more {
312 | padding-bottom: 20px;
313 | }
314 |
315 | .sidebar {
316 | background-color: #f5f5f4;
317 | }
318 |
319 | .draggable {
320 | -webkit-app-region: drag;
321 | }
322 |
323 | .not-draggable {
324 | -webkit-app-region: no-drag;
325 | }
326 |
327 | .clearfix:before, .clearfix:after {
328 | display: table;
329 | content: " ";
330 | }
331 | .clearfix:after {
332 | clear: both;
333 | }
334 |
335 | .btn {
336 | display: inline-block;
337 | padding: 3px 8px;
338 | margin-bottom: 0;
339 | font-size: 12px;
340 | line-height: 1.4;
341 | text-align: center;
342 | white-space: nowrap;
343 | vertical-align: middle;
344 | cursor: default;
345 | background-image: none;
346 | border: 1px solid transparent;
347 | border-radius: 4px;
348 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.06);
349 | -webkit-app-region: no-drag;
350 | }
351 | .btn:focus {
352 | outline: none;
353 | box-shadow: none;
354 | }
355 |
356 | .btn-mini {
357 | padding: 2px 6px;
358 | }
359 |
360 | .btn-large {
361 | padding: 6px 12px;
362 | }
363 |
364 | .btn-form {
365 | padding-right: 20px;
366 | padding-left: 20px;
367 | }
368 |
369 | .btn-default {
370 | color: #333;
371 | border-top-color: #c2c0c2;
372 | border-right-color: #c2c0c2;
373 | border-bottom-color: #a19fa1;
374 | border-left-color: #c2c0c2;
375 | background-color: #fcfcfc;
376 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fcfcfc), color-stop(100%, #f1f1f1));
377 | background-image: -webkit-linear-gradient(top, #fcfcfc 0%, #f1f1f1 100%);
378 | background-image: linear-gradient(to bottom, #fcfcfc 0%, #f1f1f1 100%);
379 | }
380 | .btn-default:active {
381 | background-color: #ddd;
382 | background-image: none;
383 | }
384 |
385 | .btn-primary,
386 | .btn-positive,
387 | .btn-negative,
388 | .btn-warning {
389 | color: #fff;
390 | text-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
391 | }
392 |
393 | .btn-primary {
394 | border-color: #388df8;
395 | border-bottom-color: #0866dc;
396 | background-color: #6eb4f7;
397 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #6eb4f7), color-stop(100%, #1a82fb));
398 | background-image: -webkit-linear-gradient(top, #6eb4f7 0%, #1a82fb 100%);
399 | background-image: linear-gradient(to bottom, #6eb4f7 0%, #1a82fb 100%);
400 | }
401 | .btn-primary:active {
402 | background-color: #3e9bf4;
403 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #3e9bf4), color-stop(100%, #0469de));
404 | background-image: -webkit-linear-gradient(top, #3e9bf4 0%, #0469de 100%);
405 | background-image: linear-gradient(to bottom, #3e9bf4 0%, #0469de 100%);
406 | }
407 |
408 | .btn-positive {
409 | border-color: #29a03b;
410 | border-bottom-color: #248b34;
411 | background-color: #5bd46d;
412 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #5bd46d), color-stop(100%, #29a03b));
413 | background-image: -webkit-linear-gradient(top, #5bd46d 0%, #29a03b 100%);
414 | background-image: linear-gradient(to bottom, #5bd46d 0%, #29a03b 100%);
415 | }
416 | .btn-positive:active {
417 | background-color: #34c84a;
418 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #34c84a), color-stop(100%, #248b34));
419 | background-image: -webkit-linear-gradient(top, #34c84a 0%, #248b34 100%);
420 | background-image: linear-gradient(to bottom, #34c84a 0%, #248b34 100%);
421 | }
422 |
423 | .btn-negative {
424 | border-color: #fb2f29;
425 | border-bottom-color: #fb1710;
426 | background-color: #fd918d;
427 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fd918d), color-stop(100%, #fb2f29));
428 | background-image: -webkit-linear-gradient(top, #fd918d 0%, #fb2f29 100%);
429 | background-image: linear-gradient(to bottom, #fd918d 0%, #fb2f29 100%);
430 | }
431 | .btn-negative:active {
432 | background-color: #fc605b;
433 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fc605b), color-stop(100%, #fb1710));
434 | background-image: -webkit-linear-gradient(top, #fc605b 0%, #fb1710 100%);
435 | background-image: linear-gradient(to bottom, #fc605b 0%, #fb1710 100%);
436 | }
437 |
438 | .btn-warning {
439 | border-color: #fcaa0e;
440 | border-bottom-color: #ee9d02;
441 | background-color: #fece72;
442 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fece72), color-stop(100%, #fcaa0e));
443 | background-image: -webkit-linear-gradient(top, #fece72 0%, #fcaa0e 100%);
444 | background-image: linear-gradient(to bottom, #fece72 0%, #fcaa0e 100%);
445 | }
446 | .btn-warning:active {
447 | background-color: #fdbc40;
448 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fdbc40), color-stop(100%, #ee9d02));
449 | background-image: -webkit-linear-gradient(top, #fdbc40 0%, #ee9d02 100%);
450 | background-image: linear-gradient(to bottom, #fdbc40 0%, #ee9d02 100%);
451 | }
452 |
453 | .btn .icon {
454 | float: left;
455 | width: 14px;
456 | height: 14px;
457 | margin-top: 1px;
458 | margin-bottom: 1px;
459 | color: #737475;
460 | font-size: 14px;
461 | line-height: 1;
462 | }
463 |
464 | .btn .icon-text {
465 | margin-right: 5px;
466 | }
467 |
468 | .btn-dropdown:after {
469 | font-family: "photon-entypo";
470 | margin-left: 5px;
471 | content: '\e873';
472 | }
473 |
474 | .btn-group {
475 | position: relative;
476 | display: inline-block;
477 | vertical-align: middle;
478 | -webkit-app-region: no-drag;
479 | }
480 | .btn-group .btn {
481 | position: relative;
482 | float: left;
483 | }
484 | .btn-group .btn:focus, .btn-group .btn:active {
485 | z-index: 2;
486 | }
487 | .btn-group .btn.active {
488 | z-index: 3;
489 | }
490 |
491 | .btn-group .btn + .btn,
492 | .btn-group .btn + .btn-group,
493 | .btn-group .btn-group + .btn,
494 | .btn-group .btn-group + .btn-group {
495 | margin-left: -1px;
496 | }
497 | .btn-group > .btn:first-child {
498 | border-top-right-radius: 0;
499 | border-bottom-right-radius: 0;
500 | }
501 | .btn-group > .btn:last-child {
502 | border-top-left-radius: 0;
503 | border-bottom-left-radius: 0;
504 | }
505 | .btn-group > .btn:not(:first-child):not(:last-child) {
506 | border-radius: 0;
507 | }
508 | .btn-group .btn + .btn {
509 | border-left: 1px solid #c2c0c2;
510 | }
511 | .btn-group .btn + .btn.active {
512 | border-left: 0;
513 | }
514 | .btn-group .active {
515 | color: #fff;
516 | border: 1px solid transparent;
517 | background-color: #6d6c6d;
518 | background-image: none;
519 | }
520 | .btn-group .active .icon {
521 | color: #fff;
522 | }
523 |
524 | .toolbar {
525 | min-height: 22px;
526 | box-shadow: inset 0 1px 0 #f5f4f5;
527 | background-color: #e8e6e8;
528 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #e8e6e8), color-stop(100%, #d1cfd1));
529 | background-image: -webkit-linear-gradient(top, #e8e6e8 0%, #d1cfd1 100%);
530 | background-image: linear-gradient(to bottom, #e8e6e8 0%, #d1cfd1 100%);
531 | }
532 | .toolbar:before, .toolbar:after {
533 | display: table;
534 | content: " ";
535 | }
536 | .toolbar:after {
537 | clear: both;
538 | }
539 |
540 | .toolbar-header {
541 | border-bottom: 1px solid #c2c0c2;
542 | }
543 | .toolbar-header .title {
544 | margin-top: 1px;
545 | }
546 |
547 | .toolbar-footer {
548 | border-top: 1px solid #c2c0c2;
549 | -webkit-app-region: drag;
550 | }
551 |
552 | .title {
553 | margin: 0;
554 | font-size: 12px;
555 | font-weight: 400;
556 | text-align: center;
557 | color: #555;
558 | cursor: default;
559 | }
560 |
561 | .toolbar-borderless {
562 | border-top: 0;
563 | border-bottom: 0;
564 | }
565 |
566 | .toolbar-actions {
567 | margin-top: 4px;
568 | margin-bottom: 3px;
569 | padding-right: 3px;
570 | padding-left: 3px;
571 | padding-bottom: 3px;
572 | -webkit-app-region: drag;
573 | }
574 | .toolbar-actions:before, .toolbar-actions:after {
575 | display: table;
576 | content: " ";
577 | }
578 | .toolbar-actions:after {
579 | clear: both;
580 | }
581 | .toolbar-actions > .btn,
582 | .toolbar-actions > .btn-group {
583 | margin-left: 4px;
584 | margin-right: 4px;
585 | }
586 |
587 | label {
588 | display: inline-block;
589 | font-size: 13px;
590 | margin-bottom: 5px;
591 | white-space: nowrap;
592 | overflow: hidden;
593 | text-overflow: ellipsis;
594 | }
595 |
596 | input[type="search"] {
597 | box-sizing: border-box;
598 | }
599 |
600 | input[type="radio"],
601 | input[type="checkbox"] {
602 | margin: 4px 0 0;
603 | line-height: normal;
604 | }
605 |
606 | .form-control {
607 | display: inline-block;
608 | width: 100%;
609 | min-height: 25px;
610 | padding: 5px 10px;
611 | font-size: 13px;
612 | line-height: 1.6;
613 | background-color: #fff;
614 | border: 1px solid #ddd;
615 | border-radius: 4px;
616 | outline: none;
617 | }
618 | .form-control:focus {
619 | border-color: #6db3fd;
620 | box-shadow: 0 0 0 3px #6db3fd;
621 | }
622 |
623 | textarea {
624 | height: auto;
625 | }
626 |
627 | .form-group {
628 | margin-bottom: 10px;
629 | }
630 |
631 | .radio,
632 | .checkbox {
633 | position: relative;
634 | display: block;
635 | margin-top: 10px;
636 | margin-bottom: 10px;
637 | }
638 | .radio label,
639 | .checkbox label {
640 | padding-left: 20px;
641 | margin-bottom: 0;
642 | font-weight: normal;
643 | }
644 |
645 | .radio input[type="radio"],
646 | .radio-inline input[type="radio"],
647 | .checkbox input[type="checkbox"],
648 | .checkbox-inline input[type="checkbox"] {
649 | position: absolute;
650 | margin-left: -20px;
651 | margin-top: 4px;
652 | }
653 |
654 | .form-actions .btn {
655 | margin-right: 10px;
656 | }
657 | .form-actions .btn:last-child {
658 | margin-right: 0;
659 | }
660 |
661 | .pane-group {
662 | position: absolute;
663 | top: 0;
664 | right: 0;
665 | bottom: 0;
666 | left: 0;
667 | display: flex;
668 | }
669 |
670 | .pane {
671 | position: relative;
672 | overflow-y: auto;
673 | flex: 1;
674 | border-left: 1px solid #ddd;
675 | }
676 | .pane:first-child {
677 | border-left: 0;
678 | }
679 |
680 | .pane-sm {
681 | max-width: 220px;
682 | min-width: 150px;
683 | }
684 |
685 | .pane-mini {
686 | width: 80px;
687 | flex: none;
688 | }
689 |
690 | .pane-one-fourth {
691 | width: 25%;
692 | flex: none;
693 | }
694 |
695 | .pane-one-third {
696 | width: 33.3%;
697 | flex: none;
698 | }
699 |
700 | img {
701 | -webkit-user-drag: text;
702 | }
703 |
704 | .img-circle {
705 | border-radius: 50%;
706 | }
707 |
708 | .img-rounded {
709 | border-radius: 4px;
710 | }
711 |
712 | .list-group {
713 | width: 100%;
714 | list-style: none;
715 | margin: 0;
716 | padding: 0;
717 | }
718 | .list-group * {
719 | margin: 0;
720 | white-space: nowrap;
721 | overflow: hidden;
722 | text-overflow: ellipsis;
723 | }
724 |
725 | .list-group-item {
726 | padding: 10px;
727 | font-size: 12px;
728 | color: #414142;
729 | border-top: 1px solid #ddd;
730 | }
731 | .list-group-item:first-child {
732 | border-top: 0;
733 | }
734 | .list-group-item.active, .list-group-item.selected {
735 | color: #fff;
736 | background-color: #116cd6;
737 | }
738 |
739 | .list-group-header {
740 | padding: 10px;
741 | }
742 |
743 | .media-object {
744 | margin-top: 3px;
745 | }
746 |
747 | .media-object.pull-left {
748 | margin-right: 10px;
749 | }
750 |
751 | .media-object.pull-right {
752 | margin-left: 10px;
753 | }
754 |
755 | .media-body {
756 | overflow: hidden;
757 | }
758 |
759 | .nav-group {
760 | font-size: 14px;
761 | }
762 |
763 | .nav-group-item {
764 | padding: 2px 10px 2px 25px;
765 | display: block;
766 | color: #333;
767 | text-decoration: none;
768 | white-space: nowrap;
769 | overflow: hidden;
770 | text-overflow: ellipsis;
771 | }
772 | .nav-group-item:active, .nav-group-item.active {
773 | background-color: #dcdfe1;
774 | }
775 | .nav-group-item .icon {
776 | width: 19px;
777 | height: 18px;
778 | float: left;
779 | color: #737475;
780 | margin-top: -3px;
781 | margin-right: 7px;
782 | font-size: 18px;
783 | text-align: center;
784 | }
785 |
786 | .nav-group-title {
787 | margin: 0;
788 | padding: 10px 10px 2px;
789 | font-size: 12px;
790 | font-weight: 500;
791 | color: #666666;
792 | }
793 |
794 | @font-face {
795 | font-family: "photon-entypo";
796 | src: url("../fonts/photon-entypo.eot");
797 | src: url("../fonts/photon-entypo.eot?#iefix") format("eot"), url("../fonts/photon-entypo.woff") format("woff"), url("../fonts/photon-entypo.ttf") format("truetype");
798 | font-weight: normal;
799 | font-style: normal;
800 | }
801 | .icon:before {
802 | position: relative;
803 | display: inline-block;
804 | font-family: "photon-entypo";
805 | speak: none;
806 | font-size: 100%;
807 | font-style: normal;
808 | font-weight: normal;
809 | font-variant: normal;
810 | text-transform: none;
811 | line-height: 1;
812 | -webkit-font-smoothing: antialiased;
813 | -moz-osx-font-smoothing: grayscale;
814 | }
815 |
816 | .icon-note:before {
817 | content: '\e800';
818 | }
819 |
820 | /* '' */
821 | .icon-note-beamed:before {
822 | content: '\e801';
823 | }
824 |
825 | /* '' */
826 | .icon-music:before {
827 | content: '\e802';
828 | }
829 |
830 | /* '' */
831 | .icon-search:before {
832 | content: '\e803';
833 | }
834 |
835 | /* '' */
836 | .icon-flashlight:before {
837 | content: '\e804';
838 | }
839 |
840 | /* '' */
841 | .icon-mail:before {
842 | content: '\e805';
843 | }
844 |
845 | /* '' */
846 | .icon-heart:before {
847 | content: '\e806';
848 | }
849 |
850 | /* '' */
851 | .icon-heart-empty:before {
852 | content: '\e807';
853 | }
854 |
855 | /* '' */
856 | .icon-star:before {
857 | content: '\e808';
858 | }
859 |
860 | /* '' */
861 | .icon-star-empty:before {
862 | content: '\e809';
863 | }
864 |
865 | /* '' */
866 | .icon-user:before {
867 | content: '\e80a';
868 | }
869 |
870 | /* '' */
871 | .icon-users:before {
872 | content: '\e80b';
873 | }
874 |
875 | /* '' */
876 | .icon-user-add:before {
877 | content: '\e80c';
878 | }
879 |
880 | /* '' */
881 | .icon-video:before {
882 | content: '\e80d';
883 | }
884 |
885 | /* '' */
886 | .icon-picture:before {
887 | content: '\e80e';
888 | }
889 |
890 | /* '' */
891 | .icon-camera:before {
892 | content: '\e80f';
893 | }
894 |
895 | /* '' */
896 | .icon-layout:before {
897 | content: '\e810';
898 | }
899 |
900 | /* '' */
901 | .icon-menu:before {
902 | content: '\e811';
903 | }
904 |
905 | /* '' */
906 | .icon-check:before {
907 | content: '\e812';
908 | }
909 |
910 | /* '' */
911 | .icon-cancel:before {
912 | content: '\e813';
913 | }
914 |
915 | /* '' */
916 | .icon-cancel-circled:before {
917 | content: '\e814';
918 | }
919 |
920 | /* '' */
921 | .icon-cancel-squared:before {
922 | content: '\e815';
923 | }
924 |
925 | /* '' */
926 | .icon-plus:before {
927 | content: '\e816';
928 | }
929 |
930 | /* '' */
931 | .icon-plus-circled:before {
932 | content: '\e817';
933 | }
934 |
935 | /* '' */
936 | .icon-plus-squared:before {
937 | content: '\e818';
938 | }
939 |
940 | /* '' */
941 | .icon-minus:before {
942 | content: '\e819';
943 | }
944 |
945 | /* '' */
946 | .icon-minus-circled:before {
947 | content: '\e81a';
948 | }
949 |
950 | /* '' */
951 | .icon-minus-squared:before {
952 | content: '\e81b';
953 | }
954 |
955 | /* '' */
956 | .icon-help:before {
957 | content: '\e81c';
958 | }
959 |
960 | /* '' */
961 | .icon-help-circled:before {
962 | content: '\e81d';
963 | }
964 |
965 | /* '' */
966 | .icon-info:before {
967 | content: '\e81e';
968 | }
969 |
970 | /* '' */
971 | .icon-info-circled:before {
972 | content: '\e81f';
973 | }
974 |
975 | /* '' */
976 | .icon-back:before {
977 | content: '\e820';
978 | }
979 |
980 | /* '' */
981 | .icon-home:before {
982 | content: '\e821';
983 | }
984 |
985 | /* '' */
986 | .icon-link:before {
987 | content: '\e822';
988 | }
989 |
990 | /* '' */
991 | .icon-attach:before {
992 | content: '\e823';
993 | }
994 |
995 | /* '' */
996 | .icon-lock:before {
997 | content: '\e824';
998 | }
999 |
1000 | /* '' */
1001 | .icon-lock-open:before {
1002 | content: '\e825';
1003 | }
1004 |
1005 | /* '' */
1006 | .icon-eye:before {
1007 | content: '\e826';
1008 | }
1009 |
1010 | /* '' */
1011 | .icon-tag:before {
1012 | content: '\e827';
1013 | }
1014 |
1015 | /* '' */
1016 | .icon-bookmark:before {
1017 | content: '\e828';
1018 | }
1019 |
1020 | /* '' */
1021 | .icon-bookmarks:before {
1022 | content: '\e829';
1023 | }
1024 |
1025 | /* '' */
1026 | .icon-flag:before {
1027 | content: '\e82a';
1028 | }
1029 |
1030 | /* '' */
1031 | .icon-thumbs-up:before {
1032 | content: '\e82b';
1033 | }
1034 |
1035 | /* '' */
1036 | .icon-thumbs-down:before {
1037 | content: '\e82c';
1038 | }
1039 |
1040 | /* '' */
1041 | .icon-download:before {
1042 | content: '\e82d';
1043 | }
1044 |
1045 | /* '' */
1046 | .icon-upload:before {
1047 | content: '\e82e';
1048 | }
1049 |
1050 | /* '' */
1051 | .icon-upload-cloud:before {
1052 | content: '\e82f';
1053 | }
1054 |
1055 | /* '' */
1056 | .icon-reply:before {
1057 | content: '\e830';
1058 | }
1059 |
1060 | /* '' */
1061 | .icon-reply-all:before {
1062 | content: '\e831';
1063 | }
1064 |
1065 | /* '' */
1066 | .icon-forward:before {
1067 | content: '\e832';
1068 | }
1069 |
1070 | /* '' */
1071 | .icon-quote:before {
1072 | content: '\e833';
1073 | }
1074 |
1075 | /* '' */
1076 | .icon-code:before {
1077 | content: '\e834';
1078 | }
1079 |
1080 | /* '' */
1081 | .icon-export:before {
1082 | content: '\e835';
1083 | }
1084 |
1085 | /* '' */
1086 | .icon-pencil:before {
1087 | content: '\e836';
1088 | }
1089 |
1090 | /* '' */
1091 | .icon-feather:before {
1092 | content: '\e837';
1093 | }
1094 |
1095 | /* '' */
1096 | .icon-print:before {
1097 | content: '\e838';
1098 | }
1099 |
1100 | /* '' */
1101 | .icon-retweet:before {
1102 | content: '\e839';
1103 | }
1104 |
1105 | /* '' */
1106 | .icon-keyboard:before {
1107 | content: '\e83a';
1108 | }
1109 |
1110 | /* '' */
1111 | .icon-comment:before {
1112 | content: '\e83b';
1113 | }
1114 |
1115 | /* '' */
1116 | .icon-chat:before {
1117 | content: '\e83c';
1118 | }
1119 |
1120 | /* '' */
1121 | .icon-bell:before {
1122 | content: '\e83d';
1123 | }
1124 |
1125 | /* '' */
1126 | .icon-attention:before {
1127 | content: '\e83e';
1128 | }
1129 |
1130 | /* '' */
1131 | .icon-alert:before {
1132 | content: '\e83f';
1133 | }
1134 |
1135 | /* '' */
1136 | .icon-vcard:before {
1137 | content: '\e840';
1138 | }
1139 |
1140 | /* '' */
1141 | .icon-address:before {
1142 | content: '\e841';
1143 | }
1144 |
1145 | /* '' */
1146 | .icon-location:before {
1147 | content: '\e842';
1148 | }
1149 |
1150 | /* '' */
1151 | .icon-map:before {
1152 | content: '\e843';
1153 | }
1154 |
1155 | /* '' */
1156 | .icon-direction:before {
1157 | content: '\e844';
1158 | }
1159 |
1160 | /* '' */
1161 | .icon-compass:before {
1162 | content: '\e845';
1163 | }
1164 |
1165 | /* '' */
1166 | .icon-cup:before {
1167 | content: '\e846';
1168 | }
1169 |
1170 | /* '' */
1171 | .icon-trash:before {
1172 | content: '\e847';
1173 | }
1174 |
1175 | /* '' */
1176 | .icon-doc:before {
1177 | content: '\e848';
1178 | }
1179 |
1180 | /* '' */
1181 | .icon-docs:before {
1182 | content: '\e849';
1183 | }
1184 |
1185 | /* '' */
1186 | .icon-doc-landscape:before {
1187 | content: '\e84a';
1188 | }
1189 |
1190 | /* '' */
1191 | .icon-doc-text:before {
1192 | content: '\e84b';
1193 | }
1194 |
1195 | /* '' */
1196 | .icon-doc-text-inv:before {
1197 | content: '\e84c';
1198 | }
1199 |
1200 | /* '' */
1201 | .icon-newspaper:before {
1202 | content: '\e84d';
1203 | }
1204 |
1205 | /* '' */
1206 | .icon-book-open:before {
1207 | content: '\e84e';
1208 | }
1209 |
1210 | /* '' */
1211 | .icon-book:before {
1212 | content: '\e84f';
1213 | }
1214 |
1215 | /* '' */
1216 | .icon-folder:before {
1217 | content: '\e850';
1218 | }
1219 |
1220 | /* '' */
1221 | .icon-archive:before {
1222 | content: '\e851';
1223 | }
1224 |
1225 | /* '' */
1226 | .icon-box:before {
1227 | content: '\e852';
1228 | }
1229 |
1230 | /* '' */
1231 | .icon-rss:before {
1232 | content: '\e853';
1233 | }
1234 |
1235 | /* '' */
1236 | .icon-phone:before {
1237 | content: '\e854';
1238 | }
1239 |
1240 | /* '' */
1241 | .icon-cog:before {
1242 | content: '\e855';
1243 | }
1244 |
1245 | /* '' */
1246 | .icon-tools:before {
1247 | content: '\e856';
1248 | }
1249 |
1250 | /* '' */
1251 | .icon-share:before {
1252 | content: '\e857';
1253 | }
1254 |
1255 | /* '' */
1256 | .icon-shareable:before {
1257 | content: '\e858';
1258 | }
1259 |
1260 | /* '' */
1261 | .icon-basket:before {
1262 | content: '\e859';
1263 | }
1264 |
1265 | /* '' */
1266 | .icon-bag:before {
1267 | content: '\e85a';
1268 | }
1269 |
1270 | /* '' */
1271 | .icon-calendar:before {
1272 | content: '\e85b';
1273 | }
1274 |
1275 | /* '' */
1276 | .icon-login:before {
1277 | content: '\e85c';
1278 | }
1279 |
1280 | /* '' */
1281 | .icon-logout:before {
1282 | content: '\e85d';
1283 | }
1284 |
1285 | /* '' */
1286 | .icon-mic:before {
1287 | content: '\e85e';
1288 | }
1289 |
1290 | /* '' */
1291 | .icon-mute:before {
1292 | content: '\e85f';
1293 | }
1294 |
1295 | /* '' */
1296 | .icon-sound:before {
1297 | content: '\e860';
1298 | }
1299 |
1300 | /* '' */
1301 | .icon-volume:before {
1302 | content: '\e861';
1303 | }
1304 |
1305 | /* '' */
1306 | .icon-clock:before {
1307 | content: '\e862';
1308 | }
1309 |
1310 | /* '' */
1311 | .icon-hourglass:before {
1312 | content: '\e863';
1313 | }
1314 |
1315 | /* '' */
1316 | .icon-lamp:before {
1317 | content: '\e864';
1318 | }
1319 |
1320 | /* '' */
1321 | .icon-light-down:before {
1322 | content: '\e865';
1323 | }
1324 |
1325 | /* '' */
1326 | .icon-light-up:before {
1327 | content: '\e866';
1328 | }
1329 |
1330 | /* '' */
1331 | .icon-adjust:before {
1332 | content: '\e867';
1333 | }
1334 |
1335 | /* '' */
1336 | .icon-block:before {
1337 | content: '\e868';
1338 | }
1339 |
1340 | /* '' */
1341 | .icon-resize-full:before {
1342 | content: '\e869';
1343 | }
1344 |
1345 | /* '' */
1346 | .icon-resize-small:before {
1347 | content: '\e86a';
1348 | }
1349 |
1350 | /* '' */
1351 | .icon-popup:before {
1352 | content: '\e86b';
1353 | }
1354 |
1355 | /* '' */
1356 | .icon-publish:before {
1357 | content: '\e86c';
1358 | }
1359 |
1360 | /* '' */
1361 | .icon-window:before {
1362 | content: '\e86d';
1363 | }
1364 |
1365 | /* '' */
1366 | .icon-arrow-combo:before {
1367 | content: '\e86e';
1368 | }
1369 |
1370 | /* '' */
1371 | .icon-down-circled:before {
1372 | content: '\e86f';
1373 | }
1374 |
1375 | /* '' */
1376 | .icon-left-circled:before {
1377 | content: '\e870';
1378 | }
1379 |
1380 | /* '' */
1381 | .icon-right-circled:before {
1382 | content: '\e871';
1383 | }
1384 |
1385 | /* '' */
1386 | .icon-up-circled:before {
1387 | content: '\e872';
1388 | }
1389 |
1390 | /* '' */
1391 | .icon-down-open:before {
1392 | content: '\e873';
1393 | }
1394 |
1395 | /* '' */
1396 | .icon-left-open:before {
1397 | content: '\e874';
1398 | }
1399 |
1400 | /* '' */
1401 | .icon-right-open:before {
1402 | content: '\e875';
1403 | }
1404 |
1405 | /* '' */
1406 | .icon-up-open:before {
1407 | content: '\e876';
1408 | }
1409 |
1410 | /* '' */
1411 | .icon-down-open-mini:before {
1412 | content: '\e877';
1413 | }
1414 |
1415 | /* '' */
1416 | .icon-left-open-mini:before {
1417 | content: '\e878';
1418 | }
1419 |
1420 | /* '' */
1421 | .icon-right-open-mini:before {
1422 | content: '\e879';
1423 | }
1424 |
1425 | /* '' */
1426 | .icon-up-open-mini:before {
1427 | content: '\e87a';
1428 | }
1429 |
1430 | /* '' */
1431 | .icon-down-open-big:before {
1432 | content: '\e87b';
1433 | }
1434 |
1435 | /* '' */
1436 | .icon-left-open-big:before {
1437 | content: '\e87c';
1438 | }
1439 |
1440 | /* '' */
1441 | .icon-right-open-big:before {
1442 | content: '\e87d';
1443 | }
1444 |
1445 | /* '' */
1446 | .icon-up-open-big:before {
1447 | content: '\e87e';
1448 | }
1449 |
1450 | /* '' */
1451 | .icon-down:before {
1452 | content: '\e87f';
1453 | }
1454 |
1455 | /* '' */
1456 | .icon-left:before {
1457 | content: '\e880';
1458 | }
1459 |
1460 | /* '' */
1461 | .icon-right:before {
1462 | content: '\e881';
1463 | }
1464 |
1465 | /* '' */
1466 | .icon-up:before {
1467 | content: '\e882';
1468 | }
1469 |
1470 | /* '' */
1471 | .icon-down-dir:before {
1472 | content: '\e883';
1473 | }
1474 |
1475 | /* '' */
1476 | .icon-left-dir:before {
1477 | content: '\e884';
1478 | }
1479 |
1480 | /* '' */
1481 | .icon-right-dir:before {
1482 | content: '\e885';
1483 | }
1484 |
1485 | /* '' */
1486 | .icon-up-dir:before {
1487 | content: '\e886';
1488 | }
1489 |
1490 | /* '' */
1491 | .icon-down-bold:before {
1492 | content: '\e887';
1493 | }
1494 |
1495 | /* '' */
1496 | .icon-left-bold:before {
1497 | content: '\e888';
1498 | }
1499 |
1500 | /* '' */
1501 | .icon-right-bold:before {
1502 | content: '\e889';
1503 | }
1504 |
1505 | /* '' */
1506 | .icon-up-bold:before {
1507 | content: '\e88a';
1508 | }
1509 |
1510 | /* '' */
1511 | .icon-down-thin:before {
1512 | content: '\e88b';
1513 | }
1514 |
1515 | /* '' */
1516 | .icon-left-thin:before {
1517 | content: '\e88c';
1518 | }
1519 |
1520 | /* '' */
1521 | .icon-right-thin:before {
1522 | content: '\e88d';
1523 | }
1524 |
1525 | /* '' */
1526 | .icon-up-thin:before {
1527 | content: '\e88e';
1528 | }
1529 |
1530 | /* '' */
1531 | .icon-ccw:before {
1532 | content: '\e88f';
1533 | }
1534 |
1535 | /* '' */
1536 | .icon-cw:before {
1537 | content: '\e890';
1538 | }
1539 |
1540 | /* '' */
1541 | .icon-arrows-ccw:before {
1542 | content: '\e891';
1543 | }
1544 |
1545 | /* '' */
1546 | .icon-level-down:before {
1547 | content: '\e892';
1548 | }
1549 |
1550 | /* '' */
1551 | .icon-level-up:before {
1552 | content: '\e893';
1553 | }
1554 |
1555 | /* '' */
1556 | .icon-shuffle:before {
1557 | content: '\e894';
1558 | }
1559 |
1560 | /* '' */
1561 | .icon-loop:before {
1562 | content: '\e895';
1563 | }
1564 |
1565 | /* '' */
1566 | .icon-switch:before {
1567 | content: '\e896';
1568 | }
1569 |
1570 | /* '' */
1571 | .icon-play:before {
1572 | content: '\e897';
1573 | }
1574 |
1575 | /* '' */
1576 | .icon-stop:before {
1577 | content: '\e898';
1578 | }
1579 |
1580 | /* '' */
1581 | .icon-pause:before {
1582 | content: '\e899';
1583 | }
1584 |
1585 | /* '' */
1586 | .icon-record:before {
1587 | content: '\e89a';
1588 | }
1589 |
1590 | /* '' */
1591 | .icon-to-end:before {
1592 | content: '\e89b';
1593 | }
1594 |
1595 | /* '' */
1596 | .icon-to-start:before {
1597 | content: '\e89c';
1598 | }
1599 |
1600 | /* '' */
1601 | .icon-fast-forward:before {
1602 | content: '\e89d';
1603 | }
1604 |
1605 | /* '' */
1606 | .icon-fast-backward:before {
1607 | content: '\e89e';
1608 | }
1609 |
1610 | /* '' */
1611 | .icon-progress-0:before {
1612 | content: '\e89f';
1613 | }
1614 |
1615 | /* '' */
1616 | .icon-progress-1:before {
1617 | content: '\e8a0';
1618 | }
1619 |
1620 | /* '' */
1621 | .icon-progress-2:before {
1622 | content: '\e8a1';
1623 | }
1624 |
1625 | /* '' */
1626 | .icon-progress-3:before {
1627 | content: '\e8a2';
1628 | }
1629 |
1630 | /* '' */
1631 | .icon-target:before {
1632 | content: '\e8a3';
1633 | }
1634 |
1635 | /* '' */
1636 | .icon-palette:before {
1637 | content: '\e8a4';
1638 | }
1639 |
1640 | /* '' */
1641 | .icon-list:before {
1642 | content: '\e8a5';
1643 | }
1644 |
1645 | /* '' */
1646 | .icon-list-add:before {
1647 | content: '\e8a6';
1648 | }
1649 |
1650 | /* '' */
1651 | .icon-signal:before {
1652 | content: '\e8a7';
1653 | }
1654 |
1655 | /* '' */
1656 | .icon-trophy:before {
1657 | content: '\e8a8';
1658 | }
1659 |
1660 | /* '' */
1661 | .icon-battery:before {
1662 | content: '\e8a9';
1663 | }
1664 |
1665 | /* '' */
1666 | .icon-back-in-time:before {
1667 | content: '\e8aa';
1668 | }
1669 |
1670 | /* '' */
1671 | .icon-monitor:before {
1672 | content: '\e8ab';
1673 | }
1674 |
1675 | /* '' */
1676 | .icon-mobile:before {
1677 | content: '\e8ac';
1678 | }
1679 |
1680 | /* '' */
1681 | .icon-network:before {
1682 | content: '\e8ad';
1683 | }
1684 |
1685 | /* '' */
1686 | .icon-cd:before {
1687 | content: '\e8ae';
1688 | }
1689 |
1690 | /* '' */
1691 | .icon-inbox:before {
1692 | content: '\e8af';
1693 | }
1694 |
1695 | /* '' */
1696 | .icon-install:before {
1697 | content: '\e8b0';
1698 | }
1699 |
1700 | /* '' */
1701 | .icon-globe:before {
1702 | content: '\e8b1';
1703 | }
1704 |
1705 | /* '' */
1706 | .icon-cloud:before {
1707 | content: '\e8b2';
1708 | }
1709 |
1710 | /* '' */
1711 | .icon-cloud-thunder:before {
1712 | content: '\e8b3';
1713 | }
1714 |
1715 | /* '' */
1716 | .icon-flash:before {
1717 | content: '\e8b4';
1718 | }
1719 |
1720 | /* '' */
1721 | .icon-moon:before {
1722 | content: '\e8b5';
1723 | }
1724 |
1725 | /* '' */
1726 | .icon-flight:before {
1727 | content: '\e8b6';
1728 | }
1729 |
1730 | /* '' */
1731 | .icon-paper-plane:before {
1732 | content: '\e8b7';
1733 | }
1734 |
1735 | /* '' */
1736 | .icon-leaf:before {
1737 | content: '\e8b8';
1738 | }
1739 |
1740 | /* '' */
1741 | .icon-lifebuoy:before {
1742 | content: '\e8b9';
1743 | }
1744 |
1745 | /* '' */
1746 | .icon-mouse:before {
1747 | content: '\e8ba';
1748 | }
1749 |
1750 | /* '' */
1751 | .icon-briefcase:before {
1752 | content: '\e8bb';
1753 | }
1754 |
1755 | /* '' */
1756 | .icon-suitcase:before {
1757 | content: '\e8bc';
1758 | }
1759 |
1760 | /* '' */
1761 | .icon-dot:before {
1762 | content: '\e8bd';
1763 | }
1764 |
1765 | /* '' */
1766 | .icon-dot-2:before {
1767 | content: '\e8be';
1768 | }
1769 |
1770 | /* '' */
1771 | .icon-dot-3:before {
1772 | content: '\e8bf';
1773 | }
1774 |
1775 | /* '' */
1776 | .icon-brush:before {
1777 | content: '\e8c0';
1778 | }
1779 |
1780 | /* '' */
1781 | .icon-magnet:before {
1782 | content: '\e8c1';
1783 | }
1784 |
1785 | /* '' */
1786 | .icon-infinity:before {
1787 | content: '\e8c2';
1788 | }
1789 |
1790 | /* '' */
1791 | .icon-erase:before {
1792 | content: '\e8c3';
1793 | }
1794 |
1795 | /* '' */
1796 | .icon-chart-pie:before {
1797 | content: '\e8c4';
1798 | }
1799 |
1800 | /* '' */
1801 | .icon-chart-line:before {
1802 | content: '\e8c5';
1803 | }
1804 |
1805 | /* '' */
1806 | .icon-chart-bar:before {
1807 | content: '\e8c6';
1808 | }
1809 |
1810 | /* '' */
1811 | .icon-chart-area:before {
1812 | content: '\e8c7';
1813 | }
1814 |
1815 | /* '' */
1816 | .icon-tape:before {
1817 | content: '\e8c8';
1818 | }
1819 |
1820 | /* '' */
1821 | .icon-graduation-cap:before {
1822 | content: '\e8c9';
1823 | }
1824 |
1825 | /* '' */
1826 | .icon-language:before {
1827 | content: '\e8ca';
1828 | }
1829 |
1830 | /* '' */
1831 | .icon-ticket:before {
1832 | content: '\e8cb';
1833 | }
1834 |
1835 | /* '' */
1836 | .icon-water:before {
1837 | content: '\e8cc';
1838 | }
1839 |
1840 | /* '' */
1841 | .icon-droplet:before {
1842 | content: '\e8cd';
1843 | }
1844 |
1845 | /* '' */
1846 | .icon-air:before {
1847 | content: '\e8ce';
1848 | }
1849 |
1850 | /* '' */
1851 | .icon-credit-card:before {
1852 | content: '\e8cf';
1853 | }
1854 |
1855 | /* '' */
1856 | .icon-floppy:before {
1857 | content: '\e8d0';
1858 | }
1859 |
1860 | /* '' */
1861 | .icon-clipboard:before {
1862 | content: '\e8d1';
1863 | }
1864 |
1865 | /* '' */
1866 | .icon-megaphone:before {
1867 | content: '\e8d2';
1868 | }
1869 |
1870 | /* '' */
1871 | .icon-database:before {
1872 | content: '\e8d3';
1873 | }
1874 |
1875 | /* '' */
1876 | .icon-drive:before {
1877 | content: '\e8d4';
1878 | }
1879 |
1880 | /* '' */
1881 | .icon-bucket:before {
1882 | content: '\e8d5';
1883 | }
1884 |
1885 | /* '' */
1886 | .icon-thermometer:before {
1887 | content: '\e8d6';
1888 | }
1889 |
1890 | /* '' */
1891 | .icon-key:before {
1892 | content: '\e8d7';
1893 | }
1894 |
1895 | /* '' */
1896 | .icon-flow-cascade:before {
1897 | content: '\e8d8';
1898 | }
1899 |
1900 | /* '' */
1901 | .icon-flow-branch:before {
1902 | content: '\e8d9';
1903 | }
1904 |
1905 | /* '' */
1906 | .icon-flow-tree:before {
1907 | content: '\e8da';
1908 | }
1909 |
1910 | /* '' */
1911 | .icon-flow-line:before {
1912 | content: '\e8db';
1913 | }
1914 |
1915 | /* '' */
1916 | .icon-flow-parallel:before {
1917 | content: '\e8dc';
1918 | }
1919 |
1920 | /* '' */
1921 | .icon-rocket:before {
1922 | content: '\e8dd';
1923 | }
1924 |
1925 | /* '' */
1926 | .icon-gauge:before {
1927 | content: '\e8de';
1928 | }
1929 |
1930 | /* '' */
1931 | .icon-traffic-cone:before {
1932 | content: '\e8df';
1933 | }
1934 |
1935 | /* '' */
1936 | .icon-cc:before {
1937 | content: '\e8e0';
1938 | }
1939 |
1940 | /* '' */
1941 | .icon-cc-by:before {
1942 | content: '\e8e1';
1943 | }
1944 |
1945 | /* '' */
1946 | .icon-cc-nc:before {
1947 | content: '\e8e2';
1948 | }
1949 |
1950 | /* '' */
1951 | .icon-cc-nc-eu:before {
1952 | content: '\e8e3';
1953 | }
1954 |
1955 | /* '' */
1956 | .icon-cc-nc-jp:before {
1957 | content: '\e8e4';
1958 | }
1959 |
1960 | /* '' */
1961 | .icon-cc-sa:before {
1962 | content: '\e8e5';
1963 | }
1964 |
1965 | /* '' */
1966 | .icon-cc-nd:before {
1967 | content: '\e8e6';
1968 | }
1969 |
1970 | /* '' */
1971 | .icon-cc-pd:before {
1972 | content: '\e8e7';
1973 | }
1974 |
1975 | /* '' */
1976 | .icon-cc-zero:before {
1977 | content: '\e8e8';
1978 | }
1979 |
1980 | /* '' */
1981 | .icon-cc-share:before {
1982 | content: '\e8e9';
1983 | }
1984 |
1985 | /* '' */
1986 | .icon-cc-remix:before {
1987 | content: '\e8ea';
1988 | }
1989 |
1990 | /* '' */
1991 | .icon-github:before {
1992 | content: '\e8eb';
1993 | }
1994 |
1995 | /* '' */
1996 | .icon-github-circled:before {
1997 | content: '\e8ec';
1998 | }
1999 |
2000 | /* '' */
2001 | .icon-flickr:before {
2002 | content: '\e8ed';
2003 | }
2004 |
2005 | /* '' */
2006 | .icon-flickr-circled:before {
2007 | content: '\e8ee';
2008 | }
2009 |
2010 | /* '' */
2011 | .icon-vimeo:before {
2012 | content: '\e8ef';
2013 | }
2014 |
2015 | /* '' */
2016 | .icon-vimeo-circled:before {
2017 | content: '\e8f0';
2018 | }
2019 |
2020 | /* '' */
2021 | .icon-twitter:before {
2022 | content: '\e8f1';
2023 | }
2024 |
2025 | /* '' */
2026 | .icon-twitter-circled:before {
2027 | content: '\e8f2';
2028 | }
2029 |
2030 | /* '' */
2031 | .icon-facebook:before {
2032 | content: '\e8f3';
2033 | }
2034 |
2035 | /* '' */
2036 | .icon-facebook-circled:before {
2037 | content: '\e8f4';
2038 | }
2039 |
2040 | /* '' */
2041 | .icon-facebook-squared:before {
2042 | content: '\e8f5';
2043 | }
2044 |
2045 | /* '' */
2046 | .icon-gplus:before {
2047 | content: '\e8f6';
2048 | }
2049 |
2050 | /* '' */
2051 | .icon-gplus-circled:before {
2052 | content: '\e8f7';
2053 | }
2054 |
2055 | /* '' */
2056 | .icon-pinterest:before {
2057 | content: '\e8f8';
2058 | }
2059 |
2060 | /* '' */
2061 | .icon-pinterest-circled:before {
2062 | content: '\e8f9';
2063 | }
2064 |
2065 | /* '' */
2066 | .icon-tumblr:before {
2067 | content: '\e8fa';
2068 | }
2069 |
2070 | /* '' */
2071 | .icon-tumblr-circled:before {
2072 | content: '\e8fb';
2073 | }
2074 |
2075 | /* '' */
2076 | .icon-linkedin:before {
2077 | content: '\e8fc';
2078 | }
2079 |
2080 | /* '' */
2081 | .icon-linkedin-circled:before {
2082 | content: '\e8fd';
2083 | }
2084 |
2085 | /* '' */
2086 | .icon-dribbble:before {
2087 | content: '\e8fe';
2088 | }
2089 |
2090 | /* '' */
2091 | .icon-dribbble-circled:before {
2092 | content: '\e8ff';
2093 | }
2094 |
2095 | /* '' */
2096 | .icon-stumbleupon:before {
2097 | content: '\e900';
2098 | }
2099 |
2100 | /* '' */
2101 | .icon-stumbleupon-circled:before {
2102 | content: '\e901';
2103 | }
2104 |
2105 | /* '' */
2106 | .icon-lastfm:before {
2107 | content: '\e902';
2108 | }
2109 |
2110 | /* '' */
2111 | .icon-lastfm-circled:before {
2112 | content: '\e903';
2113 | }
2114 |
2115 | /* '' */
2116 | .icon-rdio:before {
2117 | content: '\e904';
2118 | }
2119 |
2120 | /* '' */
2121 | .icon-rdio-circled:before {
2122 | content: '\e905';
2123 | }
2124 |
2125 | /* '' */
2126 | .icon-spotify:before {
2127 | content: '\e906';
2128 | }
2129 |
2130 | /* '' */
2131 | .icon-spotify-circled:before {
2132 | content: '\e907';
2133 | }
2134 |
2135 | /* '' */
2136 | .icon-qq:before {
2137 | content: '\e908';
2138 | }
2139 |
2140 | /* '' */
2141 | .icon-instagram:before {
2142 | content: '\e909';
2143 | }
2144 |
2145 | /* '' */
2146 | .icon-dropbox:before {
2147 | content: '\e90a';
2148 | }
2149 |
2150 | /* '' */
2151 | .icon-evernote:before {
2152 | content: '\e90b';
2153 | }
2154 |
2155 | /* '' */
2156 | .icon-flattr:before {
2157 | content: '\e90c';
2158 | }
2159 |
2160 | /* '' */
2161 | .icon-skype:before {
2162 | content: '\e90d';
2163 | }
2164 |
2165 | /* '' */
2166 | .icon-skype-circled:before {
2167 | content: '\e90e';
2168 | }
2169 |
2170 | /* '' */
2171 | .icon-renren:before {
2172 | content: '\e90f';
2173 | }
2174 |
2175 | /* '' */
2176 | .icon-sina-weibo:before {
2177 | content: '\e910';
2178 | }
2179 |
2180 | /* '' */
2181 | .icon-paypal:before {
2182 | content: '\e911';
2183 | }
2184 |
2185 | /* '' */
2186 | .icon-picasa:before {
2187 | content: '\e912';
2188 | }
2189 |
2190 | /* '' */
2191 | .icon-soundcloud:before {
2192 | content: '\e913';
2193 | }
2194 |
2195 | /* '' */
2196 | .icon-mixi:before {
2197 | content: '\e914';
2198 | }
2199 |
2200 | /* '' */
2201 | .icon-behance:before {
2202 | content: '\e915';
2203 | }
2204 |
2205 | /* '' */
2206 | .icon-google-circles:before {
2207 | content: '\e916';
2208 | }
2209 |
2210 | /* '' */
2211 | .icon-vkontakte:before {
2212 | content: '\e917';
2213 | }
2214 |
2215 | /* '' */
2216 | .icon-smashing:before {
2217 | content: '\e918';
2218 | }
2219 |
2220 | /* '' */
2221 | .icon-sweden:before {
2222 | content: '\e919';
2223 | }
2224 |
2225 | /* '' */
2226 | .icon-db-shape:before {
2227 | content: '\e91a';
2228 | }
2229 |
2230 | /* '' */
2231 | .icon-logo-db:before {
2232 | content: '\e91b';
2233 | }
2234 |
2235 | /* '' */
2236 | table {
2237 | width: 100%;
2238 | border: 0;
2239 | border-collapse: separate;
2240 | font-size: 12px;
2241 | text-align: left;
2242 | }
2243 |
2244 | thead {
2245 | background-color: #f5f5f4;
2246 | }
2247 |
2248 | tbody {
2249 | background-color: #fff;
2250 | }
2251 |
2252 | .table-striped tr:nth-child(even) {
2253 | background-color: #f5f5f4;
2254 | }
2255 |
2256 | tr:active,
2257 | .table-striped tr:active:nth-child(even) {
2258 | color: #fff;
2259 | background-color: #116cd6;
2260 | }
2261 |
2262 | thead tr:active {
2263 | color: #333;
2264 | background-color: #f5f5f4;
2265 | }
2266 |
2267 | th {
2268 | font-weight: normal;
2269 | border-right: 1px solid #ddd;
2270 | border-bottom: 1px solid #ddd;
2271 | }
2272 |
2273 | th,
2274 | td {
2275 | padding: 2px 15px;
2276 | white-space: nowrap;
2277 | overflow: hidden;
2278 | text-overflow: ellipsis;
2279 | }
2280 | th:last-child,
2281 | td:last-child {
2282 | border-right: 0;
2283 | }
2284 |
2285 | .tab-group {
2286 | margin-top: -1px;
2287 | display: flex;
2288 | border-top: 1px solid #989698;
2289 | border-bottom: 1px solid #989698;
2290 | }
2291 |
2292 | .tab-item {
2293 | position: relative;
2294 | flex: 1;
2295 | padding: 3px;
2296 | font-size: 12px;
2297 | text-align: center;
2298 | border-left: 1px solid #989698;
2299 | background-color: #b8b6b8;
2300 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #b8b6b8), color-stop(100%, #b0aeb0));
2301 | background-image: -webkit-linear-gradient(top, #b8b6b8 0%, #b0aeb0 100%);
2302 | background-image: linear-gradient(to bottom, #b8b6b8 0%, #b0aeb0 100%);
2303 | }
2304 | .tab-item:first-child {
2305 | border-left: 0;
2306 | }
2307 | .tab-item.active {
2308 | background-color: #d4d2d4;
2309 | background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #d4d2d4), color-stop(100%, #cccacc));
2310 | background-image: -webkit-linear-gradient(top, #d4d2d4 0%, #cccacc 100%);
2311 | background-image: linear-gradient(to bottom, #d4d2d4 0%, #cccacc 100%);
2312 | }
2313 | .tab-item .icon-close-tab {
2314 | position: absolute;
2315 | top: 50%;
2316 | left: 5px;
2317 | width: 15px;
2318 | height: 15px;
2319 | font-size: 15px;
2320 | line-height: 15px;
2321 | text-align: center;
2322 | color: #666;
2323 | opacity: 0;
2324 | transition: opacity .1s linear, background-color .1s linear;
2325 | border-radius: 3px;
2326 | transform: translateY(-50%);
2327 | z-index: 10;
2328 | }
2329 | .tab-item:after {
2330 | position: absolute;
2331 | top: 0;
2332 | right: 0;
2333 | bottom: 0;
2334 | left: 0;
2335 | content: "";
2336 | background-color: rgba(0, 0, 0, 0.08);
2337 | opacity: 0;
2338 | transition: opacity .1s linear;
2339 | z-index: 1;
2340 | }
2341 | .tab-item:hover:not(.active):after {
2342 | opacity: 1;
2343 | }
2344 | .tab-item:hover .icon-close-tab {
2345 | opacity: 1;
2346 | }
2347 | .tab-item .icon-close-tab:hover {
2348 | background-color: rgba(0, 0, 0, 0.08);
2349 | }
2350 |
2351 | .tab-item-fixed {
2352 | flex: none;
2353 | padding: 3px 10px;
2354 | }
2355 |
--------------------------------------------------------------------------------
/app/vendor/roboto.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Roboto';
3 | font-style: normal;
4 | font-weight: 300;
5 | src: url('../fonts/Roboto-Light.woff2') format('woff2'),
6 | url('../fonts/Roboto-Light.woff') format('woff'),
7 | url('../fonts/Roboto-Light.ttf') format('truetype');
8 | }
--------------------------------------------------------------------------------
/bin/cli.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | 'use strict';
3 |
4 |
5 | /**
6 | * Modules
7 | * Node
8 | * @constant
9 | */
10 | const path = require('path');
11 | const child_process = require('child_process');
12 |
13 | /**
14 | * Modules
15 | * External
16 | * @constant
17 | */
18 | const appRootPath = require('app-root-path');
19 | const parseSemver = require('parse-semver');
20 | const simpleReload = require('simple-reload');
21 | const tryRequire = require('try-require');
22 | const _ = require('lodash');
23 |
24 | /**
25 | * Modules
26 | * Configuration
27 | */
28 | appRootPath.setPath(path.join(__dirname, '..'));
29 |
30 | /**
31 | * Modules
32 | * Internal
33 | * @constant
34 | */
35 | const packageJson = require(path.join(appRootPath.path, 'package.json'));
36 | const logger = require(path.join(appRootPath.path, 'lib', 'logger'))({ namespace: packageJson.productName, timestamp: false });
37 |
38 | /**
39 | * Filesystem
40 | * @constant
41 | * @default
42 | */
43 | const applicationPath = path.join(appRootPath.path, packageJson.main);
44 |
45 |
46 | /**
47 | * Install required Dependencies
48 | * @param {Object} dependencyObject - package.json dependency map
49 | * @param {Function} callback - Callback
50 | */
51 | let installDependencies = (dependencyObject, callback) => {
52 | logger.debug('installDependencies', dependencyObject);
53 |
54 | let cb = callback || function () {};
55 |
56 | // Generate list of dependencies
57 | let dependencyNameList = Object.keys(dependencyObject);
58 |
59 | // Determine uninstalled dependencies
60 | dependencyNameList = dependencyNameList.filter((dependencyName) => {
61 | const isInstalled = tryRequire.resolve(dependencyName);
62 | return !isInstalled;
63 | });
64 |
65 | // Generate package names
66 | let dependencyList = dependencyNameList.map((dependencyName) => {
67 | const foundVersion = dependencyObject[dependencyName];
68 | let parsedVersion;
69 |
70 | try {
71 | parsedVersion = parseSemver(`${dependencyName}@${foundVersion}`).version;
72 | } catch(err) {
73 | parsedVersion = foundVersion;
74 | }
75 |
76 | return `${dependencyName}@${parsedVersion}`;
77 | });
78 |
79 | // Install
80 | if (dependencyList.length > 0) {
81 | /**
82 | * npm install
83 | */
84 | logger.info('installing dependencies:', `"${dependencyList.join('", "')}"`);
85 |
86 | child_process.execSync(`npm install ${dependencyList.join(' ')} --loglevel silent`, {
87 | cwd: appRootPath.path,
88 | maxBuffer: (20000 * 1024),
89 | stdio: 'inherit'
90 | });
91 |
92 | logger.info(`installing dependencies complete.`);
93 | }
94 |
95 | cb(null);
96 | };
97 |
98 | /**
99 | * Launch App
100 | * @param {String} electronPath - Path to Electron
101 | * @param {String} applicationPath - Path to App
102 | */
103 | let launchApplication = (electronPath, applicationPath) => {
104 | logger.debug('launchApplication');
105 |
106 | /**
107 | * Launch application
108 | */
109 | logger.info('application location:', `"${appRootPath.path}"`);
110 | logger.info('electron installation:', `"${path.relative(appRootPath.path, electronPath)}"`);
111 |
112 | const child = child_process.spawn(electronPath, [applicationPath], {
113 | cwd: appRootPath.path,
114 | detached: true,
115 | stdio: 'ignore'
116 | });
117 |
118 | /**
119 | * Fork process
120 | */
121 | child.unref();
122 |
123 | /**
124 | * Exit
125 | */
126 | process.on('exit', () => {
127 | logger.info('successfully started.');
128 | });
129 |
130 | process.exit(0);
131 | };
132 |
133 |
134 | /**
135 | * Main
136 | */
137 | let main = () => {
138 | logger.debug('main');
139 |
140 | installDependencies(packageJson.devDependencies, () => {
141 | let interval = setInterval(() => {
142 | const electron = simpleReload('electron');
143 |
144 | if (!electron) {
145 | return;
146 | }
147 |
148 | launchApplication(electron, applicationPath);
149 | clearInterval(interval);
150 | }, 2000);
151 | });
152 | };
153 |
154 |
155 | /**
156 | * Init
157 | */
158 | if (require.main === module) {
159 | main();
160 | }
161 |
--------------------------------------------------------------------------------
/gulpfile.babel.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 |
4 | /**
5 | * Modules
6 | * Node
7 | * @constant
8 | */
9 | const path = require('path');
10 |
11 | /**
12 | * Modules
13 | * Electron
14 | * @constant
15 | */
16 | const electron = require('electron');
17 | const { app } = electron;
18 |
19 | /**
20 | * Modules
21 | * External
22 | * @constant
23 | */
24 | const appRootPath = require('app-root-path');
25 | const electronConnect = require('electron-connect');
26 | const gulp = require('gulp');
27 | const minimist = require('minimist');
28 |
29 |
30 | /**
31 | * Modules
32 | * Configuration
33 | */
34 | appRootPath.setPath(path.join(__dirname));
35 |
36 | /**
37 | * Modules
38 | * Internal
39 | * @constant
40 | */
41 | const logger = require(path.join(appRootPath.path, 'lib', 'logger'))({ write: true });
42 | const packageJson = require(path.join(appRootPath.path, 'package.json'));
43 |
44 |
45 | /**
46 | * Filesystem
47 | * @constant
48 | * @default
49 | */
50 | const applicationPath = path.join(appRootPath['path'], packageJson.main);
51 |
52 | /**
53 | * Electron Connect Server
54 | * Init
55 | */
56 | const electronConnectServer = electronConnect.server.create({
57 | //logLevel: 2,
58 | //verbose: true,
59 | stopOnClose: true,
60 | electron: electron,
61 | path: applicationPath,
62 | useGlobalElectron: false
63 | });
64 |
65 | /**
66 | * Electron Connect Server
67 | * Files
68 | */
69 | let appSources = {
70 | main: [
71 | path.join(appRootPath['path'], 'app', 'fonts', '**', '*.*'),
72 | path.join(appRootPath['path'], 'app', 'html', '**', '*.*'),
73 | path.join(appRootPath['path'], 'app', 'images', '**', '*.*'),
74 | path.join(appRootPath['path'], 'app', 'scripts', 'main', '**', '*.*'),
75 | path.join(appRootPath['path'], 'app', 'styles', '**', '*.*'),
76 | path.join(appRootPath['path'], 'package.json')
77 | ],
78 | renderer: [
79 | path.join(appRootPath['path'], 'app', 'scripts', 'renderer', '**', '*.*')
80 | ]
81 | };
82 |
83 |
84 | /**
85 | * Server
86 | * start
87 | */
88 | gulp.task('livereload', () => {
89 | let globalArgvObj;
90 | let npmArgvObj;
91 | try { globalArgvObj = minimist(process.argv); } catch (err) {}
92 | try { npmArgvObj = minimist(JSON.parse(process.env.npm_config_argv).original); } catch (err) {}
93 |
94 | logger.info(globalArgvObj)
95 | logger.info(npmArgvObj)
96 |
97 | electronConnectServer.start();
98 | gulp.watch(appSources.main, ['main:restart']);
99 | gulp.watch(appSources.renderer, ['renderer:reload']);
100 | });
101 |
102 | /**
103 | * Main Process
104 | * restart
105 | */
106 | gulp.task('main:restart', (callback) => {
107 | electronConnectServer.restart();
108 | callback();
109 | });
110 |
111 | /**
112 | * Renderer Process
113 | * restart
114 | */
115 | gulp.task('renderer:reload', (callback) => {
116 | electronConnectServer.reload();
117 | callback();
118 | });
119 |
120 |
121 | gulp.task('default', ['livereload']);
122 |
123 |
--------------------------------------------------------------------------------
/icons/darwin/background-setup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/icons/darwin/background-setup.png
--------------------------------------------------------------------------------
/icons/darwin/icon-setup.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/icons/darwin/icon-setup.icns
--------------------------------------------------------------------------------
/icons/darwin/icon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/icons/darwin/icon.icns
--------------------------------------------------------------------------------
/icons/linux/1024x1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/icons/linux/1024x1024.png
--------------------------------------------------------------------------------
/icons/linux/128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/icons/linux/128x128.png
--------------------------------------------------------------------------------
/icons/linux/16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/icons/linux/16x16.png
--------------------------------------------------------------------------------
/icons/linux/24x24.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/icons/linux/24x24.png
--------------------------------------------------------------------------------
/icons/linux/256x256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/icons/linux/256x256.png
--------------------------------------------------------------------------------
/icons/linux/32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/icons/linux/32x32.png
--------------------------------------------------------------------------------
/icons/linux/48x48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/icons/linux/48x48.png
--------------------------------------------------------------------------------
/icons/linux/512x512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/icons/linux/512x512.png
--------------------------------------------------------------------------------
/icons/linux/64x64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/icons/linux/64x64.png
--------------------------------------------------------------------------------
/icons/linux/96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/icons/linux/96x96.png
--------------------------------------------------------------------------------
/icons/linux/icon-setup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/icons/linux/icon-setup.png
--------------------------------------------------------------------------------
/icons/linux/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/icons/linux/icon.png
--------------------------------------------------------------------------------
/icons/win32/background-setup.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/icons/win32/background-setup.bmp
--------------------------------------------------------------------------------
/icons/win32/header-setup.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/icons/win32/header-setup.bmp
--------------------------------------------------------------------------------
/icons/win32/icon-setup.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/icons/win32/icon-setup.ico
--------------------------------------------------------------------------------
/icons/win32/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/icons/win32/icon.ico
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "desktop-dimmer",
3 | "productName": "Desktop Dimmer",
4 | "version": "4.0.4",
5 | "description": "Enables darker-than-dark Desktop dimming for your displays",
6 | "license": "MIT",
7 | "homepage": "https://sidneys.github.io/desktop-dimmer",
8 | "author": {
9 | "name": "sidneys",
10 | "email": "sidneys.github.io@outlook.com",
11 | "url": "https://github.com/sidneys"
12 | },
13 | "repository": {
14 | "type": "git",
15 | "url": "https://github.com/sidneys/desktop-dimmer.git"
16 | },
17 | "bugs": {
18 | "url": "https://github.com/sidneys/desktop-dimmer/issues"
19 | },
20 | "engines": {
21 | "node": ">=9.0.0"
22 | },
23 | "os": [
24 | "darwin",
25 | "win32",
26 | "linux"
27 | ],
28 | "keywords": [
29 | "desktop dimmer",
30 | "shades",
31 | "dimmer",
32 | "shade",
33 | "turn off the lights"
34 | ],
35 | "preferGlobal": true,
36 | "dependencies": {
37 | "@sidneys/dom-tools": "^1.0.5",
38 | "@sidneys/electron-build": "^1.1.5",
39 | "@sidneys/electron-deploy-github": "^1.0.5",
40 | "@sidneys/electron-localsetup": "^1.0.6",
41 | "@sidneys/is-env": "^1.0.4",
42 | "@sidneys/logger": "^1.0.9",
43 | "@sidneys/platform-tools": "^1.0.1",
44 | "@sidneys/releasenotes": "^1.0.10",
45 | "app-root-path": "^2.0.1",
46 | "appdirectory": "^0.1.0",
47 | "auto-launch": "git://github.com/sidneys/node-auto-launch.git#master",
48 | "babel-cli": "^6.26.0",
49 | "babel-preset-electron": "^1.4.15",
50 | "chalk": "^2.3.0",
51 | "chalkline": "0.0.5",
52 | "electron-editor-context-menu": "^1.1.1",
53 | "electron-settings": "^3.1.4",
54 | "electron-updater": "^2.16.1",
55 | "file-type": "^7.2.0",
56 | "file-url": "^2.0.2",
57 | "filesize": "^3.5.11",
58 | "fs-extra": "^4.0.2",
59 | "globby": "^6.1.0",
60 | "hammerjs": "^2.0.8",
61 | "is-reachable": "^2.3.3",
62 | "keypath": "0.0.1",
63 | "lodash": "^4.17.4",
64 | "menubar": "git://github.com/sidneys/menubar.git#add-taskbar-support",
65 | "minimist": "^1.2.0",
66 | "moment": "^2.19.2",
67 | "moment-duration-format": "^1.3.0",
68 | "parse-domain": "^1.1.0",
69 | "parse-semver": "^1.1.1",
70 | "present": "^1.0.0",
71 | "query-string": "^5.0.1",
72 | "random-int": "^1.0.0",
73 | "read-chunk": "^2.1.0",
74 | "remove-markdown": "^0.2.2",
75 | "request": "^2.83.0",
76 | "semver-compare": "^1.0.0",
77 | "simple-reload": "0.0.4",
78 | "tinycolor2": "^1.4.1",
79 | "try-require": "latest"
80 | },
81 | "devDependencies": {
82 | "docdash": "^0.4.0",
83 | "electron": "1.4.15",
84 | "electron-builder": "19.45.4",
85 | "electron-connect": "^0.6.2",
86 | "electron-prebuilt-compile": "1.4.15",
87 | "eslint": "^4.11.0",
88 | "gulp": "^3.9.1",
89 | "gulp-load-plugins": "^1.5.0",
90 | "jsdoc": "^3.5.5",
91 | "minami": "^1.2.3"
92 | },
93 | "main": "./app/scripts/main/components/application.js",
94 | "bin": {
95 | "desktop-dimmer": "./bin/cli.js"
96 | },
97 | "scripts": {
98 | "build": "node ./node_modules/@sidneys/electron-build/index.js",
99 | "deploy": "node ./node_modules/@sidneys/electron-deploy-github/index.js",
100 | "docs": "./node_modules/.bin/jsdoc --verbose --configure .jsdoc.json",
101 | "livereload": "./node_modules/.bin/gulp",
102 | "localsetup": "node ./node_modules/@sidneys/electron-localsetup/index.js",
103 | "releasenotes": "node ./node_modules/@sidneys/releasenotes/index.js",
104 | "start": "./node_modules/.bin/electron ./app/scripts/main/components/application.js"
105 | },
106 | "build": {
107 | "appId": "de.sidneys.desktop-dimmer",
108 | "asar": true,
109 | "compression": "maximum",
110 | "directories": {
111 | "buildResources": "./build/staging",
112 | "output": "./build/output"
113 | },
114 | "files": [
115 | "!.appveyor.yml",
116 | "!.babelrc",
117 | "!.editorconfig",
118 | "!.eslintignore",
119 | "!.eslintrc",
120 | "!.github/**",
121 | "!.gitignore",
122 | "!.npmignore",
123 | "!.travis.yml",
124 | "!build/**",
125 | "!gh-pages/**",
126 | "!resources/**",
127 | "!website/**"
128 | ],
129 | "mac": {
130 | "category": "public.app-category.utilities",
131 | "icon": "./icons/darwin/icon.icns",
132 | "target": [
133 | "dir",
134 | "dmg",
135 | "zip"
136 | ]
137 | },
138 | "win": {
139 | "icon": "./icons/win32/icon.ico",
140 | "target": [
141 | "nsis"
142 | ]
143 | },
144 | "linux": {
145 | "category": "Utility",
146 | "icon": "./icons/linux",
147 | "target": [
148 | "AppImage",
149 | "deb",
150 | "pacman",
151 | "rpm"
152 | ]
153 | },
154 | "dmg": {
155 | "background": "./icons/darwin/background-setup.png",
156 | "icon": "./icons/darwin/icon-setup.icns"
157 | },
158 | "nsis": {
159 | "artifactName": "${name}-${version}-setup.${ext}",
160 | "deleteAppDataOnUninstall": true,
161 | "installerHeader": "./icons/win32/header-setup.bmp",
162 | "installerHeaderIcon": "./icons/win32/icon.ico",
163 | "installerIcon": "./icons/win32/icon-setup.ico",
164 | "installerSidebar": "./icons/win32/background-setup.bmp",
165 | "oneClick": false,
166 | "runAfterFinish": false
167 | }
168 | }
169 | }
170 |
--------------------------------------------------------------------------------
/resources/graphics/icon.ai:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/resources/graphics/icon.ai
--------------------------------------------------------------------------------
/resources/graphics/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/resources/graphics/icon.png
--------------------------------------------------------------------------------
/resources/screenshots/screenshot-macos.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/resources/screenshots/screenshot-macos.png
--------------------------------------------------------------------------------
/resources/screenshots/screenshot-win32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sidneys/desktop-dimmer/e1a1d2911a25a7b69314710b2a29ff0e0ded744e/resources/screenshots/screenshot-win32.png
--------------------------------------------------------------------------------