├── .babelrc ├── .eslintrc ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ └── issue-report.md └── workflows │ ├── build.yml │ ├── deploy.yml │ └── publish-wiki.yaml ├── .gitignore ├── .npmignore ├── .prettierrc ├── .releaserc.yml ├── .vscode ├── extensions.json ├── launch.json └── tasks.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── __mocks__ ├── base │ └── common │ │ ├── color.js │ │ └── platform.js └── electron.js ├── __test__ └── titlebar.test.js ├── docs ├── CSS-Customization.md ├── Colors.md ├── Get-Started.md ├── Home.md ├── Menu-Icons.md ├── Menubar-Options.md ├── Titlebar-Methods.md ├── Titlebar-Options.md ├── _Footer.md └── _Sidebar.md ├── example ├── assets │ ├── home.png │ ├── icons.json │ ├── logo.svg │ ├── run.png │ └── terminal.png ├── index.html ├── main.js ├── preload.js ├── renderer.js └── styles.css ├── jest.config.js ├── package.json ├── pnpm-lock.yaml ├── revert.sh ├── screenshots ├── 262shots_so.jpg ├── 544shots_so.jpg ├── 70shots_so.jpg └── 780shots_so.jpg ├── src ├── base │ ├── browser │ │ ├── browser.ts │ │ ├── event.ts │ │ ├── keyboardEvent.ts │ │ ├── mouseEvent.ts │ │ └── touch.ts │ └── common │ │ ├── arrays.ts │ │ ├── async.ts │ │ ├── charCode.ts │ │ ├── color.ts │ │ ├── decorators.ts │ │ ├── dom.ts │ │ ├── event.ts │ │ ├── iterator.ts │ │ ├── keyCodes.ts │ │ ├── lifecycle.ts │ │ ├── linkedList.ts │ │ ├── platform.ts │ │ └── strings.ts ├── consts.ts ├── index.ts ├── main │ ├── attach-titlebar-to-window.ts │ ├── index.ts │ └── setup-titlebar.ts ├── menubar │ ├── index.ts │ ├── menu │ │ ├── index.ts │ │ ├── item.ts │ │ ├── separator.ts │ │ └── submenu.ts │ └── menubar-options.ts └── titlebar │ ├── index.ts │ ├── options.ts │ └── themebar.ts ├── static └── theme │ ├── base.css │ ├── mac.css │ └── win.css └── tsconfig.json /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "rewire", 4 | [ 5 | "module-resolver", 6 | { 7 | "root": [ 8 | "./dist" 9 | ], 10 | "alias": { 11 | "base": "./dist/base", 12 | "static": "./static" 13 | }, 14 | "extensions": [ 15 | ".js" 16 | ] 17 | } 18 | ], 19 | [ 20 | "import-require-as-string", 21 | { 22 | "extensions": [ 23 | ".css" 24 | ] 25 | } 26 | ] 27 | ] 28 | } -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es2022": true, 4 | "node": true, 5 | "browser": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:@typescript-eslint/eslint-recommended", 10 | "standard" 11 | ], 12 | "parser": "@typescript-eslint/parser", 13 | "parserOptions": { 14 | "ecmaVersion": "latest", 15 | "sourceType": "module" 16 | }, 17 | "rules": { 18 | "indent": ["warn", "tab"], 19 | "no-unused-vars": "off", 20 | "no-multiple-empty-lines": "off", 21 | "no-tabs": "off", 22 | "quotes": ["warn", "single"], 23 | "eol-last": "off", 24 | "space-before-function-paren": "off", 25 | "@typescript-eslint/no-explicit-any": "off", 26 | "no-undef": "off", 27 | "no-redeclare": "off", 28 | "no-useless-constructor": "off", 29 | "no-dupe-class-members": "off", 30 | "no-extra-boolean-cast": "off" 31 | } 32 | } -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: AlexTorresDev 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Issue report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Problem Description 11 | 12 | [Describe the problem here clearly and concisely. Provide details about what is happening and how it affects the project's functionality.] 13 | 14 | ## Steps to Reproduce 15 | 16 | 1. [List the specific steps to reproduce the issue.] 17 | 2. [If possible, provide links, screenshots, or relevant code snippets.] 18 | 19 | ## Expected Behavior 20 | 21 | [Explain what the expected behavior of the project should be in this situation.] 22 | 23 | ## Current Behavior 24 | 25 | [Describe how the project is currently behaving instead of the expected behavior. Include any error messages, warnings, or other relevant details.] 26 | 27 | ## Additional Information 28 | 29 | [Provide any other relevant information, such as your operating system, version of the project you are using, versions of dependencies, etc.] 30 | 31 | ## Tentative Solution Steps 32 | 33 | [If you have any ideas on how to fix this issue, share them here. If not, don't worry, the project team can help find a solution.] 34 | 35 | ## Screenshots 36 | 37 | [If relevant, you can attach screenshots that demonstrate the problem.] 38 | 39 | ## Additional Context 40 | 41 | [Add any other context that might be useful for understanding the issue, such as recent code changes, events that might have triggered the problem, etc.] 42 | 43 | > **Note:** 44 | Labels - Select the appropriate labels that best describe this issue, such as "bug", "enhancement", "support", etc. 45 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | types: [opened, synchronize] 11 | 12 | workflow_dispatch: 13 | 14 | permissions: 15 | contents: write 16 | id-token: write 17 | 18 | concurrency: 19 | group: ${{ github.workflow }}-${{ github.ref }} 20 | cancel-in-progress: true 21 | 22 | jobs: 23 | build: 24 | name: 📦 Build 25 | runs-on: ubuntu-22.04 26 | steps: 27 | - name: Checkout repository 28 | uses: actions/checkout@v3 29 | - name: Install pnpm 30 | uses: pnpm/action-setup@v2 31 | with: 32 | version: 7 33 | run_install: false 34 | - name: Install Node.js 35 | uses: actions/setup-node@v3 36 | with: 37 | node-version: "lts/*" 38 | cache: pnpm 39 | - run: pnpm install --no-frozen-lockfile 40 | - name: Build 41 | run: pnpm build 42 | - name: Test 43 | run: pnpm test 44 | - name: Upload artifact 45 | uses: actions/upload-artifact@v3 46 | with: 47 | name: build-artifact 48 | path: dist 49 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | permissions: 7 | contents: write 8 | id-token: write 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.ref }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | release: 16 | name: 🚀 Deploy 17 | if: github.ref == 'refs/heads/main' 18 | runs-on: ubuntu-22.04 19 | steps: 20 | - name: Checkout repository 21 | uses: actions/checkout@v3 22 | with: 23 | fetch-depth: 0 24 | persist-credentials: false 25 | - name: Download artifact 26 | uses: dawidd6/action-download-artifact@v2 27 | with: 28 | workflow: build.yml 29 | github_token: ${{ secrets.PAT }} 30 | name: build-artifact 31 | path: dist 32 | - name: Install Node.js 33 | uses: actions/setup-node@v3 34 | with: 35 | node-version: "lts/*" 36 | - name: Set correct path 37 | run: sed -i "s/\/dist//g" package.json 38 | - name: Copy files 39 | run: | 40 | cp README.md dist 41 | cp LICENSE dist 42 | cp package.json dist 43 | cp .npmignore dist 44 | - name: Semantic Release 45 | uses: cycjimmy/semantic-release-action@v3 46 | with: 47 | extra_plugins: | 48 | @semantic-release/changelog 49 | @semantic-release/git 50 | env: 51 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 52 | GITHUB_TOKEN: ${{ secrets.PAT }} 53 | GIT_AUTHOR_NAME: 'GitHub Actions' 54 | GIT_AUTHOR_EMAIL: 'action@github.com' 55 | GIT_COMMITTER_NAME: 'GitHub Actions' 56 | GIT_COMMITTER_EMAIL: 'action@github.com' 57 | -------------------------------------------------------------------------------- /.github/workflows/publish-wiki.yaml: -------------------------------------------------------------------------------- 1 | name: Publish wiki 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - docs/** 9 | 10 | workflow_dispatch: 11 | 12 | permissions: 13 | contents: write 14 | 15 | concurrency: 16 | group: publish-wiki 17 | cancel-in-progress: true 18 | 19 | jobs: 20 | publish-wiki: 21 | runs-on: ubuntu-20.04 22 | steps: 23 | - uses: actions/checkout@v3 24 | - uses: Andrew-Chen-Wang/github-wiki-action@v4 25 | with: 26 | path: docs 27 | token: ${{ secrets.PAT }} 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Intellij ### 2 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 3 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 4 | 5 | # User-specific stuff 6 | .idea/**/workspace.xml 7 | .idea/**/tasks.xml 8 | .idea/**/usage.statistics.xml 9 | .idea/**/dictionaries 10 | .idea/**/shelf 11 | 12 | # Generated files 13 | .idea/**/contentModel.xml 14 | 15 | # Sensitive or high-churn files 16 | .idea/**/dataSources/ 17 | .idea/**/dataSources.ids 18 | .idea/**/dataSources.local.xml 19 | .idea/**/sqlDataSources.xml 20 | .idea/**/dynamic.xml 21 | .idea/**/uiDesigner.xml 22 | .idea/**/dbnavigator.xml 23 | 24 | # Gradle 25 | .idea/**/gradle.xml 26 | .idea/**/libraries 27 | 28 | # Gradle and Maven with auto-import 29 | # When using Gradle or Maven with auto-import, you should exclude module files, 30 | # since they will be recreated, and may cause churn. Uncomment if using 31 | # auto-import. 32 | # .idea/artifacts 33 | # .idea/compiler.xml 34 | # .idea/jarRepositories.xml 35 | # .idea/modules.xml 36 | # .idea/*.iml 37 | # .idea/modules 38 | # *.iml 39 | # *.ipr 40 | 41 | # CMake 42 | cmake-build-*/ 43 | 44 | # Mongo Explorer plugin 45 | .idea/**/mongoSettings.xml 46 | 47 | # File-based project format 48 | *.iws 49 | 50 | # IntelliJ 51 | out/ 52 | 53 | # mpeltonen/sbt-idea plugin 54 | .idea_modules/ 55 | 56 | # JIRA plugin 57 | atlassian-ide-plugin.xml 58 | 59 | # Cursive Clojure plugin 60 | .idea/replstate.xml 61 | 62 | # Crashlytics plugin (for Android Studio and IntelliJ) 63 | com_crashlytics_export_strings.xml 64 | crashlytics.properties 65 | crashlytics-build.properties 66 | fabric.properties 67 | 68 | # Editor-based Rest Client 69 | .idea/httpRequests 70 | 71 | # Android studio 3.1+ serialized cache file 72 | .idea/caches/build_file_checksums.ser 73 | 74 | ### Intellij Patch ### 75 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 76 | 77 | # *.iml 78 | # modules.xml 79 | # .idea/misc.xml 80 | # *.ipr 81 | 82 | # Sonarlint plugin 83 | # https://plugins.jetbrains.com/plugin/7973-sonarlint 84 | .idea/**/sonarlint/ 85 | 86 | # SonarQube Plugin 87 | # https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin 88 | .idea/**/sonarIssues.xml 89 | 90 | # Markdown Navigator plugin 91 | # https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced 92 | .idea/**/markdown-navigator.xml 93 | .idea/**/markdown-navigator-enh.xml 94 | .idea/**/markdown-navigator/ 95 | 96 | # Cache file creation bug 97 | # See https://youtrack.jetbrains.com/issue/JBR-2257 98 | .idea/$CACHE_FILE$ 99 | 100 | # CodeStream plugin 101 | # https://plugins.jetbrains.com/plugin/12206-codestream 102 | .idea/codestream.xml 103 | 104 | ### Node ### 105 | # Logs 106 | logs 107 | *.log 108 | npm-debug.log* 109 | yarn-debug.log* 110 | yarn-error.log* 111 | lerna-debug.log* 112 | 113 | # Diagnostic reports (https://nodejs.org/api/report.html) 114 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 115 | 116 | # Runtime data 117 | pids 118 | *.pid 119 | *.seed 120 | *.pid.lock 121 | 122 | # Directory for instrumented libs generated by jscoverage/JSCover 123 | lib-cov 124 | 125 | # Coverage directory used by tools like istanbul 126 | coverage 127 | *.lcov 128 | 129 | # nyc test coverage 130 | .nyc_output 131 | 132 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 133 | .grunt 134 | 135 | # Bower dependency directory (https://bower.io/) 136 | bower_components 137 | 138 | # node-waf configuration 139 | .lock-wscript 140 | 141 | # Compiled binary addons (https://nodejs.org/api/addons.html) 142 | build/Release 143 | 144 | # Dependency directories 145 | node_modules/ 146 | jspm_packages/ 147 | 148 | # TypeScript v1 declaration files 149 | typings/ 150 | 151 | # TypeScript cache 152 | *.tsbuildinfo 153 | 154 | # Optional npm cache directory 155 | .npm 156 | 157 | # Optional eslint cache 158 | .eslintcache 159 | 160 | # Microbundle cache 161 | .rpt2_cache/ 162 | .rts2_cache_cjs/ 163 | .rts2_cache_es/ 164 | .rts2_cache_umd/ 165 | 166 | # Optional REPL history 167 | .node_repl_history 168 | 169 | # Output of 'npm pack' 170 | *.tgz 171 | 172 | # Yarn Integrity file 173 | .yarn-integrity 174 | 175 | # dotenv environment variables file 176 | .env 177 | .env.test 178 | .env*.local 179 | 180 | # parcel-bundler cache (https://parceljs.org/) 181 | .cache 182 | .parcel-cache 183 | 184 | # Next.js build output 185 | .next 186 | 187 | # Nuxt.js build / generate output 188 | .nuxt 189 | dist 190 | build/ 191 | *.map 192 | index.js 193 | main.js 194 | 195 | # Gatsby files 196 | .cache/ 197 | # Comment in the public line in if your project uses Gatsby and not Next.js 198 | # https://nextjs.org/blog/next-9-1#public-directory-support 199 | # public 200 | 201 | # vuepress build output 202 | .vuepress/dist 203 | 204 | # Serverless directories 205 | .serverless/ 206 | 207 | # FuseBox cache 208 | .fusebox/ 209 | 210 | # DynamoDB Local files 211 | .dynamodb/ 212 | 213 | # TernJS port file 214 | .tern-port 215 | 216 | # Stores VSCode versions used for testing VSCode extensions 217 | .vscode-test 218 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .git/ 2 | 3 | # for WebStorm 4 | .idea/ 5 | 6 | # for npm 7 | .vscode/ 8 | .github/ 9 | node_modules/ 10 | npm-shrinkwrap.json 11 | 12 | # for grunt 13 | typings/ 14 | .tscache/ 15 | espowered/ 16 | 17 | # for https://github.com/Microsoft/TypeScript/issues/4667 18 | dist/**/*.ts 19 | !dist/**/*.d.ts 20 | 21 | # codes 22 | test/ 23 | 24 | # misc 25 | example/ 26 | screenshots/ 27 | src/ 28 | static/ 29 | docs/ 30 | .gitignore 31 | .editorconfig 32 | circle.yml 33 | setup.sh 34 | Gruntfile.js 35 | tslint.json 36 | tsconfig.json 37 | npm-debug.log 38 | .babelrc 39 | .eslintrc 40 | .prettierrc -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 2, 3 | "semi": false, 4 | "singleQuote": true, 5 | "trailingComma": "none" 6 | } -------------------------------------------------------------------------------- /.releaserc.yml: -------------------------------------------------------------------------------- 1 | branches: main 2 | plugins: 3 | - "@semantic-release/commit-analyzer" 4 | - "@semantic-release/release-notes-generator" 5 | - - "@semantic-release/npm" 6 | - pkgRoot: dist 7 | - - "@semantic-release/changelog" 8 | - changelogFile: CHANGELOG.md 9 | changelogTitle: "# Changelog" 10 | - - "@semantic-release/git" 11 | - assets: 12 | - CHANGELOG.md 13 | - package.json 14 | message: "chore(release): :bookmark: ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" 15 | - "@semantic-release/github" 16 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "usernamehw.errorlens", 4 | "dbaeumer.vscode-eslint", 5 | "pflannery.vscode-versionlens", 6 | ] 7 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Electron Main", 6 | "request": "launch", 7 | "type": "node", 8 | "presentation": { 9 | "hidden": true, 10 | }, 11 | "runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron", 12 | "preLaunchTask": "npm: build", 13 | "args": [ 14 | "--remote-debugging-port=9223", 15 | "./example/main.js" 16 | ], 17 | "outFiles": [ 18 | "${workspaceFolder}/example/**/*.js" 19 | ] 20 | }, 21 | { 22 | "name": "Electron Renderer", 23 | "type": "chrome", 24 | "request": "attach", 25 | "presentation": { 26 | "hidden": true, 27 | }, 28 | "port": 9223, 29 | "urlFilter": "*index.html", 30 | "timeout": 30000, 31 | "skipFiles": [ 32 | "${workspaceFolder}/node_modules/**/*.js", 33 | "${workspaceFolder}/dist/**/*.js", 34 | "/**/*.js" 35 | ], 36 | "trace": true, 37 | "smartStep": true, 38 | "showAsyncStacks": true, 39 | "webRoot": "${workspaceFolder}", 40 | } 41 | ], 42 | "compounds": [ 43 | { 44 | "name": "Debug CET", 45 | "configurations": [ 46 | "Electron Main", 47 | "Electron Renderer" 48 | ] 49 | } 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "build", 9 | "group": "build", 10 | "problemMatcher": [], 11 | "label": "npm: build", 12 | "detail": "tsc && babel ./dist --out-dir ./dist --extensions \".js\"" 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [4.2.8](https://github.com/AlexTorresDev/custom-electron-titlebar/compare/v4.2.7...v4.2.8) (2024-01-15) 4 | 5 | 6 | ### Bug Fixes 7 | 8 | * **unfocusEffect:** :bug: Fix unfocusEffect on titlebar ([45c6917](https://github.com/AlexTorresDev/custom-electron-titlebar/commit/45c6917c41743ba51fd6f44c3102f85600d66392)) 9 | 10 | ## [4.2.7](https://github.com/AlexTorresDev/custom-electron-titlebar/compare/v4.2.6...v4.2.7) (2023-08-13) 11 | 12 | 13 | ### Bug Fixes 14 | 15 | * **window-controls:** :adhesive_bandage: Fix Maximization button ([38740fc](https://github.com/AlexTorresDev/custom-electron-titlebar/commit/38740fcd9450b9b02654005f562a13c81bbc180e)), closes [#235](https://github.com/AlexTorresDev/custom-electron-titlebar/issues/235) 16 | 17 | ## [4.2.6](https://github.com/AlexTorresDev/custom-electron-titlebar/compare/v4.2.5...v4.2.6) (2023-08-10) 18 | 19 | 20 | ### Bug Fixes 21 | 22 | * **bgColor:** :bug: Fixed a background color ([d29b38a](https://github.com/AlexTorresDev/custom-electron-titlebar/commit/d29b38a6d7841d51a916fde917b0e8e6d6de8928)) 23 | * **bgColor:** :bug: Fixed a background color ([776a8bb](https://github.com/AlexTorresDev/custom-electron-titlebar/commit/776a8bbd3e8eb8e371b600d26260caf38bdf92dd)) 24 | 25 | ## [4.2.5](https://github.com/AlexTorresDev/custom-electron-titlebar/compare/v4.2.4...v4.2.5) (2023-07-24) 26 | 27 | 28 | ### Bug Fixes 29 | 30 | * **titlebar:** :adhesive_bandage: Fixed bugs detected by the test ([1a6bbab](https://github.com/AlexTorresDev/custom-electron-titlebar/commit/1a6bbab23da624bcb5040eeff49ce7b8d8a0e0cb)) 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Alex Torres 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 | # Custom Electron Titlebar 2 | 3 | This project is a typescript library for electron that allows you to configure a fully customizable title bar. 4 | 5 | [![CI](https://badgen.net/github/checks/AlexTorresDev/custom-electron-titlebar?label=CI)](https://github.com/AlexTorresDev/custom-electron-titlebar/actions/workflows/build-release.yml) 6 | [![License](https://badgen.net/github/license/AlexTorresDev/custom-electron-titlebar?label=License)](https://github.com/AlexTorresDev/custom-electron-titlebar/blob/master/LICENSE) 7 | [![NPM](https://badgen.net/npm/v/custom-electron-titlebar?label=NPM)](https://npmjs.org/package/custom-electron-titlebar) 8 | [![Install size](https://badgen.net/packagephobia/install/custom-electron-titlebar?label=Install%20size)](https://packagephobia.com/result?p=custom-electron-titlebar) 9 | 10 | > [!IMPORTANT] 11 | > This project will no longer be maintained, because I am the only one working on it and I have no free time left to review the issues and incorporate new features or update the dependencies to the latest versions. 12 | > 13 | > **Thanks to all the contributors and dependents of this library.** 14 | 15 | [📄 Documentation](https://github.com/AlexTorresDev/custom-electron-titlebar/wiki) 16 | 17 | ### Standard Title Bar 18 | 19 | ![Screenshot 1](screenshots/70shots_so.jpg) 20 | 21 | ### Bottom Menu Bar 22 | 23 | ![Screenshot 2](screenshots/544shots_so.jpg) 24 | 25 | ### Menu 26 | 27 | ![Screenshot 3](screenshots/780shots_so.jpg) 28 | 29 | ### Custom color 30 | 31 | ![Screenshot 4](screenshots/262shots_so.jpg) 32 | 33 | # 📦 Installing 34 | You can install this package with `npm`, `pnpm` or `yarn`. 35 | ```sh 36 | npm install custom-electron-titlebar 37 | ``` 38 | ```sh 39 | pnpm add custom-electron-titlebar 40 | ``` 41 | ```sh 42 | yarn add custom-electron-titlebar 43 | ``` 44 | 45 | # 🛠️ Usage 46 | The implementation is done as follows: 47 | 48 | In the main application file (main.js or .ts) 49 | ```js 50 | import { setupTitlebar, attachTitlebarToWindow } from "custom-electron-titlebar/main"; 51 | 52 | // setup the titlebar main process 53 | setupTitlebar(); 54 | 55 | function createWindow() { 56 | // Create the browser window. 57 | const mainWindow = new BrowserWindow({ 58 | width: 800, 59 | height: 600, 60 | //frame: false, // needed if process.versions.electron < 14 61 | titleBarStyle: 'hidden', 62 | /* You can use *titleBarOverlay: true* to use the original Windows controls */ 63 | titleBarOverlay: true, 64 | webPreferences: { 65 | sandbox: false, 66 | preload: path.join(__dirname, 'preload.js') 67 | } 68 | }); 69 | 70 | ... 71 | 72 | // attach fullScreen(f11 and not 'maximized') && focus listeners 73 | attachTitlebarToWindow(mainWindow); 74 | } 75 | ``` 76 | 77 | In the preload file (preload.js or .ts) 78 | ```js 79 | import { Titlebar } from "custom-electron-titlebar"; 80 | 81 | window.addEventListener('DOMContentLoaded', () => { 82 | // Title bar implementation 83 | new Titlebar(); 84 | }); 85 | ``` 86 | To see the options you can include in the Title Bar constructor, such as color of elements, icons, menu position, and much more, and the methods you can use, go to the [wiki](https://github.com/AlexTorresDev/custom-electron-titlebar/wiki) 87 | 88 | ## 💰 Support 89 | If you want to support my development, you can do so by donating through [💖 Sponsor](https://github.com/sponsors/AlexTorresDev) 90 | 91 | 92 | ## 📝 Contributors 93 | I would like to express my sincere gratitude to all the people who have collaborated in the development and advancement of this project. I appreciate your contributions. 94 | 95 | [![](https://contrib.rocks/image?repo=AlexTorresDev/custom-electron-titlebar)](https://github.com/AlexTorresDev/custom-electron-titlebar/graphs/contributors) 96 | 97 | 98 | ## ✅ License 99 | This project is under the [MIT](https://github.com/AlexTorresDev/custom-electron-titlebar/blob/master/LICENSE) license. 100 | -------------------------------------------------------------------------------- /__mocks__/base/common/color.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | Color: { 3 | fromHex: jest.fn(), 4 | MOCKED_COLOR: '#000000' 5 | } 6 | } -------------------------------------------------------------------------------- /__mocks__/base/common/platform.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | platform: 'Windows', 3 | isMacintosh: false, 4 | isWindows: true, 5 | isLinux: false 6 | } -------------------------------------------------------------------------------- /__mocks__/electron.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | ipcRenderer: { 3 | invoke: jest.fn(), 4 | on: jest.fn(), 5 | send: jest.fn(), 6 | sendSync: jest.fn() 7 | }, 8 | Menu: { 9 | buildFromTemplate: jest.fn() 10 | } 11 | } -------------------------------------------------------------------------------- /__test__/titlebar.test.js: -------------------------------------------------------------------------------- 1 | const { CustomTitlebar } = require('../dist/titlebar/index') 2 | const { BOTTOM_TITLEBAR_HEIGHT } = require('../dist/consts') 3 | 4 | jest.mock('electron') 5 | jest.mock('base/common/color') 6 | jest.mock('base/common/platform') 7 | 8 | describe('CustomTitlebar', () => { 9 | let titlebar 10 | 11 | beforeEach(() => { 12 | jest.clearAllMocks() 13 | titlebar = new CustomTitlebar() 14 | }) 15 | 16 | afterEach(() => { 17 | titlebar?.dispose() 18 | }) 19 | 20 | test('should create an instance of CustomTitlebar', () => { 21 | expect(titlebar).toBeInstanceOf(CustomTitlebar) 22 | }) 23 | 24 | test('should update the title', () => { 25 | titlebar.updateTitle('Test Title') 26 | expect(titlebar.titleElement.innerText).toBe('Test Title') 27 | expect(document.title).toBe('Test Title') 28 | }) 29 | 30 | test('should update the menu position', () => { 31 | titlebar.updateMenuPosition('bottom') 32 | expect(titlebar.titlebarElement.style.height).toBe(`${BOTTOM_TITLEBAR_HEIGHT}px`) 33 | expect(titlebar.containerElement.style.top).toBe(`${BOTTOM_TITLEBAR_HEIGHT}px`) 34 | expect(titlebar.menuBarContainer.classList.contains('bottom')).toBe(true) 35 | }) 36 | 37 | /* test('should update the background color', () => { 38 | const { Color } = require('base/common/color') 39 | titlebar.updateBackground(Color.fromHex('#ffffff')) 40 | console.log(titlebar.titlebarElement.style) 41 | expect(titlebar.titlebarElement.style.backgroundColor).toBe('#ffffff') 42 | }) 43 | 44 | test('should background color from string', async () => { 45 | await titlebar.updateBackground('#ffffff') 46 | console.log(titlebar.titlebarElement.style) 47 | expect(titlebar.titlebarElement.style.backgroundColor).toBe('#ffffff') 48 | }) */ 49 | 50 | test('should refresh the menu', async () => { 51 | const { ipcRenderer } = require('electron') 52 | const mockedMenu = { items: [{ label: 'Test &Menu' }] } 53 | 54 | ipcRenderer.invoke.mockResolvedValue(mockedMenu) 55 | 56 | await titlebar.refreshMenu() 57 | 58 | expect(ipcRenderer.invoke).toHaveBeenCalledWith('request-application-menu') 59 | expect(titlebar.menuBarContainer.children.length).toBe(2) // Because the 'More' button is added 60 | }) 61 | }) 62 | -------------------------------------------------------------------------------- /docs/CSS-Customization.md: -------------------------------------------------------------------------------- 1 | The title bar can be customized using CSS. For this purpose, there is a prefix that allows access to the title bar elements. 2 | 3 | All classes possessed by the initial title bar are prefixed with `cet`. 4 | 5 | ## Classes 6 | 7 | ### Titlebar Classes 8 | 9 | | Class | Description | 10 | | --- | --- | 11 | | `cet-titlebar` | Main class of the title bar | 12 | | `cet-windows` | Applied class if running on Windows | 13 | | `cet-mac` | Applied class if running on Mac | 14 | | `cet-linux` | Applied class if running on Linux | 15 | | `cet-icon` | Applied to the title bar icon | 16 | | `cet-title` | Applied to the title of the title bar | -------------------------------------------------------------------------------- /docs/Colors.md: -------------------------------------------------------------------------------- 1 | There is a class called `TitlebarColor` that allows you to configure the colors of the title bar. 2 | 3 | ## Predefined Colors 4 | 5 | The `TitlebarColor` class has several predefined colors that can be used to configure the colors of the title bar. 6 | 7 | - TitlebarColor.BLACK 8 | - TitlebarColor.BLUE 9 | - TitlebarColor.CYAN 10 | - TitlebarColor.GREEN 11 | - TitlebarColor.LIGHTGREY 12 | - TitlebarColor.RED 13 | - TitlebarColor.WHITE 14 | - TitlebarColor.TRANSPARENT 15 | 16 | ## Creating a Custom Color 17 | 18 | To create a custom color, use the static method `TitlebarColor.fromHex(color)`. This method takes a hexadecimal color as an argument and returns a `TitlebarColor` object. 19 | 20 | ```js 21 | const customColor = TitlebarColor.fromHex('#FF0000') 22 | ``` -------------------------------------------------------------------------------- /docs/Get-Started.md: -------------------------------------------------------------------------------- 1 | ## Installation 2 | 3 | How to install this library in your Electron project? 4 | 5 | ```sh 6 | npm i custom-electron-titlebar 7 | ``` 8 | 9 | ## How to use? 10 | 11 | In the main file of the project `main.js` or `index.js` import the library and call the `setupTitlebar` and `attachTitlebarToWindow` functions: 12 | 13 | ```js 14 | const { setupTitlebar, attachTitlebarToWindow } = require('custom-electron-titlebar/main'); 15 | 16 | // setup the titlebar main process 17 | setupTitlebar(); 18 | 19 | function createWindow() { 20 | // Create the browser window. 21 | const mainWindow = new BrowserWindow({ 22 | width: 800, 23 | height: 600, 24 | //frame: false, // needed if process.versions.electron < 14 25 | titleBarStyle: 'hidden', 26 | /* You can use *titleBarOverlay: true* to use the original Windows controls */ 27 | titleBarOverlay: true, 28 | webPreferences: { 29 | sandbox: false, 30 | preload: path.join(__dirname, 'preload.js') 31 | } 32 | }); 33 | 34 | ... 35 | 36 | // attach fullscreen(f11 and not 'maximized') && focus listeners 37 | attachTitlebarToWindow(mainWindow); 38 | } 39 | ``` 40 | 41 | It is important that the `titleBarStyle` property is `hidden` so that the default Electron title bar is not displayed. 42 | Likewise, the sandbox property must be added to false so that the library can function correctly. -------------------------------------------------------------------------------- /docs/Home.md: -------------------------------------------------------------------------------- 1 | ## Welcome to Custom Electron Titlebar wiki! 2 | 3 | ### What is Custom Electron Titlebar? 4 | 5 | Custom Electron Titlebar is a library that allows you to create a fully customized title bar for your Electron application. 6 | 7 | ### Why use Custom Electron Titlebar? 8 | 9 | Using the Custom Electron Titlebar in your Electron project offers a powerful solution for achieving a distinct and cohesive user interface. By seamlessly integrating a tailor-made title bar, you can enhance the overall aesthetics and branding of your application. This library allows developers to exercise creative control over every aspect of the title bar's appearance and behavior, ensuring a seamless user experience that aligns perfectly with your application's unique design and functionality. 10 | 11 | ### Features 12 | 13 | - Fast integration 14 | - Customizable 15 | - Updated to the latest version of Electron 16 | - Support for Windows, Linux and MacOS 17 | - Customizable application menu -------------------------------------------------------------------------------- /docs/Menu-Icons.md: -------------------------------------------------------------------------------- 1 | ## File structure 2 | 3 | To customize the menu icons, you need to create a `JSON` file and add the following structure within it: 4 | 5 | ```json 6 | { 7 | "submenuIndicator": "", 8 | "checkbox": "", 9 | "radioChecked": "", 10 | "radioUnchecked": "", 11 | "linux": { 12 | "minimize": "", 13 | "maximize": "", 14 | "restore": "", 15 | "close": "" 16 | }, 17 | "windows": { 18 | "minimize": "", 19 | "maximize": "", 20 | "restore": "", 21 | "close": "" 22 | } 23 | } 24 | ``` 25 | 26 | > These SVGs should have the `fill="currentColor"` attribute so that the color can be adapted correctly to the title bar colors. 27 | 28 | ## Menu items 29 | 30 | - `submenuIndicator` This is the icon for submenus. 31 | 32 | - `checkbox` This is the icon for checkboxes. 33 | 34 | - `radioChecked` This is the icon for radio items when they are selected. 35 | 36 | - `radioUnchecked` This is the icon for radio items when they are not selected. 37 | 38 | ## Titlebar items 39 | 40 | - `minimize` This is the icon for minimizing the window. 41 | 42 | - `maximize` This is the icon for maximizing the window. 43 | 44 | - `restore` This is the icon for restoring the window. 45 | 46 | - `close` This is the icon for closing the window. 47 | 48 | > **Note:**

