├── .github ├── icons │ ├── contributed-flame-mac-white.svg │ ├── contributed-flame-mac.svg │ ├── contributed-octocat-mac-white.svg │ ├── contributed-octocat-mac.svg │ ├── contributed-tile-mac-white.svg │ ├── contributed-tile-mac.svg │ ├── icon.svg │ ├── pending-flame-mac-white.svg │ ├── pending-flame-mac.svg │ ├── pending-octocat-mac-white.svg │ ├── pending-octocat-mac.svg │ ├── pending-tile-mac-white.svg │ ├── pending-tile-mac.svg │ ├── screenshot.png │ ├── streaking-flame-mac-white.svg │ ├── streaking-flame-mac.svg │ ├── streaking-octocat-mac-white.svg │ ├── streaking-octocat-mac.svg │ ├── streaking-tile-mac-white.svg │ └── streaking-tile-mac.svg └── workflows │ ├── ci.yml │ └── publish.yml ├── .gitignore ├── .husky ├── commit-msg ├── pre-commit └── pre-push ├── CHANGELOG.md ├── LICENSE ├── README.md ├── biome.json ├── forge.config.ts ├── package-lock.json ├── package.json ├── postcss.config.js ├── src ├── icons │ ├── app │ │ ├── icon.icns │ │ └── icon.ico │ ├── index.ts │ ├── linux │ │ ├── flame │ │ │ ├── contributed.png │ │ │ ├── error.png │ │ │ ├── pending.png │ │ │ └── streaking.png │ │ ├── octocat │ │ │ ├── contributed.png │ │ │ ├── error.png │ │ │ ├── pending.png │ │ │ └── streaking.png │ │ └── tile │ │ │ ├── contributed.png │ │ │ ├── error.png │ │ │ ├── pending.png │ │ │ └── streaking.png │ ├── macos │ │ ├── flame │ │ │ ├── contributed@2x.png │ │ │ ├── error@2x.png │ │ │ ├── pending@2x.png │ │ │ └── streaking@2x.png │ │ ├── octocat │ │ │ ├── contributed@2x.png │ │ │ ├── error@2x.png │ │ │ ├── pending@2x.png │ │ │ └── streaking@2x.png │ │ └── tile │ │ │ ├── contributed@2x.png │ │ │ ├── error@2x.png │ │ │ ├── pending@2x.png │ │ │ └── streaking@2x.png │ └── windows │ │ ├── flame │ │ ├── contributed.ico │ │ ├── error.ico │ │ ├── pending.ico │ │ └── streaking.ico │ │ ├── octocat │ │ ├── contributed.ico │ │ ├── error.ico │ │ ├── pending.ico │ │ └── streaking.ico │ │ └── tile │ │ ├── contributed.ico │ │ ├── error.ico │ │ ├── pending.ico │ │ └── streaking.ico ├── main │ ├── logger.ts │ ├── main.ts │ ├── menu.ts │ ├── store.ts │ └── tray-menu.ts └── renderer │ ├── index.html │ ├── preload.ts │ ├── renderer.ts │ └── styles.css ├── tailwind.config.js ├── tsconfig.json ├── webpack.main.config.ts ├── webpack.plugins.ts ├── webpack.renderer.config.ts └── webpack.rules.ts /.github/icons/contributed-flame-mac-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.github/icons/contributed-flame-mac.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.github/icons/contributed-octocat-mac-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.github/icons/contributed-octocat-mac.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.github/icons/contributed-tile-mac-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/icons/contributed-tile-mac.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/icons/icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /.github/icons/pending-flame-mac-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.github/icons/pending-flame-mac.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.github/icons/pending-octocat-mac-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.github/icons/pending-octocat-mac.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.github/icons/pending-tile-mac-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/icons/pending-tile-mac.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/icons/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/.github/icons/screenshot.png -------------------------------------------------------------------------------- /.github/icons/streaking-flame-mac-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.github/icons/streaking-flame-mac.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /.github/icons/streaking-octocat-mac-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.github/icons/streaking-octocat-mac.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.github/icons/streaking-tile-mac-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.github/icons/streaking-tile-mac.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: actions/setup-node@v4 18 | with: 19 | node-version: 22.x 20 | - run: npm clean-install 21 | - run: npm run lint 22 | - run: npm run package # build and package 23 | 24 | release: 25 | needs: build 26 | if: github.event_name == 'push' && github.ref == 'refs/heads/main' 27 | runs-on: ubuntu-latest 28 | permissions: 29 | contents: write # to be able to publish a GitHub release 30 | issues: write # to be able to comment on released issues 31 | pull-requests: write # to be able to comment on released pull requests 32 | id-token: write # to enable use of OIDC for npm provenance 33 | steps: 34 | - uses: actions/checkout@v4 35 | - uses: actions/setup-node@v4 36 | with: 37 | node-version: 22.x 38 | - run: npm clean-install 39 | - run: npm audit signatures 40 | - run: npx semantic-release 41 | env: 42 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 43 | HUSKY: 0 44 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: publish 2 | 3 | on: workflow_dispatch 4 | 5 | 6 | permissions: 7 | contents: write 8 | 9 | jobs: 10 | release-linux: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - uses: actions/setup-node@v4 15 | with: 16 | node-version: 22.x 17 | - run: npm clean-install --include=dev 18 | - run: npm run publish 19 | env: 20 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 21 | 22 | release-mac: 23 | runs-on: macos-latest 24 | steps: 25 | - uses: actions/checkout@v4 26 | - uses: actions/setup-node@v4 27 | with: 28 | node-version: 22.x 29 | - run: npm clean-install --include=dev 30 | - run: npm run publish 31 | env: 32 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 33 | 34 | release-windows: 35 | runs-on: windows-latest 36 | steps: 37 | - uses: actions/checkout@v4 38 | - uses: actions/setup-node@v4 39 | with: 40 | node-version: 22.x 41 | - run: npm clean-install --include=dev 42 | - run: npm run publish 43 | env: 44 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | .DS_Store 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # TypeScript cache 43 | *.tsbuildinfo 44 | 45 | # Optional npm cache directory 46 | .npm 47 | 48 | # Optional eslint cache 49 | .eslintcache 50 | 51 | # Optional REPL history 52 | .node_repl_history 53 | 54 | # Output of 'npm pack' 55 | *.tgz 56 | 57 | # Yarn Integrity file 58 | .yarn-integrity 59 | 60 | # dotenv environment variables file 61 | .env 62 | .env.test 63 | 64 | # parcel-bundler cache (https://parceljs.org/) 65 | .cache 66 | 67 | # next.js build output 68 | .next 69 | 70 | # nuxt.js build output 71 | .nuxt 72 | 73 | # vuepress build output 74 | .vuepress/dist 75 | 76 | # Serverless directories 77 | .serverless/ 78 | 79 | # FuseBox cache 80 | .fusebox/ 81 | 82 | # DynamoDB Local files 83 | .dynamodb/ 84 | 85 | # Webpack 86 | .webpack/ 87 | 88 | # Vite 89 | .vite/ 90 | 91 | # Electron-Forge 92 | out/ 93 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | npx --no -- commitlint --edit "$1" 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npx --no -- lint-staged 2 | -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | npm run lint && npm run package 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [2.6.1](https://github.com/jamieweavis/streaker/compare/v2.6.0...v2.6.1) (2025-04-14) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * update description ([36ce895](https://github.com/jamieweavis/streaker/commit/36ce8952587f1879ca7dfbb44ab74ceeab92a8d8)) 7 | 8 | # [2.6.0](https://github.com/jamieweavis/streaker/compare/v2.5.2...v2.6.0) (2025-03-29) 9 | 10 | 11 | ### Features 12 | 13 | * add at risk indicator to previous streak menu item ([3c2571d](https://github.com/jamieweavis/streaker/commit/3c2571d67a6c5d8297d9fad0f6a33a4cf362ceda)) 14 | 15 | ## [2.5.2](https://github.com/jamieweavis/streaker/compare/v2.5.1...v2.5.2) (2025-03-24) 16 | 17 | 18 | ### Bug Fixes 19 | 20 | * upgrade to contribution v7 ([3aa7144](https://github.com/jamieweavis/streaker/commit/3aa7144309394f028d8cce8e429ee4b641127151)) 21 | 22 | ## [2.5.1](https://github.com/jamieweavis/streaker/compare/v2.5.0...v2.5.1) (2025-03-20) 23 | 24 | 25 | ### Bug Fixes 26 | 27 | * reminder notification checkbox default state ([83b0de8](https://github.com/jamieweavis/streaker/commit/83b0de885aa87a63422a6b2f5c5c930d655d5729)) 28 | 29 | # [2.5.0](https://github.com/jamieweavis/streaker/compare/v2.4.3...v2.5.0) (2025-03-20) 30 | 31 | 32 | ### Features 33 | 34 | * open preferences window when app is reopened and already running ([d00b086](https://github.com/jamieweavis/streaker/commit/d00b086372b43aeb635fe31b989dd520a8f3c1c0)) 35 | 36 | ## [2.4.3](https://github.com/jamieweavis/streaker/compare/v2.4.2...v2.4.3) (2025-03-18) 37 | 38 | 39 | ### Bug Fixes 40 | 41 | * increase preferences window width to avoid line wrap which causes scroll ([775c716](https://github.com/jamieweavis/streaker/commit/775c716f783c5cf4f9ab8a1fdc75a16609b34d56)) 42 | 43 | ## [2.4.2](https://github.com/jamieweavis/streaker/compare/v2.4.1...v2.4.2) (2025-03-18) 44 | 45 | 46 | ### Bug Fixes 47 | 48 | * add linux specific tray icons ([673d9b0](https://github.com/jamieweavis/streaker/commit/673d9b0eaa31eff51182dfb4d8481a6fa5c0e3e3)) 49 | 50 | ## [2.4.1](https://github.com/jamieweavis/streaker/compare/v2.4.0...v2.4.1) (2025-03-17) 51 | 52 | 53 | ### Bug Fixes 54 | 55 | * tile icon theme alpha issue ([abc3797](https://github.com/jamieweavis/streaker/commit/abc37978d2b36856ae8eab5216682e07519309e3)) 56 | 57 | # [2.4.0](https://github.com/jamieweavis/streaker/compare/v2.3.3...v2.4.0) (2025-03-17) 58 | 59 | 60 | ### Features 61 | 62 | * improve tile icon theme to be simpler & clearer ([ed5e361](https://github.com/jamieweavis/streaker/commit/ed5e361aac4f299402d7ce4131dcaa6673cc9dc6)) 63 | 64 | ## [2.3.3](https://github.com/jamieweavis/streaker/compare/v2.3.2...v2.3.3) (2025-03-15) 65 | 66 | 67 | ### Bug Fixes 68 | 69 | * test executable name fix for linux ([9dfd176](https://github.com/jamieweavis/streaker/commit/9dfd176119a54de2f8682abd540f2263a33646e2)) 70 | 71 | ## [2.3.3](https://github.com/jamieweavis/streaker/compare/v2.3.2...v2.3.3) (2025-03-15) 72 | 73 | 74 | ### Bug Fixes 75 | 76 | * test executable name fix for linux ([9dfd176](https://github.com/jamieweavis/streaker/commit/9dfd176119a54de2f8682abd540f2263a33646e2)) 77 | 78 | ## [2.3.2](https://github.com/jamieweavis/streaker/compare/v2.3.1...v2.3.2) (2025-03-15) 79 | 80 | 81 | ### Bug Fixes 82 | 83 | * executable name capitalisation ([8ac19b6](https://github.com/jamieweavis/streaker/commit/8ac19b6049c335901567874f28492fd5c56702dc)) 84 | 85 | ## [2.3.1](https://github.com/jamieweavis/streaker/compare/v2.3.0...v2.3.1) (2025-03-15) 86 | 87 | 88 | ### Bug Fixes 89 | 90 | * add initial linux icons ([cdda1aa](https://github.com/jamieweavis/streaker/commit/cdda1aadebb4002e98ee0e85be2efedb446e4e69)) 91 | 92 | # [2.3.0](https://github.com/jamieweavis/streaker/compare/v2.2.0...v2.3.0) (2025-03-15) 93 | 94 | 95 | ### Features 96 | 97 | * add auto update via github releases ([95b7958](https://github.com/jamieweavis/streaker/commit/95b79581277a74abd815d6dee8206645aac72c81)) 98 | 99 | # [2.2.0](https://github.com/jamieweavis/streaker/compare/v2.1.3...v2.2.0) (2025-03-15) 100 | 101 | 102 | ### Features 103 | 104 | * add better error handling & logging ([4f4ab7c](https://github.com/jamieweavis/streaker/commit/4f4ab7c8b21a473a14af6fa49c10a507ff6a9e38)) 105 | * add logging with electron-log ([6e026ee](https://github.com/jamieweavis/streaker/commit/6e026eef4f43e0cf4eae2e447fd7d7b3bb7ec09a)) 106 | 107 | ## [2.1.3](https://github.com/jamieweavis/streaker/compare/v2.1.2...v2.1.3) (2025-03-14) 108 | 109 | 110 | ### Bug Fixes 111 | 112 | * dev tools auto open in dev mode ([25f634c](https://github.com/jamieweavis/streaker/commit/25f634c309898ecfda9385262a3c54fb01f7a71c)) 113 | 114 | ## [2.1.2](https://github.com/jamieweavis/streaker/compare/v2.1.1...v2.1.2) (2025-03-14) 115 | 116 | 117 | ### Bug Fixes 118 | 119 | * add missing light mode button styles ([e27e2cb](https://github.com/jamieweavis/streaker/commit/e27e2cbb8310aefad2e40cf2cf376abade65ac0f)) 120 | 121 | ## [2.1.1](https://github.com/jamieweavis/streaker/compare/v2.1.0...v2.1.1) (2025-03-14) 122 | 123 | 124 | ### Bug Fixes 125 | 126 | * tidy tooltip & reduce window width ([f22e54a](https://github.com/jamieweavis/streaker/commit/f22e54a4b1d837c8a03ed4ccef82e7d970b8a341)) 127 | 128 | # [2.1.0](https://github.com/jamieweavis/streaker/compare/v2.0.0...v2.1.0) (2025-03-14) 129 | 130 | 131 | ### Bug Fixes 132 | 133 | * always refetch contribution stats on preferences save ([e17c4b4](https://github.com/jamieweavis/streaker/commit/e17c4b4bcdd99aa0d9338db76297480a2a3bb187)) 134 | * rename contributions best to most ([e61278f](https://github.com/jamieweavis/streaker/commit/e61278f02e659a4b418301033bbab3c61c960030)) 135 | 136 | 137 | ### Features 138 | 139 | * improve tile icon theme ([12917d5](https://github.com/jamieweavis/streaker/commit/12917d5bd03d0c9295fc0a6f4a9814617669602e)) 140 | 141 | # [2.0.0](https://github.com/jamieweavis/streaker/compare/v1.6.1...v2.0.0) (2025-03-13) 142 | 143 | 144 | * refactor!: upgrade node & tooling, add electron forge, remove react ([2895318](https://github.com/jamieweavis/streaker/commit/2895318cff8996a2e808d9a37fe305bc42e92813)) 145 | 146 | 147 | ### BREAKING CHANGES 148 | 149 | * Upgrade Node.js to LTS (22) 150 | 151 | ## [1.6.1](https://github.com/jamieweavis/streaker/compare/v1.6.0...v1.6.1) (2023-08-29) 152 | 153 | 154 | ### Bug Fixes 155 | 156 | * streak notification message ([ceec31e](https://github.com/jamieweavis/streaker/commit/ceec31ec8c77ecd0a72130fd373d96a9584425d4)) 157 | 158 | # [1.6.0](https://github.com/jamieweavis/streaker/compare/v1.5.1...v1.6.0) (2023-03-24) 159 | 160 | 161 | ### Features 162 | 163 | * add streak count to notification and only show if streaking ([6669aae](https://github.com/jamieweavis/streaker/commit/6669aaeb541483d73c63b54f12221b255f377084)), closes [#263](https://github.com/jamieweavis/streaker/issues/263) 164 | 165 | ## [1.5.1](https://github.com/jamieweavis/streaker/compare/v1.5.0...v1.5.1) (2023-03-15) 166 | 167 | 168 | ### Bug Fixes 169 | 170 | * enable remote window module ([d36f3df](https://github.com/jamieweavis/streaker/commit/d36f3df42ec82e0c6af2ab9b93b91d59829c3547)) 171 | * update contribution to fix stat counts ([8405c6e](https://github.com/jamieweavis/streaker/commit/8405c6e8db0bb73a56bb5e128233562fb95c5e96)) 172 | 173 | # [1.5.0](https://github.com/jamieweavis/streaker/compare/v1.4.3...v1.5.0) (2022-01-06) 174 | 175 | 176 | ### Bug Fixes 177 | 178 | * **deps:** update dependency @primer/components to v23 ([854c545](https://github.com/jamieweavis/streaker/commit/854c5454b0f07c7bfbe21a49b665a5cc97b49f6b)) 179 | * **deps:** update dependency electron-store to v7 ([f3718ac](https://github.com/jamieweavis/streaker/commit/f3718acea483a4193e12a67f7967bfd942840da4)) 180 | * **deps:** update react monorepo to v17 ([2558f8c](https://github.com/jamieweavis/streaker/commit/2558f8c7b11ea797fd631eaad8c750102d8dd2cb)) 181 | * adjust window size to fit content ([77e96de](https://github.com/jamieweavis/streaker/commit/77e96dec5bae40221247ddb6afbe524850049e60)) 182 | * resolve svg import error and ignore jsx issue ([0ea1420](https://github.com/jamieweavis/streaker/commit/0ea1420b893706ec0e90463f19a6064a992121be)) 183 | 184 | 185 | ### Features 186 | 187 | * show crown icon when streaking ([20b0701](https://github.com/jamieweavis/streaker/commit/20b07019b6669a78e291871ba2815d38f952f45d)) 188 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018-present Jamie Weavis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Streaker Streaker Logo 2 | 3 | Cross-platform GitHub contribution streak/statistic tracking menu bar application with reminder notification 4 | 5 | [![ci](https://github.com/jamieweavis/streaker/actions/workflows/ci.yml/badge.svg)](https://github.com/jamieweavis/streaker/actions) 6 | [![downloads](https://img.shields.io/github/downloads/jamieweavis/streaker/total.svg)](https://github.com/jamieweavis/streaker/releases) 7 | [![version](https://img.shields.io/github/release/jamieweavis/streaker.svg)](https://github.com/jamieweavis/streaker/releases) 8 | [![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/jamieweavis/streaker/blob/main/LICENSE) 9 | 10 | Screenshot 11 | 12 | ## Install 13 | 14 | Download the latest version of Streaker from the **[GitHub releases](https://github.com/jamieweavis/streaker/releases)** page. Or see the **[Building Locally](#building-locally)** section to build it yourself. 15 | 16 | ## Features 17 | 18 | - Menu bar contribution stats 19 | - Streak 20 | - Best 21 | - Current 22 | - Previous 23 | - Contributions 24 | - Most (day) 25 | - Today 26 | - Total (year) 27 | - Menu bar icon changes depending on contribution status 28 | - Empty - you haven't contributed today 29 | - Filled - you have contributed today 30 | - Crown - you're currently on your best streak 31 | - Three different menu bar icon themes 32 | - Flame 33 | - Tile 34 | - Octocat 35 | - Reminder notification so you don't lose your streak 36 | 37 | ### Menu Bar Icons 38 | 39 | #### Light Mode 40 | 41 | | State | Flame | Tile | Octocat | 42 | | ----- | ----- | ---- | ------- | 43 | | Pending | Flame Empty | Tile Empty | Octocat Empty | 44 | | Contributed | Flame Contributed | Tile Contributed | Octocat Contributed | 45 | | Streaking | Flame Crown | Tile Streaking | Octocat Streaking | 46 | 47 | #### Dark Mode 48 | 49 | | State | Flame | Tile | Octocat | 50 | | ----- | ----- | ---- | ------- | 51 | | Pending | Flame Empty | Tile Empty | Octocat Empty | 52 | | Contributed | Flame Contributed | Tile Contributed | Octocat Contributed | 53 | | Streaking | Flame Crown | Tile Streaking | Octocat Streaking | 54 | 55 | ## Building Locally 56 | 57 | The app is built & deployed for all platforms via GitHub Actions automatically, but you can build it locally for your platform with the following steps: 58 | 59 | Clone the repository 60 | ```bash 61 | git clone https://github.com/jamieweavis/streaker.git 62 | 63 | cd streaker 64 | ``` 65 | 66 | Install npm dependencies (recommended to use Node.js v22) 67 | ```bash 68 | npm install 69 | ``` 70 | 71 | Package the app for your current platform 72 | ```bash 73 | npm run package 74 | ``` 75 | 76 | The packaged app will now be in the `out/` directory 77 | 78 | ## Development 79 | 80 | Run the app in development mode 81 | ```bash 82 | npm run start 83 | ``` 84 | 85 | *Only the renderer process currently supports hot reloading, changes in `src/main/` will require a restart* 86 | 87 | ## Built with 88 | 89 | - [TypeScript](https://github.com/microsoft/TypeScript) 90 | - [Electron](https://github.com/electron/electron) 91 | - [Tailwind CSS](https://github.com/tailwindlabs/tailwindcss) 92 | - [Electron Forge](https://github.com/electron/forge) 93 | 94 | ## Related 95 | 96 | - [Streaker CLI](https://github.com/jamieweavis/streaker-cli) - GitHub contribution streak/statistic tracking command line application with ASCII contribution graph 97 | - [Contribution](https://github.com/jamieweavis/contribution) - GitHub contribution graph parser - contribution streak & statistic calculator with zero dependencies 98 | 99 | ## License 100 | 101 | This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. 102 | -------------------------------------------------------------------------------- /biome.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", 3 | "vcs": { 4 | "enabled": false, 5 | "clientKind": "git", 6 | "useIgnoreFile": false 7 | }, 8 | "files": { 9 | "ignoreUnknown": false, 10 | "ignore": [] 11 | }, 12 | "formatter": { 13 | "enabled": true, 14 | "indentStyle": "space", 15 | "indentWidth": 2 16 | }, 17 | "organizeImports": { 18 | "enabled": true 19 | }, 20 | "linter": { 21 | "enabled": true, 22 | "rules": { 23 | "recommended": true 24 | } 25 | }, 26 | "javascript": { 27 | "formatter": { 28 | "quoteStyle": "single" 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /forge.config.ts: -------------------------------------------------------------------------------- 1 | import type { ForgeConfig } from '@electron-forge/shared-types'; 2 | import { MakerSquirrel } from '@electron-forge/maker-squirrel'; 3 | import { MakerZIP } from '@electron-forge/maker-zip'; 4 | import { MakerDeb } from '@electron-forge/maker-deb'; 5 | import { MakerRpm } from '@electron-forge/maker-rpm'; 6 | import { MakerDMG } from '@electron-forge/maker-dmg'; 7 | import { AutoUnpackNativesPlugin } from '@electron-forge/plugin-auto-unpack-natives'; 8 | import { WebpackPlugin } from '@electron-forge/plugin-webpack'; 9 | import { FusesPlugin } from '@electron-forge/plugin-fuses'; 10 | import { FuseV1Options, FuseVersion } from '@electron/fuses'; 11 | 12 | import { mainConfig } from './webpack.main.config'; 13 | import { rendererConfig } from './webpack.renderer.config'; 14 | 15 | const config: ForgeConfig = { 16 | packagerConfig: { 17 | asar: true, 18 | icon: './src/icons/app/icon', 19 | appCategoryType: 'public.app-category.developer-tools', 20 | extraResource: ['./src/'], 21 | executableName: 'streaker', 22 | }, 23 | rebuildConfig: {}, 24 | makers: [ 25 | new MakerSquirrel({}), 26 | new MakerZIP({}, ['darwin']), 27 | new MakerRpm({}), 28 | new MakerDeb({}), 29 | new MakerDMG({}, ['darwin']), 30 | ], 31 | publishers: [ 32 | { 33 | name: '@electron-forge/publisher-github', 34 | config: { 35 | repository: { 36 | owner: 'jamieweavis', 37 | name: 'streaker', 38 | }, 39 | }, 40 | }, 41 | ], 42 | plugins: [ 43 | new AutoUnpackNativesPlugin({}), 44 | new WebpackPlugin({ 45 | mainConfig, 46 | renderer: { 47 | config: rendererConfig, 48 | entryPoints: [ 49 | { 50 | html: './src/renderer/index.html', 51 | js: './src/renderer/renderer.ts', 52 | name: 'main_window', 53 | preload: { 54 | js: './src/renderer/preload.ts', 55 | }, 56 | }, 57 | ], 58 | }, 59 | }), 60 | // Fuses are used to enable/disable various Electron functionality 61 | // at package time, before code signing the application 62 | new FusesPlugin({ 63 | version: FuseVersion.V1, 64 | [FuseV1Options.RunAsNode]: false, 65 | [FuseV1Options.EnableCookieEncryption]: true, 66 | [FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false, 67 | [FuseV1Options.EnableNodeCliInspectArguments]: false, 68 | [FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: true, 69 | [FuseV1Options.OnlyLoadAppFromAsar]: true, 70 | }), 71 | ], 72 | }; 73 | 74 | export default config; 75 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "streaker", 3 | "productName": "Streaker", 4 | "private": true, 5 | "description": "Cross-platform GitHub contribution streak/statistic tracking menu bar application with reminder notification", 6 | "version": "2.6.1", 7 | "homepage": "https://github.com/jamieweavis/streaker#readme", 8 | "license": "MIT", 9 | "main": ".webpack/main", 10 | "author": "Jamie Weavis", 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/jamieweavis/streaker.git" 14 | }, 15 | "bugs": { 16 | "url": "https://github.com/jamieweavis/streaker/issues" 17 | }, 18 | "keywords": [ 19 | "contribution", 20 | "contribution-graph", 21 | "electron", 22 | "electron-app", 23 | "github", 24 | "macos", 25 | "menubar", 26 | "streak" 27 | ], 28 | "scripts": { 29 | "start": "electron-forge start", 30 | "package": "electron-forge package", 31 | "make": "electron-forge make", 32 | "publish": "electron-forge publish", 33 | "lint": "biome check src", 34 | "lint:fix": "npm run lint -- --write", 35 | "format": "biome format src", 36 | "format:write": "npm run format -- --write", 37 | "prepare": "husky" 38 | }, 39 | "release": { 40 | "branches": [ 41 | "main" 42 | ], 43 | "plugins": [ 44 | "@semantic-release/changelog", 45 | "@semantic-release/commit-analyzer", 46 | "@semantic-release/npm", 47 | "@semantic-release/git", 48 | "@semantic-release/github", 49 | "@semantic-release/release-notes-generator" 50 | ] 51 | }, 52 | "lint-staged": { 53 | "*.ts": [ 54 | "biome format --write", 55 | "biome lint --write" 56 | ] 57 | }, 58 | "commitlint": { 59 | "extends": [ 60 | "@commitlint/config-conventional" 61 | ] 62 | }, 63 | "devDependencies": { 64 | "@biomejs/biome": "^1.9.4", 65 | "@commitlint/cli": "^19.8.0", 66 | "@commitlint/config-conventional": "^19.8.0", 67 | "@electron-forge/cli": "^7.7.0", 68 | "@electron-forge/maker-deb": "^7.7.0", 69 | "@electron-forge/maker-dmg": "^7.7.0", 70 | "@electron-forge/maker-rpm": "^7.7.0", 71 | "@electron-forge/maker-squirrel": "^7.7.0", 72 | "@electron-forge/maker-zip": "^7.7.0", 73 | "@electron-forge/plugin-auto-unpack-natives": "^7.7.0", 74 | "@electron-forge/plugin-fuses": "^7.7.0", 75 | "@electron-forge/plugin-webpack": "^7.7.0", 76 | "@electron-forge/publisher-github": "^7.7.0", 77 | "@electron/fuses": "^1.8.0", 78 | "@semantic-release/changelog": "6.0.3", 79 | "@semantic-release/commit-analyzer": "13.0.1", 80 | "@semantic-release/git": "10.0.1", 81 | "@semantic-release/github": "11.0.1", 82 | "@semantic-release/npm": "12.0.1", 83 | "@semantic-release/release-notes-generator": "14.0.3", 84 | "@types/auto-launch": "^5.0.5", 85 | "@vercel/webpack-asset-relocator-loader": "^1.7.4", 86 | "css-loader": "^7.1.2", 87 | "electron": "35.0.1", 88 | "fork-ts-checker-webpack-plugin": "^9.0.2", 89 | "husky": "^9.1.7", 90 | "lint-staged": "^15.5.0", 91 | "node-loader": "^2.1.0", 92 | "postcss-loader": "^8.1.1", 93 | "semantic-release": "24.2.3", 94 | "style-loader": "^4.0.0", 95 | "ts-loader": "^9.5.2", 96 | "ts-node": "^10.9.2", 97 | "typescript": "^5.8.2" 98 | }, 99 | "dependencies": { 100 | "@tailwindcss/postcss": "4.0.14", 101 | "auto-launch": "^5.0.6", 102 | "contribution": "^7.0.0", 103 | "cron": "^4.1.0", 104 | "electron-log": "^5.3.2", 105 | "electron-squirrel-startup": "^1.0.1", 106 | "electron-store": "^8.2.0", 107 | "postcss": "^8.5.3", 108 | "tailwindcss": "4.0.14", 109 | "update-electron-app": "^3.1.1" 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | '@tailwindcss/postcss': {}, 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /src/icons/app/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/app/icon.icns -------------------------------------------------------------------------------- /src/icons/app/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/app/icon.ico -------------------------------------------------------------------------------- /src/icons/index.ts: -------------------------------------------------------------------------------- 1 | import { join } from 'node:path'; 2 | import { type NativeImage, app, nativeImage } from 'electron'; 3 | 4 | export interface Icon { 5 | contributed: NativeImage; 6 | error: NativeImage; 7 | pending: NativeImage; 8 | streaking: NativeImage; 9 | } 10 | 11 | export interface Icons { 12 | [theme: string]: Icon; 13 | } 14 | 15 | export let icons: Icons; 16 | 17 | const appPath = app.getAppPath().replace('app.asar', ''); 18 | 19 | // macOS Flame 20 | const macosFlameContributed = nativeImage.createFromPath( 21 | join(appPath, 'src/icons/macos/flame/contributed@2x.png'), 22 | ); 23 | macosFlameContributed.setTemplateImage(true); 24 | const macosFlameError = nativeImage.createFromPath( 25 | join(appPath, 'src/icons/macos/flame/error@2x.png'), 26 | ); 27 | macosFlameError.setTemplateImage(true); 28 | const macosFlamePending = nativeImage.createFromPath( 29 | join(appPath, 'src/icons/macos/flame/pending@2x.png'), 30 | ); 31 | macosFlamePending.setTemplateImage(true); 32 | const macosFlameStreaking = nativeImage.createFromPath( 33 | join(appPath, 'src/icons/macos/flame/streaking@2x.png'), 34 | ); 35 | macosFlameStreaking.setTemplateImage(true); 36 | 37 | // macOS Tile 38 | const macosTileContributed = nativeImage.createFromPath( 39 | join(appPath, 'src/icons/macos/tile/contributed@2x.png'), 40 | ); 41 | macosTileContributed.setTemplateImage(true); 42 | const macosTileError = nativeImage.createFromPath( 43 | join(appPath, 'src/icons/macos/tile/error@2x.png'), 44 | ); 45 | macosTileError.setTemplateImage(true); 46 | const macosTilePending = nativeImage.createFromPath( 47 | join(appPath, 'src/icons/macos/tile/pending@2x.png'), 48 | ); 49 | macosTilePending.setTemplateImage(true); 50 | const macosTileStreaking = nativeImage.createFromPath( 51 | join(appPath, 'src/icons/macos/tile/streaking@2x.png'), 52 | ); 53 | macosTileStreaking.setTemplateImage(true); 54 | 55 | // macOS Octocat 56 | const macosOctocatContributed = nativeImage.createFromPath( 57 | join(appPath, 'src/icons/macos/octocat/contributed@2x.png'), 58 | ); 59 | macosOctocatContributed.setTemplateImage(true); 60 | const macosOctocatError = nativeImage.createFromPath( 61 | join(appPath, 'src/icons/macos/octocat/error@2x.png'), 62 | ); 63 | macosOctocatError.setTemplateImage(true); 64 | const macosOctocatPending = nativeImage.createFromPath( 65 | join(appPath, 'src/icons/macos/octocat/pending@2x.png'), 66 | ); 67 | macosOctocatPending.setTemplateImage(true); 68 | const macosOctocatStreaking = nativeImage.createFromPath( 69 | join(appPath, 'src/icons/macos/octocat/streaking@2x.png'), 70 | ); 71 | macosOctocatStreaking.setTemplateImage(true); 72 | 73 | // Windows Flame 74 | const windowsFlameContributed = nativeImage.createFromPath( 75 | join(appPath, 'src/icons/windows/flame/contributed.ico'), 76 | ); 77 | const windowsFlameError = nativeImage.createFromPath( 78 | join(appPath, 'src/icons/windows/flame/error.ico'), 79 | ); 80 | const windowsFlamePending = nativeImage.createFromPath( 81 | join(appPath, 'src/icons/windows/flame/pending.ico'), 82 | ); 83 | const windowsFlameStreaking = nativeImage.createFromPath( 84 | join(appPath, 'src/icons/windows/flame/streaking.ico'), 85 | ); 86 | 87 | // Windows Tile 88 | const windowsTileContributed = nativeImage.createFromPath( 89 | join(appPath, 'src/icons/windows/tile/contributed.ico'), 90 | ); 91 | const windowsTileError = nativeImage.createFromPath( 92 | join(appPath, 'src/icons/windows/tile/error.ico'), 93 | ); 94 | const windowsTilePending = nativeImage.createFromPath( 95 | join(appPath, 'src/icons/windows/tile/pending.ico'), 96 | ); 97 | const windowsTileStreaking = nativeImage.createFromPath( 98 | join(appPath, 'src/icons/windows/tile/streaking.ico'), 99 | ); 100 | 101 | // Windows Octocat 102 | const windowsOctocatContributed = nativeImage.createFromPath( 103 | join(appPath, 'src/icons/windows/octocat/contributed.ico'), 104 | ); 105 | const windowsOctocatError = nativeImage.createFromPath( 106 | join(appPath, 'src/icons/windows/octocat/error.ico'), 107 | ); 108 | const windowsOctocatPending = nativeImage.createFromPath( 109 | join(appPath, 'src/icons/windows/octocat/pending.ico'), 110 | ); 111 | const windowsOctocatStreaking = nativeImage.createFromPath( 112 | join(appPath, 'src/icons/windows/octocat/streaking.ico'), 113 | ); 114 | 115 | // Linux Flame 116 | const linuxFlameContributed = nativeImage.createFromPath( 117 | join(appPath, 'src/icons/linux/flame/contributed.png'), 118 | ); 119 | const linuxFlameError = nativeImage.createFromPath( 120 | join(appPath, 'src/icons/linux/flame/error.png'), 121 | ); 122 | const linuxFlamePending = nativeImage.createFromPath( 123 | join(appPath, 'src/icons/linux/flame/pending.png'), 124 | ); 125 | const linuxFlameStreaking = nativeImage.createFromPath( 126 | join(appPath, 'src/icons/linux/flame/streaking.png'), 127 | ); 128 | 129 | // Linux Tile 130 | const linuxTileContributed = nativeImage.createFromPath( 131 | join(appPath, 'src/icons/linux/tile/contributed.png'), 132 | ); 133 | const linuxTileError = nativeImage.createFromPath( 134 | join(appPath, 'src/icons/linux/tile/error.png'), 135 | ); 136 | const linuxTilePending = nativeImage.createFromPath( 137 | join(appPath, 'src/icons/linux/tile/pending.png'), 138 | ); 139 | const linuxTileStreaking = nativeImage.createFromPath( 140 | join(appPath, 'src/icons/linux/tile/streaking.png'), 141 | ); 142 | 143 | // Linux Octocat 144 | const linuxOctocatContributed = nativeImage.createFromPath( 145 | join(appPath, 'src/icons/linux/octocat/contributed.png'), 146 | ); 147 | const linuxOctocatError = nativeImage.createFromPath( 148 | join(appPath, 'src/icons/linux/octocat/error.png'), 149 | ); 150 | const linuxOctocatPending = nativeImage.createFromPath( 151 | join(appPath, 'src/icons/linux/octocat/pending.png'), 152 | ); 153 | const linuxOctocatStreaking = nativeImage.createFromPath( 154 | join(appPath, 'src/icons/linux/octocat/streaking.png'), 155 | ); 156 | 157 | switch (process.platform) { 158 | case 'darwin': 159 | icons = { 160 | flame: { 161 | contributed: macosFlameContributed, 162 | error: macosFlameError, 163 | pending: macosFlamePending, 164 | streaking: macosFlameStreaking, 165 | }, 166 | tile: { 167 | contributed: macosTileContributed, 168 | error: macosTileError, 169 | pending: macosTilePending, 170 | streaking: macosTileStreaking, 171 | }, 172 | octocat: { 173 | contributed: macosOctocatContributed, 174 | error: macosOctocatError, 175 | pending: macosOctocatPending, 176 | streaking: macosOctocatStreaking, 177 | }, 178 | }; 179 | break; 180 | case 'win32': 181 | icons = { 182 | flame: { 183 | contributed: windowsFlameContributed, 184 | error: windowsFlameError, 185 | pending: windowsFlamePending, 186 | streaking: windowsFlameStreaking, 187 | }, 188 | tile: { 189 | contributed: windowsTileContributed, 190 | error: windowsTileError, 191 | pending: windowsTilePending, 192 | streaking: windowsTileStreaking, 193 | }, 194 | octocat: { 195 | contributed: windowsOctocatContributed, 196 | error: windowsOctocatError, 197 | pending: windowsOctocatPending, 198 | streaking: windowsOctocatStreaking, 199 | }, 200 | }; 201 | break; 202 | case 'linux': 203 | icons = { 204 | flame: { 205 | contributed: linuxFlameContributed, 206 | error: linuxFlameError, 207 | pending: linuxFlamePending, 208 | streaking: linuxFlameStreaking, 209 | }, 210 | tile: { 211 | contributed: linuxTileContributed, 212 | error: linuxTileError, 213 | pending: linuxTilePending, 214 | streaking: linuxTileStreaking, 215 | }, 216 | octocat: { 217 | contributed: linuxOctocatContributed, 218 | error: linuxOctocatError, 219 | pending: linuxOctocatPending, 220 | streaking: linuxOctocatStreaking, 221 | }, 222 | }; 223 | break; 224 | default: { 225 | throw new Error( 226 | `Platform "${process.platform}" is not currently supported!`, 227 | ); 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /src/icons/linux/flame/contributed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/linux/flame/contributed.png -------------------------------------------------------------------------------- /src/icons/linux/flame/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/linux/flame/error.png -------------------------------------------------------------------------------- /src/icons/linux/flame/pending.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/linux/flame/pending.png -------------------------------------------------------------------------------- /src/icons/linux/flame/streaking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/linux/flame/streaking.png -------------------------------------------------------------------------------- /src/icons/linux/octocat/contributed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/linux/octocat/contributed.png -------------------------------------------------------------------------------- /src/icons/linux/octocat/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/linux/octocat/error.png -------------------------------------------------------------------------------- /src/icons/linux/octocat/pending.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/linux/octocat/pending.png -------------------------------------------------------------------------------- /src/icons/linux/octocat/streaking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/linux/octocat/streaking.png -------------------------------------------------------------------------------- /src/icons/linux/tile/contributed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/linux/tile/contributed.png -------------------------------------------------------------------------------- /src/icons/linux/tile/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/linux/tile/error.png -------------------------------------------------------------------------------- /src/icons/linux/tile/pending.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/linux/tile/pending.png -------------------------------------------------------------------------------- /src/icons/linux/tile/streaking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/linux/tile/streaking.png -------------------------------------------------------------------------------- /src/icons/macos/flame/contributed@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/macos/flame/contributed@2x.png -------------------------------------------------------------------------------- /src/icons/macos/flame/error@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/macos/flame/error@2x.png -------------------------------------------------------------------------------- /src/icons/macos/flame/pending@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/macos/flame/pending@2x.png -------------------------------------------------------------------------------- /src/icons/macos/flame/streaking@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/macos/flame/streaking@2x.png -------------------------------------------------------------------------------- /src/icons/macos/octocat/contributed@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/macos/octocat/contributed@2x.png -------------------------------------------------------------------------------- /src/icons/macos/octocat/error@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/macos/octocat/error@2x.png -------------------------------------------------------------------------------- /src/icons/macos/octocat/pending@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/macos/octocat/pending@2x.png -------------------------------------------------------------------------------- /src/icons/macos/octocat/streaking@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/macos/octocat/streaking@2x.png -------------------------------------------------------------------------------- /src/icons/macos/tile/contributed@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/macos/tile/contributed@2x.png -------------------------------------------------------------------------------- /src/icons/macos/tile/error@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/macos/tile/error@2x.png -------------------------------------------------------------------------------- /src/icons/macos/tile/pending@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/macos/tile/pending@2x.png -------------------------------------------------------------------------------- /src/icons/macos/tile/streaking@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/macos/tile/streaking@2x.png -------------------------------------------------------------------------------- /src/icons/windows/flame/contributed.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/windows/flame/contributed.ico -------------------------------------------------------------------------------- /src/icons/windows/flame/error.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/windows/flame/error.ico -------------------------------------------------------------------------------- /src/icons/windows/flame/pending.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/windows/flame/pending.ico -------------------------------------------------------------------------------- /src/icons/windows/flame/streaking.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/windows/flame/streaking.ico -------------------------------------------------------------------------------- /src/icons/windows/octocat/contributed.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/windows/octocat/contributed.ico -------------------------------------------------------------------------------- /src/icons/windows/octocat/error.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/windows/octocat/error.ico -------------------------------------------------------------------------------- /src/icons/windows/octocat/pending.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/windows/octocat/pending.ico -------------------------------------------------------------------------------- /src/icons/windows/octocat/streaking.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/windows/octocat/streaking.ico -------------------------------------------------------------------------------- /src/icons/windows/tile/contributed.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/windows/tile/contributed.ico -------------------------------------------------------------------------------- /src/icons/windows/tile/error.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/windows/tile/error.ico -------------------------------------------------------------------------------- /src/icons/windows/tile/pending.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/windows/tile/pending.ico -------------------------------------------------------------------------------- /src/icons/windows/tile/streaking.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jamieweavis/streaker/90568ab1f3249aef18b9de7dfa4a1aabb9c29e42/src/icons/windows/tile/streaking.ico -------------------------------------------------------------------------------- /src/main/logger.ts: -------------------------------------------------------------------------------- 1 | import log from 'electron-log'; 2 | 3 | export const logger = log; 4 | -------------------------------------------------------------------------------- /src/main/main.ts: -------------------------------------------------------------------------------- 1 | import AutoLaunch from 'auto-launch'; 2 | import { type GitHubStats, fetchGitHubStats } from 'contribution'; 3 | import { CronJob, CronTime } from 'cron'; 4 | import type { NativeImage } from 'electron'; 5 | import { BrowserWindow, Notification, Tray, app, ipcMain } from 'electron'; 6 | import { UpdateSourceType, updateElectronApp } from 'update-electron-app'; 7 | 8 | import { icons } from '../icons'; 9 | import { logger } from './logger'; 10 | import MenuBuilder from './menu'; 11 | import { store } from './store'; 12 | import { createTrayMenu } from './tray-menu'; 13 | 14 | declare const MAIN_WINDOW_WEBPACK_ENTRY: string; 15 | declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: string; 16 | 17 | export interface Preferences { 18 | username: string; 19 | launchAtLogin: boolean; 20 | reminderEnabled: boolean; 21 | reminderTime: string; 22 | iconTheme: string; 23 | } 24 | 25 | const isDev = process.env.NODE_ENV === 'development'; 26 | 27 | const bootstrap = (): void => { 28 | const { username, iconTheme, reminderTime, reminderEnabled } = store.store; 29 | 30 | let preferencesWindow: BrowserWindow | null = null; 31 | let lastFetchedStats: GitHubStats | undefined; 32 | 33 | const tray = new Tray(icons[iconTheme].pending); 34 | 35 | const createPreferencesWindow = () => { 36 | preferencesWindow = new BrowserWindow({ 37 | width: 310, 38 | height: 485, 39 | show: false, 40 | resizable: false, 41 | maximizable: false, 42 | minimizable: false, 43 | fullscreenable: false, 44 | frame: false, 45 | webPreferences: { 46 | preload: MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY, 47 | additionalArguments: Object.entries(store.store).map( 48 | ([key, value]) => `--${key}=${value}`, 49 | ), 50 | }, 51 | }); 52 | 53 | preferencesWindow.loadURL(MAIN_WINDOW_WEBPACK_ENTRY); 54 | 55 | const menuBuilder = new MenuBuilder(preferencesWindow); 56 | menuBuilder.buildMenu(); 57 | 58 | preferencesWindow.on('ready-to-show', () => { 59 | if (preferencesWindow) { 60 | preferencesWindow.show(); 61 | if (isDev) preferencesWindow.webContents.openDevTools(); 62 | } 63 | if (process.platform === 'darwin') app.dock.show(); 64 | }); 65 | 66 | preferencesWindow.on('closed', () => { 67 | preferencesWindow = null; 68 | if (process.platform === 'darwin') app.dock.hide(); 69 | }); 70 | }; 71 | 72 | const createTray = ({ 73 | username, 74 | gitHubStats, 75 | icon, 76 | }: { 77 | username?: string; 78 | gitHubStats?: GitHubStats; 79 | icon?: NativeImage; 80 | }) => { 81 | const trayMenu = createTrayMenu({ 82 | username, 83 | gitHubStats, 84 | createPreferencesWindow: createPreferencesWindow, 85 | requestContributionData: fetchContributionStats, 86 | isPreferencesWindowOpen: () => !!preferencesWindow, 87 | focusPreferencesWindow: () => { 88 | if (!preferencesWindow) return; 89 | if (preferencesWindow.isMinimized()) preferencesWindow.restore(); 90 | preferencesWindow.focus(); 91 | }, 92 | }); 93 | tray.setContextMenu(trayMenu); 94 | if (icon) tray.setImage(icon); 95 | }; 96 | 97 | const fetchContributionStats = async () => { 98 | const username = store.get('username'); 99 | const iconTheme = store.get('iconTheme'); 100 | 101 | if (!username) { 102 | return createTray({ icon: icons[iconTheme].error }); 103 | } 104 | 105 | let gitHubStats: GitHubStats | undefined; 106 | let icon = icons[iconTheme].pending; 107 | 108 | try { 109 | gitHubStats = await fetchGitHubStats(username); 110 | logger.info('Fetched contribution stats', gitHubStats); 111 | 112 | lastFetchedStats = gitHubStats; 113 | 114 | if (gitHubStats.currentStreak > 0) { 115 | icon = icons[iconTheme].contributed; 116 | } 117 | if (gitHubStats.currentStreak === gitHubStats.bestStreak) { 118 | icon = icons[iconTheme].streaking; 119 | } 120 | } catch (err) { 121 | icon = icons[iconTheme].error; 122 | } 123 | 124 | createTray({ username, gitHubStats, icon }); 125 | }; 126 | 127 | const savePreferences = (preferences: Preferences) => { 128 | logger.info('Saving preferences:', preferences); 129 | 130 | // Update auto launch 131 | try { 132 | if (preferences.launchAtLogin !== store.get('launchAtLogin')) { 133 | if (preferences.launchAtLogin) { 134 | autoLaunch.enable(); 135 | logger.info('Auto launch enabled'); 136 | } else { 137 | autoLaunch.disable(); 138 | logger.info('Auto launch disabled'); 139 | } 140 | } 141 | } catch (error) { 142 | logger.error('Error enabling auto launch', error); 143 | } 144 | 145 | // Update reminder notification 146 | try { 147 | if ( 148 | preferences.reminderEnabled !== store.get('reminderEnabled') || 149 | preferences.reminderTime !== store.get('reminderTime') 150 | ) { 151 | if (preferences.reminderEnabled) { 152 | const [hours, minutes] = preferences.reminderTime.split(':'); 153 | const cronTime = new CronTime(`${minutes} ${hours} * * *`); 154 | reminderNotification.setTime(cronTime); 155 | reminderNotification.start(); 156 | logger.info( 157 | 'Reminder notification enabled:', 158 | preferences.reminderTime, 159 | ); 160 | } else { 161 | reminderNotification.stop(); 162 | logger.info('Reminder notification disabled'); 163 | } 164 | } 165 | } catch (error) { 166 | logger.error('Error enabling reminder notification', error); 167 | } 168 | 169 | // Save preferences 170 | store.set(preferences); 171 | logger.info('Preferences saved'); 172 | }; 173 | 174 | const showReminderNotification = () => { 175 | logger.info('Triggering reminder notification'); 176 | 177 | try { 178 | if (!Notification.isSupported()) { 179 | logger.error('Notification not supported'); 180 | return; 181 | } 182 | if (!lastFetchedStats.isStreakAtRisk) { 183 | logger.info('Streak not at risk, skipping reminder notification'); 184 | return; 185 | } 186 | new Notification({ 187 | title: `🔥 Don't lose your streak!`, 188 | body: `Contribute today to continue your ${lastFetchedStats.previousStreak} day streak on GitHub`, 189 | }).show(); 190 | } catch (error) { 191 | logger.error('Error showing reminder notification', error); 192 | } 193 | }; 194 | 195 | app.on('window-all-closed', () => {}); 196 | if (process.platform === 'darwin') app.dock.hide(); 197 | 198 | app.on('activate', () => { 199 | if (!preferencesWindow) createPreferencesWindow(); 200 | }); 201 | 202 | // Fetch data on app start and every minute 203 | fetchContributionStats(); 204 | new CronJob('* * * * *', fetchContributionStats, null, true); 205 | 206 | // Handle saving preferences 207 | ipcMain.on('savePreferences', (_, preferences: Preferences) => { 208 | savePreferences(preferences); 209 | fetchContributionStats(); 210 | }); 211 | 212 | // Set up reminder notification 213 | const [hours, minutes] = reminderTime.split(':'); 214 | const reminderNotification = new CronJob( 215 | `${minutes} ${hours} * * *`, 216 | showReminderNotification, 217 | ); 218 | if (reminderEnabled) reminderNotification.start(); 219 | 220 | // Set up auto launch 221 | const autoLaunch = new AutoLaunch({ 222 | name: 'Streaker', 223 | isHidden: true, 224 | }); 225 | 226 | // Set up auto updater 227 | updateElectronApp({ 228 | updateSource: { 229 | type: UpdateSourceType.ElectronPublicUpdateService, 230 | repo: 'jamieweavis/streaker', 231 | }, 232 | updateInterval: '1 hour', 233 | logger: logger, 234 | }); 235 | 236 | // Open preferences window automatically if username is not set (most likely fresh install) 237 | if (!username || isDev) createPreferencesWindow(); 238 | }; 239 | 240 | app 241 | .whenReady() 242 | .then(bootstrap) 243 | .catch((error) => { 244 | logger.error('An error occurred', error); 245 | }); 246 | -------------------------------------------------------------------------------- /src/main/menu.ts: -------------------------------------------------------------------------------- 1 | import { Menu, app } from 'electron'; 2 | import type { BrowserWindow, MenuItemConstructorOptions } from 'electron'; 3 | 4 | interface DarwinMenuItemConstructorOptions extends MenuItemConstructorOptions { 5 | selector?: string; 6 | submenu?: DarwinMenuItemConstructorOptions[] | Menu; 7 | } 8 | 9 | export default class MenuBuilder { 10 | mainWindow: BrowserWindow; 11 | 12 | constructor(mainWindow: BrowserWindow) { 13 | this.mainWindow = mainWindow; 14 | } 15 | 16 | buildMenu(): Menu { 17 | if ( 18 | process.env.NODE_ENV === 'development' || 19 | process.env.DEBUG_PROD === 'true' 20 | ) { 21 | this.setupDevelopmentEnvironment(); 22 | } 23 | 24 | const template = 25 | process.platform === 'darwin' 26 | ? this.buildDarwinTemplate() 27 | : this.buildDefaultTemplate(); 28 | 29 | const menu = Menu.buildFromTemplate(template); 30 | Menu.setApplicationMenu(menu); 31 | 32 | return menu; 33 | } 34 | 35 | setupDevelopmentEnvironment(): void { 36 | this.mainWindow.webContents.on('context-menu', (_, props) => { 37 | const { x, y } = props; 38 | 39 | Menu.buildFromTemplate([ 40 | { 41 | label: 'Inspect element', 42 | click: () => { 43 | this.mainWindow.webContents.inspectElement(x, y); 44 | }, 45 | }, 46 | ]).popup({ window: this.mainWindow }); 47 | }); 48 | } 49 | 50 | buildDarwinTemplate(): MenuItemConstructorOptions[] { 51 | const subMenuAbout: DarwinMenuItemConstructorOptions = { 52 | label: 'Streaker', 53 | submenu: [ 54 | { 55 | label: 'About Streaker', 56 | selector: 'orderFrontStandardAboutPanel:', 57 | }, 58 | { type: 'separator' }, 59 | { 60 | label: 'Quit', 61 | accelerator: 'Command+Q', 62 | click: () => { 63 | app.quit(); 64 | }, 65 | }, 66 | ], 67 | }; 68 | 69 | const subMenuEdit: DarwinMenuItemConstructorOptions = { 70 | label: 'Edit', 71 | submenu: [ 72 | { label: 'Undo', accelerator: 'Command+Z', selector: 'undo:' }, 73 | { label: 'Redo', accelerator: 'Shift+Command+Z', selector: 'redo:' }, 74 | { type: 'separator' }, 75 | { label: 'Cut', accelerator: 'Command+X', selector: 'cut:' }, 76 | { label: 'Copy', accelerator: 'Command+C', selector: 'copy:' }, 77 | { label: 'Paste', accelerator: 'Command+V', selector: 'paste:' }, 78 | { 79 | label: 'Select All', 80 | accelerator: 'Command+A', 81 | selector: 'selectAll:', 82 | }, 83 | ], 84 | }; 85 | 86 | return [subMenuAbout, subMenuEdit]; 87 | } 88 | 89 | buildDefaultTemplate() { 90 | const templateDefault = [ 91 | { 92 | label: '&File', 93 | submenu: [ 94 | { 95 | label: '&Close', 96 | accelerator: 'Ctrl+W', 97 | click: () => { 98 | this.mainWindow.close(); 99 | }, 100 | }, 101 | ], 102 | }, 103 | ]; 104 | 105 | return templateDefault; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/store.ts: -------------------------------------------------------------------------------- 1 | import Store from 'electron-store'; 2 | 3 | export const store = new Store({ 4 | defaults: { 5 | username: '', 6 | launchAtLogin: false, 7 | reminderEnabled: false, 8 | reminderTime: '20:00', 9 | iconTheme: 'flame', 10 | }, 11 | }); 12 | -------------------------------------------------------------------------------- /src/main/tray-menu.ts: -------------------------------------------------------------------------------- 1 | import type { GitHubStats } from 'contribution'; 2 | import { Menu, app, shell } from 'electron'; 3 | 4 | import pjson from '../../package.json'; 5 | 6 | export interface CreateTrayMenuOptions { 7 | username?: string; 8 | gitHubStats?: GitHubStats; 9 | createPreferencesWindow: () => void; 10 | requestContributionData: () => void; 11 | isPreferencesWindowOpen: () => boolean; 12 | focusPreferencesWindow: () => void; 13 | } 14 | 15 | export const createTrayMenu = ({ 16 | username, 17 | gitHubStats, 18 | createPreferencesWindow, 19 | requestContributionData, 20 | isPreferencesWindowOpen, 21 | focusPreferencesWindow, 22 | }: CreateTrayMenuOptions): Menu => { 23 | const createOrFocusPreferencesWindow = () => { 24 | if (!isPreferencesWindowOpen()) return createPreferencesWindow(); 25 | focusPreferencesWindow(); 26 | }; 27 | 28 | return Menu.buildFromTemplate([ 29 | { 30 | label: username || 'Set GitHub username...', 31 | enabled: !username, 32 | click: createOrFocusPreferencesWindow, 33 | }, 34 | { type: 'separator' }, 35 | { label: 'Streak:', enabled: false }, 36 | { 37 | label: ` Best:\t\t${gitHubStats.bestStreak || 0}`, 38 | enabled: false, 39 | }, 40 | { 41 | label: ` Current:\t${gitHubStats.currentStreak || 0}`, 42 | enabled: false, 43 | }, 44 | { 45 | label: ` Previous:\t${gitHubStats.previousStreak || 0}${gitHubStats.isStreakAtRisk ? '\t(at risk!)' : ''}`, 46 | enabled: false, 47 | }, 48 | { label: 'Contributions:', enabled: false }, 49 | { 50 | label: ` Most:\t\t${gitHubStats.mostContributions || 0}`, 51 | enabled: false, 52 | }, 53 | { 54 | label: ` Today:\t\t${gitHubStats.todaysContributions || 0}`, 55 | enabled: false, 56 | }, 57 | { 58 | label: ` Total:\t\t${gitHubStats.totalContributions || 0}`, 59 | enabled: false, 60 | }, 61 | { type: 'separator' }, 62 | { 63 | label: 'Reload', 64 | enabled: !!username, 65 | accelerator: 'CmdOrCtrl+R', 66 | click: requestContributionData, 67 | }, 68 | { 69 | label: 'Open GitHub Profile...', 70 | enabled: !!username, 71 | accelerator: 'CmdOrCtrl+O', 72 | click: () => shell.openExternal(`https://github.com/${username}`), 73 | }, 74 | { type: 'separator' }, 75 | { 76 | label: 'Preferences...', 77 | accelerator: 'CmdOrCtrl+,', 78 | click: createOrFocusPreferencesWindow, 79 | }, 80 | { 81 | label: 'About Streaker...', 82 | click: () => shell.openExternal(pjson.homepage), 83 | }, 84 | { 85 | label: 'Feedback && Support...', 86 | click: () => shell.openExternal(pjson.bugs.url), 87 | }, 88 | { type: 'separator' }, 89 | { 90 | label: 'Quit Streaker', 91 | accelerator: 'CmdOrCtrl+Q', 92 | click: () => app.quit(), 93 | }, 94 | ]); 95 | }; 96 | -------------------------------------------------------------------------------- /src/renderer/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Streaker Preferences 7 | 8 | 9 | 10 | 11 | 12 |

