├── .github ├── CODEOWNERS └── FUNDING.yml ├── info.json ├── gui ├── assets │ ├── ghub.png │ ├── kofi.png │ ├── pp.png │ ├── wa_bg.png │ ├── favicon.png │ ├── logo_mini.png │ ├── wadark_med.png │ ├── ProductSans-Light.woff │ ├── loader.svg │ ├── ui.js │ └── styles.css └── index.html ├── icons └── wa_dark.ico ├── index.js ├── package.json ├── .gitignore ├── LICENSE ├── README.md └── run-gui.js /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | **/* @m4heshd -------------------------------------------------------------------------------- /info.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.2025.7" 3 | } -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: m4heshd 2 | custom: ['https://www.paypal.me/mpwk'] -------------------------------------------------------------------------------- /gui/assets/ghub.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firefinchdev/whatsapp-desktop-dark/HEAD/gui/assets/ghub.png -------------------------------------------------------------------------------- /gui/assets/kofi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firefinchdev/whatsapp-desktop-dark/HEAD/gui/assets/kofi.png -------------------------------------------------------------------------------- /gui/assets/pp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firefinchdev/whatsapp-desktop-dark/HEAD/gui/assets/pp.png -------------------------------------------------------------------------------- /icons/wa_dark.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firefinchdev/whatsapp-desktop-dark/HEAD/icons/wa_dark.ico -------------------------------------------------------------------------------- /gui/assets/wa_bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firefinchdev/whatsapp-desktop-dark/HEAD/gui/assets/wa_bg.png -------------------------------------------------------------------------------- /gui/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firefinchdev/whatsapp-desktop-dark/HEAD/gui/assets/favicon.png -------------------------------------------------------------------------------- /gui/assets/logo_mini.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firefinchdev/whatsapp-desktop-dark/HEAD/gui/assets/logo_mini.png -------------------------------------------------------------------------------- /gui/assets/wadark_med.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firefinchdev/whatsapp-desktop-dark/HEAD/gui/assets/wadark_med.png -------------------------------------------------------------------------------- /gui/assets/ProductSans-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firefinchdev/whatsapp-desktop-dark/HEAD/gui/assets/ProductSans-Light.woff -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | switch (process.platform) { 2 | case 'win32': 3 | require("./run-gui").startGUI(); 4 | break; 5 | case 'darwin': 6 | require("./run-gui").startGUI(); 7 | break; 8 | default: 9 | console.log('\x1b[31m%s\x1b[0m', 'This platform is not supported.\n'); 10 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "whatsapp-desktop-dark", 3 | "version": "2.2025.7", 4 | "description": "Dark UI for WhatsApp desktop by firefinchdev", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/firefinchdev/whatsapp-desktop-dark.git" 8 | }, 9 | "main": "index.js", 10 | "scripts": { 11 | "test": "echo \"Error: no test specified\" && exit 1", 12 | "run-gui": "node index.js", 13 | "dist-win": "node build.js win32", 14 | "dist-mac": "node build.js darwin" 15 | }, 16 | "keywords": [ 17 | "whatsapp", 18 | "dark", 19 | "night" 20 | ], 21 | "author": "Mahesh Bandara Wijerathna", 22 | "license": "MIT", 23 | "dependencies": { 24 | "asar": "0.14.6", 25 | "express": "4.17.1", 26 | "fs-extra": "8.1.0", 27 | "is-color": "1.0.2", 28 | "open": "6.4.0", 29 | "ps-node": "0.1.6", 30 | "semver": "6.3.0", 31 | "socket.io": "2.3.0" 32 | }, 33 | "devDependencies": { 34 | "nexe": "3.3.2", 35 | "resourcehacker": "4.2.51" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /gui/assets/loader.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 2 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 3 | 4 | # User-specific stuff 5 | .idea/**/workspace.xml 6 | .idea/**/tasks.xml 7 | .idea/**/usage.statistics.xml 8 | .idea/**/dictionaries 9 | .idea/**/shelf 10 | 11 | # Generated files 12 | .idea/**/contentModel.xml 13 | 14 | # Sensitive or high-churn files 15 | .idea/**/dataSources/ 16 | .idea/**/dataSources.ids 17 | .idea/**/dataSources.local.xml 18 | .idea/**/sqlDataSources.xml 19 | .idea/**/dynamic.xml 20 | .idea/**/uiDesigner.xml 21 | .idea/**/dbnavigator.xml 22 | 23 | # Gradle 24 | .idea/**/gradle.xml 25 | .idea/**/libraries 26 | 27 | # Gradle and Maven with auto-import 28 | # When using Gradle or Maven with auto-import, you should exclude module files, 29 | # since they will be recreated, and may cause churn. Uncomment if using 30 | # auto-import. 31 | # .idea/modules.xml 32 | # .idea/*.iml 33 | # .idea/modules 34 | 35 | # CMake 36 | cmake-build-*/ 37 | 38 | # Mongo Explorer plugin 39 | .idea/**/mongoSettings.xml 40 | 41 | # File-based project format 42 | *.iws 43 | 44 | # IntelliJ 45 | out/ 46 | 47 | # mpeltonen/sbt-idea plugin 48 | .idea_modules/ 49 | 50 | # JIRA plugin 51 | atlassian-ide-plugin.xml 52 | 53 | # Cursive Clojure plugin 54 | .idea/replstate.xml 55 | 56 | # Crashlytics plugin (for Android Studio and IntelliJ) 57 | com_crashlytics_export_strings.xml 58 | crashlytics.properties 59 | crashlytics-build.properties 60 | fabric.properties 61 | 62 | # Editor-based Rest Client 63 | .idea/httpRequests 64 | 65 | # Android studio 3.1+ serialized cache file 66 | .idea/caches/build_file_checksums.ser 67 | 68 | # Node 69 | node_modules/ 70 | 71 | # Project 72 | extracted/ 73 | backup/ 74 | app.asar 75 | dist/ 76 | styles/win32/main.js 77 | styles/darwin/main.js -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Mahesh Bandara Wijerathna 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 | 23 | - dark-whatsapp, located at vednoc/dark-whatsapp, is licensed as follows: 24 | """ 25 | MIT License 26 | 27 | Copyright (c) 2018 vednoc 28 | 29 | Permission is hereby granted, free of charge, to any person obtaining a copy 30 | of this software and associated documentation files (the "Software"), to deal 31 | in the Software without restriction, including without limitation the rights 32 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 33 | copies of the Software, and to permit persons to whom the Software is 34 | furnished to do so, subject to the following conditions: 35 | 36 | The above copyright notice and this permission notice shall be included in all 37 | copies or substantial portions of the Software. 38 | 39 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 40 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 41 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 42 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 43 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 44 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 45 | SOFTWARE. 46 | """ -------------------------------------------------------------------------------- /gui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WhatsApp Desktop Dark Mode Installer 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 | Initializing... 17 |
18 |
19 | Working.. 20 |
21 |
22 |
23 |
24 |
25 |
X
26 |
27 | 30 |
31 | {wadark_version} 32 |
33 | (Check for updates) 34 |
35 |
36 |
37 |