49 | Title bar icons are not compatible with macOS.
50 | Title bar icons are not displayed when `titlebarOverlay` is set to true. -------------------------------------------------------------------------------- /docs/Menubar-Options.md: -------------------------------------------------------------------------------- 1 | > If what you want is to know the options for the title bar, see [Titlebar Options](./Titlebar-Options). 2 | 3 | Just like with title bar options, menu options are passed as an object to the `Titlebar` or `CustomTitlebar` component: 4 | 5 | ```js 6 | const options = { 7 | // title bar options 8 | // rest of the menu options 9 | }; 10 | 11 | new Titlebar(options); 12 | ``` 13 | 14 | ## Menu Color 15 | 16 | This is the background color of the menu. It can be a hexadecimal color using `TitlebarColor.fromHex(color)` or a `TitlebarColor`. 17 | 18 | For more color details, see [Colors](./Colors). 19 | 20 | ```js 21 | const options = { 22 | // title bar options 23 | backgroundColor: TitlebarColor.fromHex('#FF0000') 24 | }; 25 | ``` 26 | 27 | ## Enable Mnemonics 28 | 29 | Mnemonics are a way to navigate the user interface using the keyboard. To enable them, you should pass the `enableMnemonics` option as `true`: 30 | 31 | ```js 32 | const options = { 33 | // title bar options 34 | enableMnemonics: true 35 | }; 36 | ``` 37 | 38 | ## Menu Icons 39 | 40 | These are the icons displayed on special menu items, such as **radio**, **checkbox**, and **submenu** items. These are defined in a `JSON` file, and the file path is passed in the options. 41 | 42 | ```js 43 | const options = { 44 | // title bar options 45 | icons: path.join(__dirname, 'menu-icons.json') 46 | }; 47 | ``` 48 | 49 | For more icon details, see [Menu Icons](./Menu-Icons). 50 | 51 | ## Menu 52 | 53 | This is the menu displayed in the menu bar. This option is deprecated, and it's recommended to use `setupTitlebar` in the main application file. 54 | 55 | ```js 56 | const options = { 57 | // title bar options 58 | menu: Menu.buildFromTemplate(template) 59 | }; 60 | ``` 61 | 62 | ## Menu Position 63 | 64 | This is the position of the menu in the title bar. It can be `left` or `bottom`. 65 | 66 | ```js 67 | const options = { 68 | // title bar options 69 | menuPosition: 'left' 70 | }; 71 | ``` 72 | 73 | ## Only Show Menu in Title Bar 74 | 75 | This option allows showing the menu only in the title bar. This removes all elements from the bar except for the buttons. 76 | 77 | ```js 78 | const options = { 79 | // title bar options 80 | onlyShowMenubar: true 81 | }; 82 | ``` 83 | 84 | ## Remove Menu from Title Bar 85 | 86 | This option allows removing the `menubar` from `titlebar`. The default value is `false` 87 | 88 | ```js 89 | const options = { 90 | // title bar options 91 | removeMenuBar: true 92 | }; 93 | ``` 94 | 95 | ## Menu Item Color 96 | 97 | This is the background color of the menu items when the cursor is hovering over each one. It can be a hexadecimal color using `TitlebarColor.fromHex(color)` or a `TitlebarColor`. 98 | 99 | For more color details, see [Colors](./Colors). 100 | 101 | ```js 102 | const options = { 103 | // title bar options 104 | itemBackgroundColor: TitlebarColor.fromHex('#FF0000') 105 | }; 106 | ``` 107 | 108 | ## Menu Item Separator Color 109 | 110 | This is the background color of the menu item separators. It can be a hexadecimal color using `TitlebarColor.fromHex(color)` or a `TitlebarColor`. 111 | 112 | For more color details, see [Colors](./Colors). 113 | 114 | ```js 115 | const options = { 116 | // title bar options 117 | menuSeparatorColor: TitlebarColor.fromHex('#FF0000') 118 | }; 119 | ``` 120 | 121 | ## Menu Icon Color 122 | 123 | This is the color of the menu icons. It can be a hexadecimal color using or a `TitlebarColor`. 124 | 125 | For more color details, see [Colors](./Colors). 126 | 127 | ```js 128 | const options = { 129 | // title bar options 130 | svgColor: TitlebarColor.fromHex('#FF0000') 131 | }; 132 | ``` 133 | 134 | ## Menu Transparency 135 | 136 | This is the transparency of the menu background. It can be a decimal value between `0` and `1`. 137 | 138 | ```js 139 | const options = { 140 | // title bar options 141 | transparent: 0.5 142 | }; 143 | ``` -------------------------------------------------------------------------------- /docs/Titlebar-Methods.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexTorresDev/custom-electron-titlebar/e0681beaf928ffc475a7c8f856d1fcddb5fb56a3/docs/Titlebar-Methods.md -------------------------------------------------------------------------------- /docs/Titlebar-Options.md: -------------------------------------------------------------------------------- 1 | > If you want to learn about the menu bar options, see [Menubar Options](./menubar-options). 2 | 3 | The titlebar has various options that allow for customization. These options are passed as an object to the `Titlebar` or `CustomTitlebar` component: 4 | 5 | ```js 6 | const options = { 7 | // options 8 | }; 9 | 10 | new Titlebar(options); 11 | ``` 12 | 13 | ## Background color of the titlebar 14 | This is the background color of the titlebar. It can be a hexadecimal color using `TitlebarColor.fromHex(color)` or a `TitlebarColor`. 15 | 16 | For more details on colors, see [Colors](./Colors). 17 | 18 | ```js 19 | const options = { 20 | backgroundColor: TitlebarColor.fromHex('#FF0000') 21 | }; 22 | ``` 23 | 24 | ## Container overflow 25 | 26 | The overflow of the container is the way the content is displayed when the container size is smaller than the content size. It can be `auto`, `hidden` or `visible`. 27 | 28 | ```js 29 | const options = { 30 | overflow: 'auto' 31 | }; 32 | ``` 33 | 34 | ## Application icon 35 | 36 | This is the icon that is displayed in the titlebar. It can be a `NativeImage` icon or a path to an image file. 37 | 38 | ```js 39 | const options = { 40 | icon: path.join(__dirname, 'icon.png') 41 | }; 42 | ``` 43 | 44 | or using `nativeImage` 45 | ```js 46 | const options = { 47 | icon: nativeImage.createFromPath(path.join(__dirname, 'icon.png')) 48 | }; 49 | ``` 50 | 51 | For more details on `NativeImage`, see [Electron NativeImage](https://www.electronjs.org/docs/latest/api/native-image). 52 | 53 | ## Application icon size 54 | 55 | This is the size of the icon that is displayed in the titlebar. This must be a number and must be between 16 and 24. (size in pixels) 56 | 57 | ```js 58 | const options = { 59 | iconSize: 20 60 | }; 61 | ``` 62 | 63 | ## Title location 64 | 65 | This is the location of the title. It can be `left`, `center` or `right`. 66 | 67 | ```js 68 | const options = { 69 | titleHorizontalAlignment: 'left' 70 | }; 71 | ``` 72 | 73 | ## Buttons order 74 | 75 | It can be `inverted` or `first-buttons`. 76 | 77 | `inverted` completely reverses the bar, meaning buttons on the left are shown on the right and vice versa. 78 | 79 | `first-buttons` shows the titlebar normally, but buttons on the right are shown on the left. 80 | 81 | ```js 82 | const options = { 83 | order: 'inverted' 84 | }; 85 | ``` 86 | 87 | ## Titlebar buttons 88 | 89 | ### Minimize 90 | 91 | Indicates whether the minimize button is enabled or not. 92 | 93 | ```js 94 | const options = { 95 | minimizable: true 96 | } 97 | ``` 98 | 99 | ### Maximize 100 | 101 | Indicates whether the maximize button is enabled or not. 102 | 103 | ```js 104 | const options = { 105 | maximizable: true 106 | } 107 | ``` 108 | 109 | ### Close 110 | 111 | Indicates whether the close button is enabled or not. 112 | 113 | ```js 114 | const options = { 115 | closeable: true 116 | } 117 | ``` 118 | 119 | ## Button tooltips 120 | 121 | Allows for customization of the button titles that are displayed when hovering over them. 122 | 123 | ```js 124 | const options = { 125 | tooltips: { 126 | minimize: 'Minimize', 127 | maximize: 'Maximize', 128 | restoreDown: 'Restore', 129 | close: 'Close' 130 | } 131 | } 132 | ``` -------------------------------------------------------------------------------- /docs/_Footer.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexTorresDev/custom-electron-titlebar/e0681beaf928ffc475a7c8f856d1fcddb5fb56a3/docs/_Footer.md -------------------------------------------------------------------------------- /docs/_Sidebar.md: -------------------------------------------------------------------------------- 1 | - [Get Started](./Get-Started) 2 | - [Titlebar Options](./Titlebar-Options) 3 | - [Menubar Options](./Menubar-Options) 4 | - [Colors](./Colors) 5 | - [Menu Icons](./Menu-Icons) 6 | - [Titlebar Methods](./Titlebar-Methods) 7 | - [CSS Customization](./CSS-Customization) 8 | -------------------------------------------------------------------------------- /example/assets/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexTorresDev/custom-electron-titlebar/e0681beaf928ffc475a7c8f856d1fcddb5fb56a3/example/assets/home.png -------------------------------------------------------------------------------- /example/assets/icons.json: -------------------------------------------------------------------------------- 1 | { 2 | "check": "", 3 | "arrow": "", 4 | "windows": { 5 | "minimize": "", 6 | "maximize": "", 7 | "restore": "", 8 | "close": "" 9 | } 10 | } -------------------------------------------------------------------------------- /example/assets/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/assets/run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexTorresDev/custom-electron-titlebar/e0681beaf928ffc475a7c8f856d1fcddb5fb56a3/example/assets/run.png -------------------------------------------------------------------------------- /example/assets/terminal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/AlexTorresDev/custom-electron-titlebar/e0681beaf928ffc475a7c8f856d1fcddb5fb56a3/example/assets/terminal.png -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Hello World! 9 | 14 | 15 | 16 | 17 |