14 | Streaker Preferences 15 |

16 | 17 |
18 |
19 | 20 | 22 |
23 | 24 |
25 | 29 |
30 | 31 |
32 | 36 | Get a reminder if your streak is at 37 | risk! 🔔 38 |
39 | 40 |
41 | 42 | 44 |
45 | 46 |
47 | 48 | 54 |
55 | 56 |
57 | 61 | 65 |
66 |
67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/renderer/preload.ts: -------------------------------------------------------------------------------- 1 | import { contextBridge, ipcRenderer } from 'electron'; 2 | 3 | import type { Preferences } from '../main/main'; 4 | 5 | contextBridge.exposeInMainWorld('streaker', { 6 | initialPreferences: () => { 7 | const preferences: Record = {}; 8 | for (const arg of process.argv) { 9 | const [key, value] = arg.split('='); 10 | if (key && value) { 11 | preferences[key.substring(2)] = value; 12 | } 13 | } 14 | return preferences; 15 | }, 16 | savePreferences: (preferences: Preferences) => 17 | ipcRenderer.send('savePreferences', preferences), 18 | }); 19 | -------------------------------------------------------------------------------- /src/renderer/renderer.ts: -------------------------------------------------------------------------------- 1 | import './styles.css'; 2 | 3 | const gitHubUsername = document.getElementById( 4 | 'github-username', 5 | ) as HTMLInputElement; 6 | const launchAtLogin = document.getElementById( 7 | 'launch-at-login', 8 | ) as HTMLInputElement; 9 | const reminderNotification = document.getElementById( 10 | 'reminder-notification', 11 | ) as HTMLInputElement; 12 | const reminderTime = document.getElementById( 13 | 'reminder-time', 14 | ) as HTMLInputElement; 15 | const iconTheme = document.getElementById('icon-theme') as HTMLSelectElement; 16 | 17 | const closeWindow = () => window.close(); 18 | const savePreferences = () => { 19 | // @ts-ignore see preload.ts 20 | window.streaker.savePreferences({ 21 | username: gitHubUsername.value, 22 | launchAtLogin: launchAtLogin.checked, 23 | reminderEnabled: reminderNotification.checked, 24 | reminderTime: reminderTime.value, 25 | iconTheme: iconTheme.value, 26 | }); 27 | closeWindow(); 28 | }; 29 | 30 | document.getElementById('cancel-button').addEventListener('click', closeWindow); 31 | 32 | window.addEventListener('keydown', (event) => { 33 | if (event.key === 'Escape') closeWindow(); 34 | if (event.key === 'Enter') savePreferences(); 35 | }); 36 | 37 | document 38 | .getElementById('save-button') 39 | .addEventListener('click', () => savePreferences()); 40 | 41 | // @ts-ignore see preload.ts 42 | const initialPreferences = window.streaker.initialPreferences(); 43 | 44 | gitHubUsername.value = initialPreferences.username || ''; 45 | launchAtLogin.checked = initialPreferences.launchAtLogin === 'true'; 46 | reminderNotification.checked = initialPreferences.reminderEnabled === 'true'; 47 | reminderTime.value = initialPreferences.reminderTime; 48 | iconTheme.value = initialPreferences.iconTheme; 49 | 50 | console.log('Preferences:', initialPreferences); 51 | -------------------------------------------------------------------------------- /src/renderer/styles.css: -------------------------------------------------------------------------------- 1 | @import "tailwindcss"; 2 | 3 | body { 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, 5 | Arial, sans-serif; 6 | } 7 | 8 | input[type="time"]::-webkit-calendar-picker-indicator { 9 | display: none; 10 | } 11 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | content: ['./src/renderer/**/*.{js,jsx,ts,tsx}'], 3 | theme: {}, 4 | variants: {}, 5 | plugins: [], 6 | }; 7 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES6", 4 | "allowJs": true, 5 | "module": "commonjs", 6 | "skipLibCheck": true, 7 | "esModuleInterop": true, 8 | "noImplicitAny": true, 9 | "sourceMap": true, 10 | "baseUrl": ".", 11 | "outDir": "dist", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "paths": { 15 | "*": ["node_modules/*"] 16 | } 17 | }, 18 | "include": ["src/**/*"] 19 | } 20 | -------------------------------------------------------------------------------- /webpack.main.config.ts: -------------------------------------------------------------------------------- 1 | import type { Configuration } from 'webpack'; 2 | 3 | import { rules } from './webpack.rules'; 4 | import { plugins } from './webpack.plugins'; 5 | 6 | export const mainConfig: Configuration = { 7 | /** 8 | * This is the main entry point for your application, it's the first file 9 | * that runs in the main process. 10 | */ 11 | entry: './src/main/main.ts', 12 | // Put your normal webpack config below here 13 | module: { 14 | rules, 15 | }, 16 | plugins, 17 | resolve: { 18 | extensions: ['.js', '.ts', '.jsx', '.tsx', '.css', '.json'], 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /webpack.plugins.ts: -------------------------------------------------------------------------------- 1 | import type IForkTsCheckerWebpackPlugin from 'fork-ts-checker-webpack-plugin'; 2 | 3 | // eslint-disable-next-line @typescript-eslint/no-var-requires 4 | const ForkTsCheckerWebpackPlugin: typeof IForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); 5 | 6 | export const plugins = [ 7 | new ForkTsCheckerWebpackPlugin({ 8 | logger: 'webpack-infrastructure', 9 | }), 10 | ]; 11 | -------------------------------------------------------------------------------- /webpack.renderer.config.ts: -------------------------------------------------------------------------------- 1 | import type { Configuration } from 'webpack'; 2 | 3 | import { rules } from './webpack.rules'; 4 | import { plugins } from './webpack.plugins'; 5 | 6 | rules.push({ 7 | test: /\.css$/, 8 | use: [ 9 | { loader: 'style-loader' }, 10 | { loader: 'css-loader' }, 11 | { loader: 'postcss-loader' }, 12 | ], 13 | }); 14 | 15 | export const rendererConfig: Configuration = { 16 | module: { 17 | rules, 18 | }, 19 | plugins, 20 | resolve: { 21 | extensions: ['.js', '.ts', '.jsx', '.tsx', '.css'], 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /webpack.rules.ts: -------------------------------------------------------------------------------- 1 | import type { ModuleOptions } from 'webpack'; 2 | 3 | export const rules: Required['rules'] = [ 4 | // Add support for native node modules 5 | { 6 | // We're specifying native_modules in the test because the asset relocator loader generates a 7 | // "fake" .node file which is really a cjs file. 8 | test: /native_modules[/\\].+\.node$/, 9 | use: 'node-loader', 10 | }, 11 | // { 12 | // test: /[/\\]node_modules[/\\].+\.(m?js|node)$/, 13 | // parser: { amd: false }, 14 | // use: { 15 | // loader: '@vercel/webpack-asset-relocator-loader', 16 | // options: { 17 | // outputAssetBase: 'native_modules', 18 | // }, 19 | // }, 20 | // }, 21 | { 22 | test: /\.tsx?$/, 23 | exclude: /(node_modules|\.webpack)/, 24 | use: { 25 | loader: 'ts-loader', 26 | options: { 27 | transpileOnly: true, 28 | }, 29 | }, 30 | }, 31 | ]; 32 | --------------------------------------------------------------------------------