⚠ Make sure to keep WhatsApp Desktop running before starting the installation

38 |
39 |
40 |
Restore
41 |
42 |
43 |
Install
44 |
45 |
46 |
47 | Support: 48 |
49 |
50 |
51 | Donate with PayPal 52 |
53 |
54 | Support on Ko-fi 55 |
56 |
57 | Contribute on GitHub 58 |
59 |
60 |
61 |
62 | 63 |
64 | 65 | -------------------------------------------------------------------------------- /gui/assets/ui.js: -------------------------------------------------------------------------------- 1 | //Elements 2 | let overlay = document.querySelector('#overlay'); 3 | let txtOL = document.querySelector('#txtOL'); 4 | let btnRestore = document.querySelector('#btnRestore'); 5 | let btnInstall = document.querySelector('#btnInstall'); 6 | let btnClose = document.querySelector('#btnClose'); 7 | let btnPaypal = document.querySelector('#btnPaypal'); 8 | let btnKofi = document.querySelector('#btnKofi'); 9 | let btnGithub = document.querySelector('#btnGithub'); 10 | let lblVersion = document.querySelector('#lblVersion'); 11 | let lblInfo = document.querySelector('#lblInfo'); 12 | let lnkCheckUpd = document.querySelector('#lnkCheckUpd'); 13 | 14 | //Incoming messages 15 | socket.on('showOL', function (data) { 16 | showOL(data); 17 | }); 18 | 19 | socket.on('hideOL', function () { 20 | hideOL(); 21 | }); 22 | 23 | socket.on('setOLTxt', function (data) { 24 | setOLTxt(data); 25 | }); 26 | 27 | socket.on('setMacUI', function () { 28 | lblInfo.innerHTML = '⚠ Make sure that you have WhatsApp Desktop installed in Applications directory'; 29 | }); 30 | 31 | socket.on('setVersion', function (data) { 32 | setVersion(data); 33 | }); 34 | 35 | socket.on('ask', function (data, fn) { 36 | fn(confirm(data)); 37 | }); 38 | 39 | socket.on('say', function (data) { 40 | alert(data); 41 | }); 42 | 43 | socket.on('endInit', function (data) { 44 | endInit(data); 45 | }); 46 | 47 | socket.on('disconnect', reason => { 48 | console.log(`reason: ${reason}`); 49 | }); 50 | 51 | //UI element events 52 | btnInstall.addEventListener('click', function () { 53 | showOL('Starting installation..'); 54 | socket.emit('startInstall'); 55 | }); 56 | 57 | btnRestore.addEventListener('click', function () { 58 | showOL('Starting restoration..'); 59 | socket.emit('startRestore'); 60 | }); 61 | 62 | btnClose.addEventListener('click', function () { 63 | if (confirm('Are you sure you want to exit the installer?')) { 64 | quit(); 65 | } 66 | }); 67 | 68 | btnPaypal.addEventListener('click', function () { 69 | window.open('https://www.paypal.me/mpwk?locale.x=en_US', '_blank') 70 | }); 71 | 72 | btnKofi.addEventListener('click', function () { 73 | window.open('https://ko-fi.com/m4heshd', '_blank') 74 | }); 75 | 76 | btnGithub.addEventListener('click', function () { 77 | window.open('https://github.com/firefinchdev/whatsapp-desktop-dark', '_blank') 78 | }); 79 | 80 | lnkCheckUpd.addEventListener('click', function () { 81 | socket.emit('checkUpd'); 82 | }); 83 | 84 | //UI Functions 85 | function showOL(text) { 86 | if (text) { 87 | txtOL.innerHTML = text; 88 | } 89 | overlay.style.opacity = '1'; 90 | overlay.style.visibility = 'visible'; 91 | } 92 | 93 | function hideOL() { 94 | overlay.style.opacity = '0'; 95 | overlay.style.visibility = 'hidden'; 96 | } 97 | 98 | function setOLTxt(text) { 99 | txtOL.innerHTML = text; 100 | } 101 | 102 | function setVersion(text) { 103 | lblVersion.innerHTML = text; 104 | } 105 | 106 | function endInit(isBkAvail) { 107 | isBkAvail ? btnRestore.removeAttribute('disabled') : btnRestore.setAttribute('disabled', true); 108 | hideOL(); 109 | } 110 | 111 | function quit() { 112 | let body = document.querySelector('body'); 113 | body.innerHTML = 114 | '
\n' + 115 | ' \n' + 116 | ' Visit on GitHub\n' + 117 | '
\n' + 118 | ' Visit on GitHub\n' + 119 | '
\n' + 120 | '
'; 121 | } 122 | 123 | const pSBC = (p, c0, c1, l) => { 124 | let r, g, b, P, f, t, h, i = parseInt, m = Math.round, a = typeof (c1) == "string"; 125 | if (typeof (p) != "number" || p < -1 || p > 1 || typeof (c0) != "string" || (c0[0] != 'r' && c0[0] != '#') || (c1 && !a)) return null; 126 | if (!this.pSBCr) this.pSBCr = (d) => { 127 | let n = d.length, x = {}; 128 | if (n > 9) { 129 | [r, g, b, a] = d = d.split(","), n = d.length; 130 | if (n < 3 || n > 4) return null; 131 | x.r = i(r[3] == "a" ? r.slice(5) : r.slice(4)), x.g = i(g), x.b = i(b), x.a = a ? parseFloat(a) : -1 132 | } else { 133 | if (n == 8 || n == 6 || n < 4) return null; 134 | if (n < 6) d = "#" + d[1] + d[1] + d[2] + d[2] + d[3] + d[3] + (n > 4 ? d[4] + d[4] : ""); 135 | d = i(d.slice(1), 16); 136 | if (n == 9 || n == 5) x.r = d >> 24 & 255, x.g = d >> 16 & 255, x.b = d >> 8 & 255, x.a = m((d & 255) / 0.255) / 1000; 137 | else x.r = d >> 16, x.g = d >> 8 & 255, x.b = d & 255, x.a = -1 138 | } 139 | return x 140 | }; 141 | h = c0.length > 9, h = a ? c1.length > 9 ? true : c1 == "c" ? !h : false : h, f = this.pSBCr(c0), P = p < 0, t = c1 && c1 != "c" ? this.pSBCr(c1) : P ? { 142 | r: 0, 143 | g: 0, 144 | b: 0, 145 | a: -1 146 | } : {r: 255, g: 255, b: 255, a: -1}, p = P ? p * -1 : p, P = 1 - p; 147 | if (!f || !t) return null; 148 | if (l) r = m(P * f.r + p * t.r), g = m(P * f.g + p * t.g), b = m(P * f.b + p * t.b); 149 | else r = m((P * f.r ** 2 + p * t.r ** 2) ** 0.5), g = m((P * f.g ** 2 + p * t.g ** 2) ** 0.5), b = m((P * f.b ** 2 + p * t.b ** 2) ** 0.5); 150 | a = f.a, t = t.a, f = a >= 0 || t >= 0, a = f ? a < 0 ? t : t < 0 ? a : a * P + t * p : 0; 151 | if (h) return "rgb" + (f ? "a(" : "(") + r + "," + g + "," + b + (f ? "," + m(a * 1000) / 1000 : "") + ")"; 152 | else return "#" + (4294967296 + r * 16777216 + g * 65536 + b * 256 + (f ? m(a * 255) : 0)).toString(16).slice(1, f ? undefined : -2) 153 | }; -------------------------------------------------------------------------------- /gui/assets/styles.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --dark: #272c35; 3 | --dark_alpha: rgba(19, 19, 21, 0.93); 4 | --darker: #1f232a; 5 | --lighter: #e9e9e9; 6 | --accent: #5792ff; 7 | --accent_hover: #497ad5; 8 | --msgout: #131a25; 9 | --border: #808080; 10 | } 11 | 12 | @font-face { 13 | font-family: 'Product Sans Light Regular'; 14 | font-style: normal; 15 | font-weight: normal; 16 | src: local('Product Sans Light Regular'), url('ProductSans-Light.woff') format('woff'); 17 | } 18 | 19 | ::selection { 20 | background: none; 21 | } 22 | 23 | body { 24 | background: var(--msgout); 25 | color: var(--lighter); 26 | text-align: center; 27 | font-family: 'Product Sans Light Regular', sans-serif; 28 | background-image: url(wa_bg.png); 29 | background-repeat: repeat 30 | } 31 | 32 | a { 33 | color: inherit; 34 | text-decoration: none; 35 | } 36 | 37 | .link { 38 | color: var(--accent); 39 | cursor: pointer; 40 | } 41 | 42 | .link:active:hover { 43 | color: var(--accent_hover); 44 | } 45 | 46 | .secLabel { 47 | text-align: left; 48 | margin-bottom: 5px; 49 | margin-left: 7px; 50 | font-size: 12px; 51 | } 52 | 53 | #pnlMainContainer { 54 | background: var(--darker); 55 | width: 400px; 56 | height: 470px; 57 | position: absolute; 58 | top: 0; 59 | bottom: 0; 60 | left: 0; 61 | right: 0; 62 | margin: auto; 63 | padding: 15px; 64 | border-radius: 20px; 65 | box-shadow: 0px 2px 9px -1px rgba(0, 0, 0, 0.2), 0px 8px 9px 4px rgba(0, 0, 0, 0.14), 0px 1px 3px 0px rgba(0, 0, 0, .12); 66 | } 67 | 68 | #overlay { 69 | background: var(--dark_alpha); 70 | width: 100%; 71 | height: 100%; 72 | border-radius: inherit; 73 | position: absolute; 74 | margin: -15px; 75 | backdrop-filter: blur(3px); 76 | visibility: visible; 77 | transition: visibility 0.2s linear, opacity 0.2s linear; 78 | } 79 | 80 | .grid-ol { 81 | height: 100%; 82 | padding: 0 15px 0 15px; 83 | display: grid; 84 | grid-template-columns: 1fr auto 1fr; 85 | grid-template-rows: 1fr auto auto 1fr; 86 | grid-template-areas: 'space1 space1 space1' 'space2 divTxtOL space3' 'space2 divImgOL space3' 'space4 space4 space4'; 87 | } 88 | 89 | #divTxtOL { 90 | grid-area: divTxtOL; 91 | } 92 | 93 | #divImgOL { 94 | grid-area: divImgOL; 95 | } 96 | 97 | #txtOL { 98 | font-size: 20px; 99 | } 100 | 101 | #imgOL { 102 | padding-top: 20px; 103 | } 104 | 105 | .grid-main { 106 | margin: 5px; 107 | display: grid; 108 | grid-template-columns: 1fr 1fr; 109 | grid-column-gap: 15px; 110 | grid-row-gap: 8px; 111 | grid-template-areas: 'divCtl divCtl' 'divImgLogo divImgLogo' 'divLblVersion divLblVersion' 'divCmbTheme divCmbTheme' 'divLblInfo divLblInfo' 'divBtnRestore divBtnInstall' 'divSupport divSupport'; 112 | } 113 | 114 | #divCtl { 115 | grid-area: divCtl; 116 | text-align: end; 117 | } 118 | 119 | #divImgLogo { 120 | grid-area: divImgLogo; 121 | } 122 | 123 | #divLblVersion { 124 | grid-area: divLblVersion; 125 | } 126 | 127 | #divCmbTheme { 128 | grid-area: divCmbTheme; 129 | } 130 | 131 | #divLblInfo { 132 | grid-area: divLblInfo; 133 | } 134 | 135 | #divBtnRestore { 136 | grid-area: divBtnRestore; 137 | } 138 | 139 | #divBtnInstall { 140 | grid-area: divBtnInstall; 141 | } 142 | 143 | #divSupport { 144 | grid-area: divSupport; 145 | margin-top: 10px; 146 | } 147 | 148 | .btn { 149 | cursor: pointer; 150 | padding: 10px 24px; 151 | /*max-width: 60px;*/ 152 | border-radius: 3px; 153 | background: var(--accent); 154 | font-size: 17px; 155 | transition: box-shadow .18s ease-out, background .18s ease-out, color .18s ease-out; 156 | } 157 | 158 | .btn:hover, #btnClose:hover { 159 | box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.06), 0 2px 5px 0 rgba(0, 0, 0, 0.2); 160 | background: var(--accent_hover); 161 | } 162 | 163 | .btn:active:hover, #btnClose:active:hover { 164 | background: var(--dark); 165 | } 166 | 167 | .btn[disabled] { 168 | pointer-events: none; 169 | background: var(--dark); 170 | color: darkgray; 171 | } 172 | 173 | .img-btn { 174 | display: flex; 175 | justify-content: center; 176 | align-items: center; 177 | padding: 0; 178 | margin-right: 10px; 179 | } 180 | 181 | .cmb { 182 | display: block; 183 | font-size: 16px; 184 | font-family: inherit; 185 | /*font-weight: 700;*/ 186 | color: var(--lighter); 187 | line-height: 1.3; 188 | padding: .6em 1.4em .5em .8em; 189 | width: 100%; 190 | max-width: 100%; 191 | box-sizing: border-box; 192 | margin: 0; 193 | box-shadow: 0 1px 0 1px rgba(0, 0, 0, .04); 194 | border-radius: .5em; 195 | border-color: var(--border); 196 | -moz-appearance: none; 197 | -webkit-appearance: none; 198 | appearance: none; 199 | background-color: var(--dark); 200 | background-image: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%235792FF%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E'); 201 | background-repeat: no-repeat, repeat; 202 | background-position: right .7em top 50%, 0 0; 203 | background-size: .65em auto, 100%; 204 | } 205 | 206 | .cmb::-ms-expand { 207 | display: none; 208 | } 209 | 210 | .cmb:hover { 211 | border-color: #888; 212 | } 213 | 214 | .cmb:focus { 215 | border-color: var(--accent); 216 | color: var(--lighter); 217 | outline: none; 218 | } 219 | 220 | #lblInfo { 221 | color: var(--accent) !important; 222 | } 223 | 224 | #btnClose { 225 | float: right; 226 | padding: 5px 10px; 227 | background: none; 228 | font-weight: bold; 229 | font-size: 15px; 230 | } 231 | 232 | #divSupportContent { 233 | border: var(--border) 1px solid; 234 | border-radius: .5em; 235 | padding: 8px; 236 | display: flex; 237 | justify-content: center; 238 | align-items: center; 239 | } 240 | 241 | /* End screen */ 242 | #endScrn { 243 | cursor: pointer; 244 | width: 400px; 245 | height: 430px; 246 | position: absolute; 247 | top: 0; 248 | bottom: 0; 249 | left: 0; 250 | right: 0; 251 | margin: auto; 252 | } 253 | 254 | #endImg { 255 | margin-bottom: 20px; 256 | filter: drop-shadow(5px 5px 5px rgba(0, 0, 0, 0.2)); 257 | } 258 | 259 | #endTxt { 260 | font-size: 30px; 261 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |

3 |
4 | WhatsApp Desktop Dark Mode by firefinchdev 5 |
6 | WhatsApp Desktop Dark Mode by firefinchdev 7 |
8 | This is a fork of WhatsApp Desktop Dark Mode by m4heshd 9 |
10 | Total downloads 11 | 12 | Closed issues 13 | 14 | 15 | Join the chat on Telegram 16 | 17 |
18 |

19 | 20 | Stable version: [**`2.2025.7`**](https://github.com/firefinchdev/whatsapp-desktop-dark/releases/tag/2.2025.7) (Check if this version matches with your version of WhatsApp. The mod will still work even if the versions doesn't match) 21 | 22 | This mod is a simple tweak for [WhatsApp Desktop **Official** version](https://www.whatsapp.com/download/) to enable dark mode until it is released officially. It works for Windows and macOS. 23 | 24 | ⚠ **Please note that this script won't work with Microsoft store or App store version of WhatsApp desktop. You need to use the direct downloaded version linked above** 25 | 26 | ### DISCLAIMER: 27 | > **This is not an official version of WhatsApp Desktop Dark mode. So make sure to read through this document before you use this mod because i won't be responsible for any damage you've done yourself trying to install this mod (It's not even that hard 🙄). The coding/styling behind this tweak has full transparency since I've made it open-source right here.** 28 | 29 | ### Steps to Install: 30 | 1. [Download](https://github.com/firefinchdev/whatsapp-desktop-dark#downloads) the files. 31 | 2. [Run](https://github.com/firefinchdev/whatsapp-desktop-dark#installation) the downloaded files. 32 | 3. Open WhatsApp Desktop -> Settings -> Chats -> Theme and select Dark. 33 | 34 | # Is this safe? 35 | The answer is right there on the top. **This is an open source project**. which literally means that you can see through the source code of this mod. So **I CANNOT STEAL YOUR MESSEGES, I CANNOT TAP INTO YOUR CALLS**. If you're like one of the "Anti-Vaccination" parents, please avoid using this mod. Wouldn't you rather use the official WhatsApp version with a simple tweak like this than using some random third-party app just to have a proper dark UI? 36 | 37 | **NOTE:** This script will not replace any of the functionality related coding inside WhatsApp. Just the styling. 38 | 39 | # Downloads 40 | 41 | **NOTE:** *THIS IS NOT A MODDED VERSION OF WhatsApp*. WhatsApp Desktop Dark Mode is a simple set of styling that needs to be injected to the source files of your current installation of WhatsApp. I've made it much easier by creating a installation script using NodeJS and made it easier furthermore by creating simple executable files using [nexe](https://github.com/nexe/nexe). If you don't trust the executable files I've provided below, you can build and run it from the source code itself by following the instructions in the next section. 42 | 43 | - [All releases (with source)](https://github.com/firefinchdev/whatsapp-desktop-dark/releases) 44 | - Windows (x86 & x64) - [Download](https://github.com/firefinchdev/whatsapp-desktop-dark/releases/download/2.2025.7/WADark-2.2025.7-Windows.zip) 45 | - macOS (x64) - [Download](https://github.com/firefinchdev/whatsapp-desktop-dark/releases/download/2.2025.7/WADark-2.2025.7-macOS.zip) 46 | 47 | ## Installation 48 | 49 | Make sure you have [WhatsApp Desktop](https://www.whatsapp.com/download/) installed in your PC or mac first. 50 | 51 | ⚠ **Please note that this script won't work with Microsoft store or App store version of WhatsApp desktop. You need to use the direct downloaded version linked above** 52 | 53 | Make sure WhatsApp Desktop is running before you start the installation. Then wait for the process to finish. That's it. 54 | 55 | Simply extract the downloaded ZIP file and run `WADark.exe` on windows. For macOS you'll need to make `WADark` executable first, by executing `chmod +x WADark` from the root of extracted directory. Then run it with `WADark` from the same directory. 56 | 57 | **NOTE:** It's recommended not to delete the script folder after the installation because the script automatically creates a backup of your original WhatsApp source in case you need to revert back to the original version. 58 | 59 | ### How to remove the mod? 60 | Simply rerun the script and it will ask you to restore the backup made by any previous installations. (Or use the button on top left to switch to light mode) 61 | 62 | ### My WhatsApp turned back to light theme after sometime. What to do? 63 | This means that your WhatsApp build is probably auto updated to a newer version. Rerun the script and It'll tell you if a new version is available. Even if the dark mode version is not updated, just rerun the script and install the dark mode again. 64 | 65 | # Build the installation script yourself 66 | 67 | Building process is really simple even if you're not a pro developer. You will just need [NodeJS](https://nodejs.org) and [NPM](https://www.npmjs.com/) (Usually packed with NodeJS) installed on your computer to build and run the script yourself. 68 | 69 | Clone or download this repository, `cd` to that directory and enter the following commands to start the script. There are two methods you can run the script. 70 | 71 | **Method 1 (Build binaries)** 72 | 73 | 1. `npm install` (Wait for this to finish) 74 | 2. `npm run dist-win` (Windows) or `npm run dist-mac` (macOS) 75 | 3. Finally locate and run the binary file created in `dist/$PLATFORM/` directory. 76 | 77 | **Method 2 (Run directly from source)** 78 | 79 | 1. `npm install` (Wait for this to finish) 80 | 2. `npm run run-gui` for GUI installer 81 | 82 | Simple as that. 83 | 84 | # Support this project 85 | 86 | Involvement as a contributor by adding a few lines of code, fixing a bug, responding to issues, testing etc.. would be one of the most helpful methods you could support the project. If you're not a programmer but a generous person, you can send a small donation this way buy clicking the following button. 87 | 88 | [![Donate to m4heshd](https://i.ibb.co/3vQTMts/paypal-donate-icon-7.png)](https://www.paypal.me/mpwk?locale.x=en_US) 89 | 90 | Or you can buy me a "ko-fi" by clicking this button 91 | 92 | [![ko-fi](https://i.ibb.co/QmQknmc/ko-fi.png)](https://ko-fi.com/m4heshd) 93 | 94 | # Got an Issue? 95 | 96 | [Follow this link](https://github.com/firefinchdev/whatsapp-desktop-dark/issues) to submit your issues and please remember to be descriptive when submitting issues. Also don't forget to attach a small screenshot if the issue is style/GUI related. 97 | 98 | # Known issues 99 | 100 | **Windows:** 101 | 102 | - Automatically killing the WhatsApp process might take a little time or the process might repeat itself [`unfixable/SW-HW dependent`] 103 | 104 | # License 105 | "WhatsApp Desktop Dark Mode by firefinchdev" is licensed under [MIT License](LICENSE). So you are allowed to use freely and modify the application. I will not be responsible for any outcome. Proceed with any action at your own risk. 106 | -------------------------------------------------------------------------------- /run-gui.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra'); 2 | const nodefs = require("fs"); 3 | const ps = require('ps-node'); 4 | const request = require('https').request; 5 | const {URL} = require('url'); 6 | const semver = require('semver'); 7 | const asar = require('asar'); 8 | const path = require('path'); 9 | const {spawn} = require("child_process"); 10 | const open = require('open'); 11 | const express = require('express'); 12 | const xApp = express(); 13 | let io = require('socket.io'); 14 | 15 | let client = null; 16 | let version = '0'; 17 | let started = false; 18 | let updURL = 'https://raw.githubusercontent.com/firefinchdev/whatsapp-desktop-dark/master/package.json'; 19 | let bkPath = path.join(__dirname, 'backup', 'app.asar'); 20 | let WAPath = null; 21 | let platform = process.platform; 22 | let execPath = '/Applications/WhatsApp.app/Contents/MacOS/WhatsApp'; 23 | let command = 'WhatsApp.exe'; 24 | let psargs = 'ux'; 25 | 26 | if (platform === 'darwin') { 27 | WAPath = '/Applications/WhatsApp.app/Contents/Resources/app.asar'; 28 | command = execPath; 29 | psargs = 'ax'; 30 | } 31 | 32 | //Backend setup 33 | exports.startGUI = function () { 34 | xApp.use(express.static(path.join(__dirname, 'gui'))); 35 | 36 | let xServ = xApp.listen(3210, function () { 37 | console.log('WADark GUI installer backend started'); 38 | openInstaller(); 39 | }); 40 | 41 | io = io(xServ, { 42 | pingTimeout: 90000 43 | }); 44 | 45 | io.on('connection', function (socket) { 46 | if (!started) { 47 | client = socket; 48 | console.log('WADark installer client connected. ID - ' + socket.id); 49 | 50 | //Incoming messages 51 | client.on('startInstall', function () { 52 | setOLTxt('Identifying process..'); 53 | if (fs.existsSync(bkPath)) { 54 | ask('Current backup will be replaced and it cannot be undone. Are you sure want to continue?', function () { 55 | validateAndStart(false); 56 | }, function () { 57 | say('Hope you\'re enjoying WhatsApp dark.. :)'); 58 | hideOL(); 59 | }); 60 | } else { 61 | validateAndStart(false); 62 | } 63 | }); 64 | 65 | client.on('startRestore', function () { 66 | setOLTxt('Identifying process..'); 67 | if (fs.existsSync(bkPath)) { 68 | validateAndStart(true); 69 | } else { 70 | say('Unable to locate the backup file'); 71 | hideOL(); 72 | } 73 | }); 74 | 75 | client.on('endApp', function () { 76 | console.log('Quitting the installer..\n'); 77 | client.disconnect(); 78 | process.exit(0); 79 | }); 80 | 81 | client.on('checkUpd', function () { 82 | checkAppUpd(false); 83 | }); 84 | 85 | if (platform === 'darwin') { 86 | setMacUI(); 87 | } 88 | 89 | startInit(); 90 | } else { 91 | console.log('Client connection rejected. ID - ' + socket.id); 92 | socket.emit('setOLTxt', 'One instance of the process is already running.. Please restart if this is an error.'); 93 | } 94 | }); 95 | }; 96 | 97 | //Backend functions 98 | function startInit() { 99 | showOL('Reading version info..'); 100 | fs.readJson(path.join(__dirname, 'info.json'), (error, infoJSON) => { 101 | if (!error) { 102 | version = infoJSON.version; 103 | // version = "0.3.4940"; 104 | setVersion('v' + version); 105 | checkAppUpd(true); 106 | } else { 107 | console.log(error); 108 | start(); 109 | } 110 | }); 111 | } 112 | 113 | function checkAppUpd(isStart) { 114 | showOL('Checking for updates..'); 115 | let req = request(new URL(updURL), function (res) { 116 | 117 | res.on('data', (d) => { 118 | let latest = JSON.parse(d.toString())['version']; 119 | if (semver.lt(version, latest)) { 120 | ask('A new update is available (v' + latest + '). Would you like to download?', openDownload, start); 121 | } else { 122 | if (isStart) { 123 | start(); 124 | } else { 125 | hideOL(); 126 | say('Your script copy is up to date.'); 127 | } 128 | } 129 | }); 130 | }); 131 | 132 | req.on('error', (e) => { 133 | console.log(e); 134 | if (isStart) { 135 | start(); 136 | } else { 137 | hideOL(); 138 | } 139 | }); 140 | 141 | req.end(); 142 | } 143 | 144 | function start() { 145 | endInit(fs.existsSync(bkPath)); 146 | } 147 | 148 | function validateAndStart(isRestore) { 149 | if (platform === 'darwin') { 150 | if (fs.existsSync(WAPath)) { 151 | startInstall(isRestore); 152 | } else { 153 | say('Unable to locate your WhatsApp Desktop installation. Check again and retry.'); 154 | hideOL(); 155 | } 156 | } else { 157 | startInstall(isRestore); 158 | } 159 | } 160 | 161 | function startInstall(isRestore) { 162 | started = true; 163 | ps.lookup({ 164 | command: command, 165 | psargs: psargs 166 | }, function (err, resultList) { 167 | if (err) { 168 | console.log(err); 169 | } else { 170 | if (resultList.length) { 171 | setOLTxt('Please close WhatsApp Desktop manually to continue installation..
(Please wait if you already have)'); 172 | if (platform === 'win32') { 173 | WAPath = resultList[0].command; 174 | execPath = WAPath; 175 | } 176 | startInstall(isRestore); 177 | } else { 178 | if (WAPath) { 179 | if (isRestore) { 180 | restoreBackup(WAPath); 181 | } else { 182 | applyDarkStyles(WAPath); 183 | } 184 | } else { 185 | say('WhatsApp process not found. Make sure WhatsApp desktop is running before installing dark mode.'); 186 | hideOL(); 187 | started = false; 188 | } 189 | } 190 | 191 | } 192 | }); 193 | } 194 | 195 | function applyDarkStyles(procPath) { 196 | try { 197 | let dir = path.dirname(procPath); 198 | let fullpath = path.join(dir, 'resources', 'app.asar'); 199 | 200 | if (platform === 'darwin') { 201 | fullpath = procPath; 202 | } 203 | 204 | setOLTxt('Backing up..'); 205 | fs.copySync(fullpath, bkPath); 206 | 207 | setOLTxt('Extracting..'); 208 | let extPath = path.join(__dirname, 'extracted'); 209 | asar.extractAll(fullpath, extPath); 210 | 211 | setOLTxt('Injecting styles..'); 212 | let indexPath = path.join(extPath, "index.html"); 213 | let rendererPath = path.join(extPath, "renderer.js"); 214 | if (fs.existsSync(rendererPath)) { 215 | try { 216 | let updatedRenderer = nodefs 217 | .readFileSync(rendererPath) 218 | .toString() 219 | .replace("DARK_MODE: !prod", "DARK_MODE: true"); 220 | 221 | nodefs.writeFileSync(rendererPath, updatedRenderer); 222 | 223 | let newAsar = path.join(__dirname, 'app.asar'); 224 | asar.createPackage(extPath, newAsar, function () { 225 | setOLTxt('Replacing files..'); 226 | try { 227 | fs.copySync(newAsar, fullpath); 228 | 229 | setOLTxt('Cleaning up..'); 230 | fs.removeSync(extPath); 231 | fs.removeSync(newAsar); 232 | 233 | say('All done. Go to WhatsApp Desktop -> Settings -> Chats -> Themes and select Dark'); 234 | 235 | let WAPP = spawn(execPath, [], { 236 | detached: true, 237 | stdio: ['ignore', 'ignore', 'ignore'] 238 | }); 239 | WAPP.unref(); 240 | startInit(); 241 | started = false; 242 | } catch (error) { 243 | console.log(error); 244 | setOLTxt('An error occurred. Cleaning up..'); 245 | fs.removeSync(extPath); 246 | fs.removeSync(newAsar); 247 | startInit(); 248 | started = false; 249 | } 250 | }); 251 | } catch (error) { 252 | console.log(error); 253 | hideOL(); 254 | started = false; 255 | } 256 | } else { 257 | say('\x1b[31m%s\x1b[0m', 'Failed to extract WhatsApp source.'); 258 | hideOL(); 259 | started = false; 260 | } 261 | } catch (error) { 262 | console.log(error); 263 | hideOL(); 264 | started = false; 265 | } 266 | } 267 | 268 | function restoreBackup(procPath) { 269 | setOLTxt('Restoring original version of the application...'); 270 | started = true; 271 | let dir = path.dirname(procPath); 272 | let fullpath = path.join(dir, 'resources', 'app.asar'); 273 | 274 | if (platform === 'darwin') { 275 | fullpath = procPath; 276 | } 277 | 278 | try { 279 | fs.copySync(bkPath, fullpath); 280 | 281 | say('All done. Make sure to let the developers know if something was wrong.. :)'); 282 | let WAPP = spawn(execPath, [], { 283 | detached: true, 284 | stdio: ['ignore', 'ignore', 'ignore'] 285 | }); 286 | WAPP.unref(); 287 | hideOL(); 288 | started = false; 289 | } catch (error) { 290 | say('An error occurred while restoring.'); 291 | console.log(error); 292 | hideOL(); 293 | started = false; 294 | } 295 | } 296 | 297 | function openDownload() { 298 | (async () => { 299 | await open('https://github.com/firefinchdev/whatsapp-desktop-dark/releases/latest'); 300 | })(); 301 | } 302 | 303 | function openInstaller() { 304 | (async () => { 305 | await open('http://127.0.0.1:3210/'); 306 | })(); 307 | } 308 | 309 | //Client functions 310 | function setOLTxt(text) { 311 | client.emit('setOLTxt', text); 312 | } 313 | 314 | function showOL(text) { 315 | client.emit('showOL', text); 316 | } 317 | 318 | function hideOL() { 319 | client.emit('hideOL'); 320 | } 321 | 322 | function setMacUI() { 323 | client.emit('setMacUI'); 324 | } 325 | 326 | function setVersion(ver) { 327 | client.emit('setVersion', ver); 328 | } 329 | 330 | function endInit(isBkAvail) { 331 | client.emit('endInit', isBkAvail); 332 | } 333 | 334 | function ask(text, yes, no) { 335 | client.emit('ask', text, function (resp) { 336 | if (resp) { 337 | yes(); 338 | } else { 339 | no(); 340 | } 341 | }); 342 | } 343 | 344 | function say(msg) { 345 | client.emit('say', msg); 346 | } --------------------------------------------------------------------------------