Hi 👋🏻!

18 | We are using Node.js , 19 | Chromium , 20 | and Electron . 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /example/main.js: -------------------------------------------------------------------------------- 1 | // Modules to control application life and create native browser window 2 | const { app, BrowserWindow, Menu, nativeImage, ipcMain, nativeTheme } = require('electron') 3 | const { setupTitlebar, attachTitlebarToWindow } = require('custom-electron-titlebar/main') 4 | const path = require('path') 5 | 6 | // Setup the titlebar 7 | setupTitlebar() 8 | 9 | function createWindow() { 10 | // Create the browser window. 11 | const mainWindow = new BrowserWindow({ 12 | width: 1000, 13 | height: 600, 14 | frame: false, // Use to linux 15 | titleBarStyle: 'hidden', 16 | titleBarOverlay: true, 17 | webPreferences: { 18 | sandbox: false, 19 | preload: path.join(__dirname, 'preload.js') 20 | } 21 | }) 22 | 23 | /* const menu = Menu.buildFromTemplate(exampleMenuTemplate) 24 | Menu.setApplicationMenu(menu) */ 25 | 26 | // and load the index.html of the app. 27 | // mainWindow.loadFile('index.html') 28 | mainWindow.loadURL('https://github.com') 29 | 30 | // Open the DevTools. 31 | // mainWindow.webContents.openDevTools() 32 | 33 | // Attach listeners 34 | attachTitlebarToWindow(mainWindow) 35 | } 36 | 37 | ipcMain.handle('dark-mode:toggle', () => { 38 | if (nativeTheme.shouldUseDarkColors) { 39 | nativeTheme.themeSource = 'light' 40 | } else { 41 | nativeTheme.themeSource = 'dark' 42 | } 43 | return nativeTheme.shouldUseDarkColors 44 | }) 45 | 46 | ipcMain.handle('dark-mode:system', () => { 47 | nativeTheme.themeSource = 'system' 48 | }) 49 | 50 | // This method will be called when Electron has finished 51 | // initialization and is ready to create browser windows. 52 | // Some APIs can only be used after this event occurs. 53 | app.whenReady().then(() => { 54 | createWindow() 55 | 56 | app.on('activate', function () { 57 | // On macOS it's common to re-create a window in the app when the 58 | // dock icon is clicked and there are no other windows open. 59 | if (BrowserWindow.getAllWindows().length === 0) createWindow() 60 | }) 61 | }) 62 | 63 | // Quit when all windows are closed, except on macOS. There, it's common 64 | // for applications and their menu bar to stay active until the user quits 65 | // explicitly with Cmd + Q. 66 | app.on('window-all-closed', function () { 67 | if (process.platform !== 'darwin') app.quit() 68 | }) 69 | 70 | // In this file you can include the rest of your app's specific main process 71 | // code. You can also put them in separate files and require them here. 72 | 73 | 74 | // Custom menu 75 | const exampleMenuTemplate = [ 76 | { 77 | label: 'Simple O&ptions', 78 | submenu: [ 79 | { 80 | label: 'Quit', 81 | click: () => app.quit() 82 | }, 83 | { 84 | label: 'Radio1', 85 | type: 'radio', 86 | checked: true 87 | }, 88 | { 89 | label: 'Radio2', 90 | type: 'radio' 91 | }, 92 | { 93 | label: 'Check&box1', 94 | type: 'checkbox', 95 | checked: true, 96 | click: (item) => { 97 | console.log('item is checked? ' + item.checked) 98 | } 99 | }, 100 | { type: 'separator' }, 101 | { 102 | label: 'Che&ckbox2', 103 | type: 'checkbox', 104 | checked: false, 105 | click: (item) => { 106 | console.log('item is checked? ' + item.checked) 107 | } 108 | } 109 | ] 110 | }, 111 | { 112 | label: 'With &Icons', 113 | submenu: [ 114 | { 115 | icon: nativeImage.createFromPath(path.resolve('example/assets', 'home.png')), 116 | label: 'Go to &Home using Native Image' 117 | }, 118 | { 119 | icon: path.resolve('example/assets', 'run.png'), 120 | label: 'Run using string', 121 | submenu: [ 122 | { 123 | label: 'Submenu of run' 124 | }, 125 | { 126 | label: 'Print', 127 | accelerator: 'CmdOrCtrl+P' 128 | }, 129 | { 130 | type: 'separator' 131 | }, 132 | { 133 | label: 'Item 2 of submenu of run' 134 | } 135 | ] 136 | } 137 | ] 138 | }, 139 | { 140 | label: 'A&dvanced Options', 141 | submenu: [ 142 | { 143 | label: 'Quit', 144 | click: () => app.quit() 145 | }, 146 | { 147 | label: 'Radio1', 148 | type: 'radio', 149 | checked: true 150 | }, 151 | { 152 | label: 'Radio2', 153 | type: 'radio' 154 | }, 155 | { 156 | label: 'Checkbox1', 157 | type: 'checkbox', 158 | checked: true, 159 | click: (item) => { 160 | console.log('item is checked? ' + item.checked) 161 | } 162 | }, 163 | { type: 'separator' }, 164 | { 165 | label: 'Checkbox2', 166 | type: 'checkbox', 167 | checked: false, 168 | click: (item) => { 169 | console.log('item is checked? ' + item.checked) 170 | } 171 | }, 172 | { 173 | label: 'Radio Test', 174 | submenu: [ 175 | { 176 | label: 'S&le Checkbox', 177 | type: 'checkbox', 178 | checked: true 179 | }, 180 | { 181 | label: 'Radio1', 182 | checked: true, 183 | type: 'radio' 184 | }, 185 | { 186 | label: 'Radio2', 187 | type: 'radio' 188 | }, 189 | { 190 | label: 'Radio3', 191 | type: 'radio' 192 | }, 193 | { type: 'separator' }, 194 | { 195 | label: 'Radio1', 196 | checked: true, 197 | type: 'radio' 198 | }, 199 | { 200 | label: 'Radio2', 201 | type: 'radio' 202 | }, 203 | { 204 | label: 'Radio3', 205 | type: 'radio' 206 | } 207 | ] 208 | }, 209 | { 210 | label: 'zoomIn', 211 | role: 'zoomIn' 212 | }, 213 | { 214 | label: 'zoomOut', 215 | role: 'zoomOut' 216 | }, 217 | { 218 | label: 'Radio1', 219 | type: 'radio' 220 | }, 221 | { 222 | label: 'Radio2', 223 | checked: true, 224 | type: 'radio' 225 | } 226 | ] 227 | }, 228 | { 229 | label: '&View', 230 | submenu: [ 231 | { role: 'reload' }, 232 | { role: 'forceReload' }, 233 | { type: 'separator' }, 234 | { role: 'zoomIn' }, 235 | { role: 'zoomOut' }, 236 | { role: 'resetZoom' }, 237 | { role: 'toggleDevTools', icon: path.resolve('example/assets', 'terminal.png') } 238 | ] 239 | } 240 | ] 241 | -------------------------------------------------------------------------------- /example/preload.js: -------------------------------------------------------------------------------- 1 | /** 2 | * The preload script runs before. It has access to web APIs 3 | * as well as Electron's renderer process modules and some 4 | * polyfilled Node.js functions. 5 | * 6 | * https://www.electronjs.org/docs/latest/tutorial/sandbox 7 | */ 8 | const { CustomTitlebar, TitlebarColor } = require('custom-electron-titlebar') 9 | const path = require('path') 10 | 11 | window.addEventListener('DOMContentLoaded', () => { 12 | const replaceText = (selector, text) => { 13 | const element = document.getElementById(selector) 14 | if (element) element.innerText = text 15 | } 16 | 17 | for (const type of ['chrome', 'node', 'electron']) { 18 | replaceText(`${type}-version`, process.versions[type]) 19 | } 20 | 21 | // eslint-disable-next-line no-new 22 | new CustomTitlebar({ 23 | backgroundColor: TitlebarColor.fromHex('#6538b9'), 24 | menuTransparency: 0.2 25 | // icon: path.resolve('example/assets', 'logo.svg'), 26 | // icons: path.resolve('example/assets', 'icons.json'), 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /example/renderer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is loaded via the