├── .eslintrc.json ├── .gitattributes ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json └── settings.json ├── .vscodeignore ├── CHANGELOG.md ├── README.md ├── extension.js ├── jsconfig.json ├── package.json ├── resources ├── before-after.png ├── before-after.psd ├── dont-show-again.png ├── logo.png └── preview.png └── workbench.main.css /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": false, 4 | "commonjs": true, 5 | "es6": true, 6 | "node": true 7 | }, 8 | "parserOptions": { 9 | "sourceType": "module" 10 | }, 11 | "rules": { 12 | "no-const-assign": "warn", 13 | "no-this-before-super": "warn", 14 | "no-undef": "warn", 15 | "no-unreachable": "warn", 16 | "no-unused-vars": "warn", 17 | "constructor-super": "warn", 18 | "valid-typeof": "warn" 19 | } 20 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Set default behavior to automatically normalize line endings. 2 | * text=auto 3 | 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode-test/ 3 | *.vsix 4 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "lehni.vscode-fix-checksums", 6 | "be5invis.vscode-custom-css" 7 | ] 8 | } -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that launches the extension inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "runtimeExecutable": "${execPath}", 13 | "args": [ 14 | "--extensionDevelopmentPath=${workspaceFolder}" 15 | ] 16 | } 17 | ] 18 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "typescript.surveys.enabled": false 4 | } -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .gitignore 3 | jsconfig.json 4 | .eslintrc.json 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## Version 1.10.5 4 | 5 | - Update instructions for [Custom UI 6 | Style](https://marketplace.visualstudio.com/items?itemName=subframe7536.custom-ui-style) 7 | extension 8 | 9 | 10 | ## Version 1.10.4 11 | 12 | - Update instructions for [Apc Customize 13 | UI++](https://marketplace.visualstudio.com/items?itemName=drcika.apc-extension) 14 | extension 15 | 16 | ## Version 1.10.3 17 | 18 | - Add instructions for replacement with [Apc Customize 19 | UI++](https://marketplace.visualstudio.com/items?itemName=drcika.apc-extension) 20 | extension 21 | 22 | ## Version 1.10.2 23 | 24 | - Remove stylesheet tweak again, now that Customize UI integrates it 25 | 26 | ## Version 1.10.1 27 | 28 | - Improve instructions for replacement with [Customize 29 | UI](https://marketplace.visualstudio.com/items?itemName=iocave.customize-ui) 30 | extension 31 | 32 | ## Version 1.10.0 33 | 34 | - Deprecate extension in favor of [Customize 35 | UI](https://marketplace.visualstudio.com/items?itemName=iocave.customize-ui) 36 | 37 | ## Version 1.9.1 38 | 39 | - Make new patch adjustments actually for VSCode v1.33.0, not just v1.34.0 🤦‍ 40 | 41 | ## Version 1.9.0 42 | 43 | - Adjust patches for VSCode v1.33.0 44 | 45 | ## Version 1.8.1 46 | 47 | - Adjust patch for latest VSCode Insiders v1.32.0 48 | 49 | ## Version 1.8.0 50 | 51 | - Fix bug that causes an empty application window in VSCode v1.31.0 52 | 53 | ## Version 1.7.1 54 | 55 | - Adjust patch for latest VSCode Insiders v1.30.0 56 | 57 | ## Version 1.7.0 58 | 59 | - Fix random drag failures on tabs 60 | - Adjust patch for latest VSCode Insiders v1.30.0 61 | 62 | ## Version 1.6.4 63 | 64 | - Adjust patch for latest VSCode Insiders v1.29.0 65 | 66 | ## Version 1.6.3 67 | 68 | - Improve breadcrumb styling to not affect tabs scrolling 69 | 70 | ## Version 1.6.2 71 | 72 | - Adjust patch for latest VSCode Insiders v1.29.0 73 | 74 | ## Version 1.6.1 75 | 76 | - Improve positioning of breadcrumbs under tabs (#18) 77 | 78 | ## Version 1.6.0 79 | 80 | - Adjust patch for latest VSCode Insiders v1.28.0 81 | 82 | ## Version 1.5.2 83 | 84 | - Disable file dragging on draggable tabs containers 85 | 86 | ## Version 1.5.1 87 | 88 | - Don't activate extension when VSCode is running as an Extension Development 89 | Host 90 | 91 | ## Version 1.5.0 92 | 93 | - Improve handling of draggable tab bars 94 | 95 | ## Version 1.4.1 96 | 97 | - Fix fullscreen view when side-bar is displayed right 98 | 99 | ## Version 1.4.0 100 | 101 | - Correctly handle layout when no side-bar, activity-bar, or tab-bar is 102 | displayed 103 | 104 | ## Version 1.3.3 105 | 106 | - Improve README 107 | - Add recommendations 108 | 109 | ## Version 1.3.2 110 | 111 | - Include link to lehni.vscode-fix-checksums extension 112 | 113 | ## Version 1.3.1 114 | 115 | - Do not move first tab in additional split views 116 | 117 | ## Version 1.3.0 118 | 119 | - Add support for side-bar on the right 120 | 121 | ## Version 1.2.0 122 | 123 | - Support Electron 2.0 in VSCode 1.26.0-insider and newer 124 | 125 | ## Version 1.1.0 126 | 127 | - Add proper support for zoom levels 128 | 129 | ## Version 1.0.11 130 | 131 | - Add support for latest VSCode Insiders version by removing reliance on 132 | `.titlebar-style-custom` CSS class. 133 | 134 | ## Version 1.0.10 135 | 136 | - Avoid invalid restores over newer versions of VSCode by using VSCode version 137 | for versioned backups. 138 | 139 | ## Version 1.0.9 140 | 141 | - Improve robustness of enable / disable commands. 142 | 143 | ## Version 1.0.8 144 | 145 | - Add support for fullscreen mode. 146 | - Only apply styling overrides if `"window.titleBarStyle"` setting is set to 147 | `"custom"`. 148 | - Read activity-bar width from CSS variable, so it can optionally be changed / overridden 149 | with the [Custom CSS and JS Loader](https://marketplace.visualstudio.com/items?itemName=be5invis.vscode-custom-css) 150 | extension (e.g. when using the `"window.zoomLevel"` setting). 151 | 152 | ## Version 1.0.7 153 | 154 | - Move badges in activity-bar close to original location in relation to icon. 155 | - Fix dragging on area around traffic lights on activity-bar. 156 | 157 | ## Version 1.0.6 158 | 159 | - Fix dragging on area around traffic lights when activity-bar is hidden. 160 | 161 | ## Version 1.0.5 162 | 163 | - Support dragging tab-bar in recent versions of VSCode. 164 | 165 | ## Version 1.0.4 166 | 167 | - Add disclaimer and notes about updating extension and VSCode to README. 168 | 169 | ## Version 1.0.3 170 | 171 | - Improve restoration of patched files when disabling titlebar-less mode. 172 | - Correct title-bar styling for any combination of invisible activity-bar / 173 | side-bar (#1). 174 | - Add paragraph about required user settings to README (#2). 175 | 176 | ## Version 1.0.2 177 | 178 | - Fix display name. 179 | 180 | ## Version 1.0.1 181 | 182 | - Documentation tweaks. 183 | 184 | ## Version 1.0.0 185 | 186 | - Initial release. 187 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Titlebar-less VSCode for macOS 3 | 4 |

5 | Preview 6 |

7 | 8 | Originally extension, and now a recommended combination of third party 9 | extensions and settings, designed to hide the titlebar on VSCode for macOS, and 10 | inline the traffic lights (= window controls). 11 | 12 | ## Deprecation Notice 13 | 14 | This extension does not work on VSCode 1.40 or newer, and has therefore been 15 | deprecated. 16 | 17 | ## Recommended Replacement 18 | 19 | ### Custom UI Style 20 | 21 | As of VSCode 1.94, you may use [Custom UI Style](https://marketplace.visualstudio.com/items?itemName=subframe7536.custom-ui-style) 22 | instead, which you can configure to achieve the same UI style by adding these 23 | settings in your `settings.json` file, see [Issue #16](https://github.com/subframe7536/vscode-custom-ui-style/issues/16): 24 | 25 | ```jsonc 26 | { 27 | "window.titleBarStyle": "native", 28 | "window.customTitleBarVisibility": "never", 29 | "custom-ui-style.electron": { 30 | "titleBarStyle": "hiddenInset" 31 | }, 32 | "custom-ui-style.stylesheet": { 33 | ".monaco-workbench": { 34 | "--activitybar-width": "77px", 35 | "--titlebar-height": "37px", 36 | 37 | // Titlebar 38 | ".part.sidebar.left .composite.title": { 39 | "&, .title-actions, .global-actions, .monaco-toolbar": { 40 | "height": "var(--titlebar-height)" 41 | }, 42 | 43 | ".title-label": { 44 | "line-height": "var(--titlebar-height)" 45 | } 46 | }, 47 | 48 | // Tabs 49 | ".title.tabs": { 50 | "--editor-group-tab-height": "var(--titlebar-height) !important", 51 | }, 52 | 53 | // Wide activity bar 54 | ".split-view-view:has(> .part.activitybar.left)": { 55 | "&, .part.activitybar.left, .content": { 56 | "min-width": "var(--activitybar-width)", 57 | }, 58 | 59 | // Make the split-view after the wide activity bar smaller. 60 | // Also handle the situation where the sidebar is hidden, 61 | // in which case the editor itself needs to shrink / auto-size. 62 | "& + .split-view-view.visible, & + .split-view-view:not(.visible) + .split-view-view.visible": { 63 | "--offset": "calc(var(--activitybar-width) - 48px)", 64 | "margin-left": "var(--offset)", 65 | 66 | "> *:first-child": { 67 | "width": "auto", 68 | "margin-right": "var(--offset)", 69 | 70 | // Auto-size the editor when the sidebar is hidden. 71 | ".content": { 72 | "&, .split-view-view": { 73 | "&, .monaco-editor": { 74 | "&, .overflow-guard, .overlayWidgets" : { 75 | "&, .monaco-scrollable-element, .sticky-widget": { 76 | "width": "unset !important", 77 | "left": "0", 78 | "right": "0" 79 | }, 80 | 81 | ".sticky-widget": { 82 | "right": "14px !important" 83 | } 84 | } 85 | } 86 | } 87 | } 88 | }, 89 | } 90 | }, 91 | 92 | // Search-Widget 93 | ".search-view .search-widget": { 94 | ".replace-container": { 95 | "width": "calc(100% - 18px)", 96 | 97 | ".replace-input": { 98 | "&, .monaco-findInput": { 99 | "width": "100% !important" 100 | } 101 | } 102 | } 103 | }, 104 | 105 | ".monaco-sash.vertical.minimum": { 106 | // TODO: Improve collapsing of the sidebar. 107 | "left": "calc(var(--activitybar-width) - var(--vscode-sash-size) / 2) !important" 108 | }, 109 | 110 | ".part.activitybar.left": { 111 | ".monaco-action-bar": { 112 | ".action-label": { 113 | "width": "var(--activitybar-width)" 114 | } 115 | } 116 | }, 117 | 118 | "&:not(.fullscreen)": { 119 | // Make room for traffic lights. 120 | ".part.activitybar.left > .content": { 121 | "padding-top": "var(--titlebar-height)" 122 | }, 123 | 124 | // Move editor title when side-bar is hidden, when side-bar is on the 125 | // right or when activity-bar is hidden and side-bar is not on the right 126 | "&:has(.sidebar.right)": { 127 | ".monaco-split-view2.horizontal .split-view-view:not(.visible) + .split-view-view.visible .editor .title .tabs-and-actions-container": { 128 | "padding-left": "var(--activitybar-width)" 129 | }, 130 | 131 | ".auxiliarybar.left .composite.title": { 132 | "padding-left": "var(--activitybar-width)" 133 | } 134 | }, 135 | 136 | // Allow dragging on the activity-bar, status-bar, tabs-container, side-bar title... 137 | ".activitybar, .statusbar, .tabs-container, .sidebar .composite.title": { 138 | "-webkit-app-region": "drag", 139 | 140 | // ...but still allow to click actions and items without dragging. 141 | ".content .monaco-action-bar, .statusbar-item, .tab, .title .title-actions .action-label": { 142 | "-webkit-app-region": "no-drag" 143 | } 144 | } 145 | }, 146 | 147 | ".statusbar": { 148 | // Don't indent the statusbar items. 149 | "> .items-container > .statusbar-item.left.first-visible-item": { 150 | "padding-left": "0" 151 | }, 152 | 153 | // Show the host button again, and make it as wide as the .activitybar. 154 | "#status\\.host": { 155 | "&": { 156 | "display": "block !important", 157 | "width": "calc(var(--activitybar-width) - 1px)" 158 | }, 159 | ".codicon": { 160 | "margin": "0 auto" 161 | } 162 | } 163 | } 164 | }, 165 | 166 | // Make line numbers a bit smaller. 167 | ".editor .margin-view-overlays .line-numbers": { 168 | "font-size": "85%" 169 | }, 170 | 171 | // Show search inputs and results in monospaced font. 172 | ".search-widget textarea, .find-widget textarea, .search-view .results .match": { 173 | "font-family": "var(--cus-monospace-font) !important" 174 | } 175 | } 176 | } 177 | ``` 178 | 179 | ## Replacement History 180 | 181 | ### Apc Customize UI++ 182 | 183 | Before `Custom UI Style`, [Apc Customize UI++](https://marketplace.visualstudio.com/items?itemName=drcika.apc-extension) 184 | was recommended as a replacement, which you could configure to achieve the same 185 | UI style by adding these settings in your `settings.json` file, see 186 | [Issue #52](https://github.com/drcika/apc-extension/issues/52): 187 | 188 | ```jsonc 189 | { 190 | "window.titleBarStyle": "native", 191 | "window.customTitleBarVisibility": "never", 192 | "window.density.editorTabHeight": "compact", 193 | "apc.electron": { 194 | "titleBarStyle": "hiddenInset", 195 | }, 196 | "apc.header": { 197 | "height": 37 198 | }, 199 | "apc.sidebar.titlebar": { 200 | "height": 37 201 | }, 202 | "apc.activityBar": { 203 | "size": 77, 204 | "itemSize": 48, 205 | "itemMargin": 0 206 | }, 207 | "apc.stylesheet": { 208 | // Don't indent the sidebar title. 209 | ".custom-sidebar-titlebar .sidebar .composite.title": "padding-left: 0;", 210 | // Don't indent the statusbar items. 211 | ".monaco-workbench .part.statusbar>.items-container>.statusbar-item.left.first-visible-item": "padding-left: 0;", 212 | // Show the host button, and make it as wide as the .activitybar. 213 | ".statusbar #status\\.host": "display: block !important; width: calc(var(--activity-bar-action-size) - 1px); background: #555 !important;", 214 | ".statusbar #status\\.host .codicon": "margin: 0 auto;", 215 | } 216 | } 217 | ``` 218 | 219 | ### Customize UI 220 | 221 | Before `Apc Customize UI++`, [Customize UI](https://marketplace.visualstudio.com/items?itemName=iocave.customize-ui) 222 | was recommended as a replacement, which you could configure to achieve the same 223 | UI style by adding these settings in your `settings.json` file: 224 | 225 | ```jsonc 226 | { 227 | "window.titleBarStyle": "native", 228 | "customizeUI.titleBar": "inline", 229 | "customizeUI.activityBar": "wide", 230 | "customizeUI.stylesheet": { 231 | // Center action items in .activitybar. 232 | ".monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item .action-label": "margin: 0 auto;", 233 | // Show the host button, and make it as wide as the .activitybar. 234 | ".statusbar #status\\.host": "width: 76px; background: #555 !important;", 235 | ".statusbar #status\\.host .codicon": "margin: 0 auto;" 236 | } 237 | } 238 | ``` 239 | 240 | ## Deprecated Installation Instructions 241 | 242 | Follow the instructions in the 243 | [Marketplace](https://marketplace.visualstudio.com/items?itemName=lehni.vscode-titlebar-less-macos), 244 | or run the following in the command palette: 245 | 246 | ```shell 247 | ext install lehni.vscode-titlebar-less-macos 248 | ``` 249 | 250 | Alternatively, you can run this command in the command line: 251 | 252 | ```sh 253 | code --install-extension lehni.vscode-titlebar-less-macos 254 | ``` 255 | 256 | ## Usage 257 | 258 | The extension adds 2 new commands to the command palette: 259 | 260 | ```js 261 | Titlebar-Less: Enable // Enable titlebar-less mode on macOS (patches core files) 262 | Titlebar-Less: Disable // Disable titlebar-less mode on macOS (restores core files) 263 | ``` 264 | 265 | After executing either of these commands, you need to fully restart VSCode in 266 | order to see the extension's effect. Simply reloading the window is not enough. 267 | 268 | If VSCode complains about it being corrupted after the restart, you have two 269 | options: 270 | 271 | 1. Install the 272 | [vscode-fix-checksums](https://marketplace.visualstudio.com/items?itemName=lehni.vscode-fix-checksums) 273 | extension and adjust the internal checksums to prevent this error from being 274 | displayed. This will also solve the display of `[Unsupported]` in titles and 275 | menus. 276 | 277 | 2. Choose `Don't Show Again`: 278 |

279 | Don't Show Again 280 |

281 | 282 | See [Disclaimer / A Word of Caution](#disclaimer--a-word-of-caution) for 283 | details. 284 | 285 | See [Required User Settings](#required-user-settings), if the title bar doesn't look right after the restart. 286 | 287 | ## Required User Settings 288 | 289 | This extension only works with the following User Settings. In order to change 290 | them, choose `Code` > `Preferences` > `Settings` in the menu, and add / change 291 | these lines: 292 | 293 | ```json 294 | "window.titleBarStyle": "custom", 295 | "window.nativeTabs": false, 296 | ``` 297 | 298 | ## Applying the Patches as Root 299 | 300 | Due to security restrictions on some systems, VSCode may need to run as root 301 | in order to be able to apply the patches. To do so, open the `Terminal.app` and 302 | run: 303 | 304 | ```sh 305 | sudo "/Applications/Visual Studio Code.app/Contents/MacOS/Electron" 306 | ``` 307 | 308 | Or this if you're using VSCode Insiders: 309 | 310 | ```sh 311 | sudo "/Applications/Visual Studio Code - Insiders.app/Contents/MacOS/Electron" 312 | ``` 313 | 314 | Once you ave applied the patches by executing `Titlebar-Less: Enable` as root, 315 | quit VSCode and start it normally without root privileges again. 316 | 317 | ## Disclaimer / A Word of Caution 318 | 319 | This extension modifies files that are part of the core of VSCode, so use it at 320 | your own risk. 321 | 322 | This extension creates backup files before modifying the core files, and these 323 | can be restored at any time using the `Titlebar-Less: Disable` command. 324 | 325 | If anything goes wrong, you can always reinstall VSCode from 326 | [code.visualstudio.com](https://code.visualstudio.com/download) without loosing 327 | any settings or installed extensions. 328 | 329 | ## Updating VSCode / Titlebar-Less 330 | 331 | When either VSCode or this extension is updated to a newer version, you can 332 | reapply the extension's modifications of the core files simply by running this 333 | command again, followed by a restart of the full application: 334 | 335 | ```js 336 | Titlebar-Less: Enable 337 | ``` 338 | 339 | ## Before / After 340 | 341 |

342 | Before/After 343 |

344 | 345 | ## Inspiration 346 | 347 | https://github.com/Microsoft/vscode/issues/12377 348 | 349 | In particular, [@orta](https://github.com/orta)'s work on a similar fork. 350 | 351 | ## License 352 | 353 | MIT © Jürg Lehni, 2018 354 | -------------------------------------------------------------------------------- /extension.js: -------------------------------------------------------------------------------- 1 | const vscode = require('vscode') 2 | const fs = require('fs') 3 | const path = require('path') 4 | 5 | const appDir = path.dirname(require.main.filename) 6 | const fsOptions = { encoding: 'utf8' } 7 | 8 | const version = parseFloat(vscode.version) 9 | 10 | const { showTrafficLights } = vscode.workspace.getConfiguration('titlebarLess') 11 | 12 | const patches = { 13 | 'vs/code/electron-main/main.js': [ 14 | // Override the Electron BrowserWindow options: 15 | // https://electronjs.org/docs/api/frameless-window 16 | [ 17 | '.titleBarStyle="hidden",', 18 | showTrafficLights 19 | ? `.titleBarStyle="${version < 1.26 ? 'hidden-inset' : 'hiddenInset'}",` 20 | : '.frame=false,' 21 | ] 22 | ], 23 | 'vs/workbench/workbench.main.js': [ 24 | // Never show the TITLEBAR_PART when "window.titleBarStyle" is "custom" 25 | [ 26 | new RegExp([ 27 | // TODO: Remove support for older versions once they are in a distant past 28 | // 1.33.0 <= v 29 | 'return"native"!==\\w\\.getTitleBarStyle\\(this\\.configurationService,this\\.environmentService\\)(&&\\(!this\\.state\\.fullscreen)', 30 | // 1.30.0 <= v < 1.33.0 31 | 'return!!this\\.useCustomTitleBarStyle\\(\\)(&&\\(!\\w\\.isFullscreen\\(\\))', 32 | // v < 1.30.0 33 | 'return"custom"===this\\.getCustomTitleBarStyle\\(\\)(&&\\(!\\w\\.isFullscreen\\(\\))' 34 | ].join('|')), 35 | 'return false$1$2$3' 36 | ], 37 | // Handle setting of traffic-lights size and .titlebar-less class on .monaco-workbench 38 | [ 39 | // Patch the full layout function in layout.ts, and parse it to retrieve 40 | // its parameter and the object on which to call `getZoomFactor()`: 41 | // Use `[^}]*` at the beginning and end of the body, to match any code 42 | // that doesn't involve any changing nesting. This is required to loosely 43 | // match smaller changes in different version of VSCode, as well as random 44 | // line-breaks inserted by the minifier. 45 | // Also, `this\.contextViewService\.layout\(\))` can't be matched anymore, 46 | // since that's now called further down in workbench.main.js too. 47 | // TODO: Replace `(\.layout=function\(\w+\)\{|layout\(\w+\)\{)` with `(layout\(\w+\)\{)` once VSCode v1.31.0 is in a distant past 48 | /(\.layout=function\(\w+\)\{|layout\(\w+\)\{)([^}]*this\.workbenchSize=[\s\S]*(\w+)\.getZoomFactor\(\)[\s\S]*this\.parts\.activitybar\.layout\([^)]*\)[^}]*)}/m, 49 | (all, func, body, browser) => { 50 | return `${func} 51 | var layoutService = ( 52 | // TODO: Remove support for older versions once they are in a distant past 53 | // 1.33.0 <= v 54 | this.layoutService || 55 | // v < 1.33.0 56 | this.partService 57 | ) 58 | var editorGroupService = layoutService && ( 59 | // TODO: Remove support for older versions once they are in a distant past 60 | // 1.33.0 <= v 61 | this.editorGroupService || 62 | // 1.31.0 <= v < 1.33.0 63 | layoutService.editorGroupService || 64 | // v < 1.31.0 65 | layoutService.workbenchLayout && layoutService.workbenchLayout.editorGroupService 66 | ); 67 | var configurationService = layoutService && layoutService.configurationService; 68 | var environmentService = layoutService && layoutService.environmentService; 69 | if (!layoutService) { 70 | console.error('Unable to retrieve layoutService'); 71 | } else if (!editorGroupService) { 72 | console.error('Unable to retrieve editorGroupService'); 73 | } else if (!configurationService) { 74 | console.error('Unable to retrieve configurationService'); 75 | } else if (!environmentService) { 76 | console.error('Unable to retrieve environmentService'); 77 | } else if ( 78 | // Only activate titlebar-less mode if "window.titleBarStyle" is set to "custom", 79 | // and VSCode isn't running as an Extension Development Host: 80 | !environmentService.isExtensionDevelopment && 81 | "custom" === configurationService.getValue().window.titleBarStyle 82 | ) { 83 | // Add .titlebar-less to .monaco-workbench, see workbench.main.css 84 | this.workbenchContainer.classList.add("titlebar-less"); 85 | ${showTrafficLights 86 | ? `// Set traffic-lights size, taking zoom-factor into account: 87 | var factor = ${browser}.getZoomFactor(); 88 | var width = 78 / factor; 89 | var height = 35 / factor; 90 | this.partLayoutInfo.activitybar.width = width;` 91 | 92 | : `var width = 0; 93 | var height = 0;` 94 | } 95 | var style = document.documentElement.style; 96 | style.setProperty("--traffic-lights-width", width + "px"); 97 | style.setProperty("--traffic-lights-height", height + "px"); 98 | // Install handlers on editorGroupService to determine the draggable titles with tabs, 99 | // by adding the .titlebar-less-draggable CSS class only to the titles at the top of the window: 100 | if (!this.titlebarLessHandlers) { 101 | var handleDraggableTitles = () => process.nextTick(() => { 102 | for (const title of Array.from(global.document.querySelectorAll('.editor .title'))) { 103 | title.classList.toggle('titlebar-less-draggable', !title.getBoundingClientRect().top); 104 | } 105 | }); 106 | var handlers = this.titlebarLessHandlers = []; 107 | editorGroupService.onDidLayout(handleDraggableTitles ,null, handlers); 108 | editorGroupService.onDidAddGroup(handleDraggableTitles ,null, handlers); 109 | editorGroupService.onDidMoveGroup(handleDraggableTitles ,null, handlers); 110 | editorGroupService.onDidRemoveGroup(handleDraggableTitles ,null, handlers); 111 | } 112 | } 113 | ${body} 114 | }` 115 | } 116 | ] 117 | ], 118 | 'vs/workbench/workbench.main.css': [ 119 | // Add our CSS modifications to the end of the main file 120 | [ 121 | /$/g, // Append to the end of the file 122 | readFile('workbench.main.css') 123 | ] 124 | ] 125 | } 126 | 127 | const messages = { 128 | success: verb => `Titlebar-less mode ${verb}. Please restart VSCode to see effect.`, 129 | fail: (verb, result) => `Unable to ${verb} all patches (${result.applied}/${result.total})` 130 | } 131 | 132 | exports.activate = function activate(context) { 133 | context.subscriptions.push( 134 | vscode.commands.registerCommand('titlebarLess.enable', enable), 135 | vscode.commands.registerCommand('titlebarLess.disable', disable) 136 | ) 137 | cleanupOrigFiles() 138 | } 139 | 140 | function enable() { 141 | // Always try to disable before enabling, but ignore if nothing was there to 142 | // disable (= it was already disabled before). 143 | let result = applyPatches(false) 144 | if (result.success || result.applied === 0) { 145 | result = applyPatches(true) 146 | vscode.window.showInformationMessage(result.success 147 | ? messages.success('enabled') 148 | : messages.fail('apply', result) 149 | ) 150 | } else { 151 | vscode.window.showInformationMessage(messages.fail('remove', result)) 152 | } 153 | } 154 | 155 | function disable() { 156 | const result = applyPatches(false) 157 | // Ignore if nothing was there to disable (= it was already disabled before). 158 | vscode.window.showInformationMessage(result.success || result.applied === 0 159 | ? messages.success('disabled') 160 | : messages.fail('remove', result) 161 | ) 162 | } 163 | 164 | function applyPatches(enable) { 165 | let applied = 0 166 | let total = 0 167 | for (const [filePath, filePatches] of Object.entries(patches)) { 168 | const file = getFilePath(filePath) 169 | const orig = `${file}.orig.${vscode.version}` 170 | try { 171 | const amount = filePatches.length 172 | total += amount 173 | if (enable) { 174 | let content = fs.readFileSync(file, fsOptions) 175 | let found = 0 176 | for (const [find, replace] of filePatches) { 177 | const patched = content.replace(find, replace) 178 | if (patched !== content) { 179 | content = patched 180 | found++ 181 | } else { 182 | console.error(`Unable to apply patch: ${find}`) 183 | } 184 | } 185 | if (found === amount) { 186 | fs.renameSync(file, orig) 187 | fs.writeFileSync(file, content, fsOptions) 188 | applied += amount 189 | } 190 | } else { 191 | if (fs.existsSync(orig)) { 192 | fs.unlinkSync(file) 193 | fs.renameSync(orig, file) 194 | applied += amount 195 | } 196 | } 197 | } catch (err) { 198 | console.error(err) 199 | } 200 | } 201 | return { 202 | success: applied === total, 203 | applied, 204 | total 205 | } 206 | } 207 | 208 | function cleanupOrigFiles() { 209 | // Remove all old backup files that aren't related to the current version 210 | // of VSCode anymore. 211 | for (const filePath of Object.keys(patches)) { 212 | const dir = path.dirname(getFilePath(filePath)) 213 | const oldOrigFiles = fs.readdirSync(dir) 214 | .filter(file => /\.orig\./.test(file)) 215 | .filter(file => !file.endsWith(vscode.version)) 216 | for (const file of oldOrigFiles) { 217 | fs.unlinkSync(path.join(dir, file)) 218 | } 219 | } 220 | } 221 | 222 | function getFilePath(filePath) { 223 | return path.join(appDir, ...filePath.split('/')) 224 | } 225 | 226 | function readFile(filename) { 227 | return fs.readFileSync(path.join(__dirname, filename), fsOptions) 228 | } 229 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "target": "es6", 5 | "checkJs": false, 6 | "lib": [ 7 | "es6" 8 | ] 9 | }, 10 | "exclude": [ 11 | "node_modules" 12 | ] 13 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vscode-titlebar-less-macos", 3 | "displayName": "Titlebar-Less VSCode for macOS", 4 | "description": "An extension for a titlebar-less VSCode on macOS", 5 | "icon": "resources/logo.png", 6 | "version": "1.10.5", 7 | "license": "MIT", 8 | "main": "./extension", 9 | "publisher": "lehni", 10 | "author": { 11 | "name": "Jürg Lehni", 12 | "email": "juerg@scratchdisk.com" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/lehni/vscode-titlebar-less-macos/issues" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/lehni/vscode-titlebar-less-macos" 20 | }, 21 | "engines": { 22 | "vscode": "^1.25.0" 23 | }, 24 | "keywords": [ 25 | "vscode", 26 | "vsc", 27 | "extension", 28 | "titlebar", 29 | "macos" 30 | ], 31 | "categories": [ 32 | "Other" 33 | ], 34 | "activationEvents": [ 35 | "onCommand:titlebarLess.enable", 36 | "onCommand:titlebarLess.disable" 37 | ], 38 | "contributes": { 39 | "configuration": { 40 | "title": "Titlebar-Less", 41 | "properties": { 42 | "titlebarLess.showTrafficLights": { 43 | "type": "boolean", 44 | "default": true, 45 | "description": "Toggles the macOS window control buttons. (requires restart)" 46 | } 47 | } 48 | }, 49 | "commands": [ 50 | { 51 | "command": "titlebarLess.enable", 52 | "title": "Titlebar-Less: Enable" 53 | }, 54 | { 55 | "command": "titlebarLess.disable", 56 | "title": "Titlebar-Less: Disable" 57 | } 58 | ] 59 | }, 60 | "scripts": { 61 | "postinstall": "node ./node_modules/vscode/bin/install" 62 | }, 63 | "devDependencies": { 64 | "@types/node": "^7.0.43", 65 | "@types/mocha": "^2.2.42", 66 | "eslint": "^4.11.0", 67 | "vscode": "^1.1.6" 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /resources/before-after.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lehni/vscode-titlebar-less-macos/84eb3e88219c08afaded32d0e6d30c3b9387f22f/resources/before-after.png -------------------------------------------------------------------------------- /resources/before-after.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lehni/vscode-titlebar-less-macos/84eb3e88219c08afaded32d0e6d30c3b9387f22f/resources/before-after.psd -------------------------------------------------------------------------------- /resources/dont-show-again.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lehni/vscode-titlebar-less-macos/84eb3e88219c08afaded32d0e6d30c3b9387f22f/resources/dont-show-again.png -------------------------------------------------------------------------------- /resources/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lehni/vscode-titlebar-less-macos/84eb3e88219c08afaded32d0e6d30c3b9387f22f/resources/logo.png -------------------------------------------------------------------------------- /resources/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lehni/vscode-titlebar-less-macos/84eb3e88219c08afaded32d0e6d30c3b9387f22f/resources/preview.png -------------------------------------------------------------------------------- /workbench.main.css: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Styles added by vscode-titlebar-less-macos extension 4 | * https://marketplace.visualstudio.com/items?itemName=lehni.vscode-titlebar-less-macos 5 | */ 6 | 7 | :root { 8 | --traffic-lights-width: 78px; 9 | --traffic-lights-height: 35px; 10 | } 11 | 12 | /** 13 | * Extension requires "window.titleBarStyle" to be set to "custom", 14 | * so use .titlebar-style-custom as a filter for all modifications. 15 | */ 16 | 17 | /* Make activity-bar wider to align with traffic lights */ 18 | .monaco-workbench.titlebar-less .activitybar, 19 | .monaco-workbench.titlebar-less .activitybar .content { 20 | width: var(--traffic-lights-width); 21 | } 22 | 23 | /* Move activity-bar .content down to not be covered by traffic lights */ 24 | .monaco-workbench.titlebar-less:not(.fullscreen) .activitybar:not(.right) .content .monaco-action-bar.vertical { 25 | margin-top: var(--traffic-lights-height); 26 | } 27 | 28 | /* Move editor title when side-bar is hidden... */ 29 | .monaco-workbench.titlebar-less:not(.fullscreen).nosidebar .editor .title, 30 | /* ...or when side-bar is on the right... */ 31 | .monaco-workbench.titlebar-less:not(.fullscreen) .sidebar.right + .editor .title, 32 | /* ...also move side-bar title when activity-bar is hidden and side-bar is not on the right... */ 33 | .monaco-workbench.titlebar-less:not(.fullscreen) .activitybar[aria-hidden="true"] + .sidebar:not(.right) > .title { 34 | padding-left: var(--traffic-lights-width); 35 | } 36 | /* ... and for all the selectors above, move nested .tabs-breadcrumbs back by the same amount. */ 37 | .monaco-workbench.titlebar-less:not(.fullscreen).nosidebar .editor .title .tabs-breadcrumbs, 38 | .monaco-workbench.titlebar-less:not(.fullscreen) .sidebar.right + .editor .title .tabs-breadcrumbs, 39 | .monaco-workbench.titlebar-less:not(.fullscreen) .activitybar[aria-hidden="true"] + .sidebar:not(.right) > .title .tabs-breadcrumbs { 40 | margin-left: calc(-1 * var(--traffic-lights-width)); 41 | } 42 | 43 | /* ...but when activity-bar is visible and side-bar is hidden... */ 44 | .monaco-workbench.titlebar-less:not(.fullscreen) .activitybar:not([aria-hidden="true"]) + .sidebar:not(.right) + .editor .title, 45 | /* ...or when there's a split-view with multiple tabs, don't move .tabs-container... */ 46 | .monaco-workbench.titlebar-less .split-view-container .split-view-view + .split-view-view .title { 47 | padding-left: 0 !important; 48 | } 49 | /* ... and for all the selectors above, clear moving of nested .tabs-breadcrumbs. */ 50 | .monaco-workbench.titlebar-less:not(.fullscreen) .activitybar:not([aria-hidden="true"]) + .sidebar:not(.right) + .editor .title .tabs-breadcrumbs, 51 | .monaco-workbench.titlebar-less .split-view-container .split-view-view + .split-view-view .title .tabs-breadcrumbs { 52 | margin-left: 0 !important; 53 | } 54 | 55 | .monaco-workbench.titlebar-less .activitybar .content .monaco-action-bar .action-label { 56 | width: auto; 57 | background-position: center; 58 | padding: 0; 59 | } 60 | 61 | .monaco-workbench.titlebar-less .activitybar .content .monaco-action-bar .action-item::before { 62 | width: auto; 63 | right: 0; 64 | } 65 | 66 | .monaco-workbench.titlebar-less .activitybar .content .monaco-action-bar .badge { 67 | width: 100%; 68 | } 69 | 70 | .monaco-workbench.titlebar-less .activitybar .content .monaco-action-bar .badge .badge-content { 71 | left: calc(50% + 0.5em); /* Relative to font-size, so it works on all zoom levels */ 72 | right: auto; 73 | } 74 | 75 | /* Allow dragging on the activity-bar... */ 76 | .monaco-workbench.titlebar-less:not(.fullscreen) .activitybar { 77 | -webkit-app-region: drag; 78 | } 79 | 80 | /* ...but still allow to click actions without dragging */ 81 | .monaco-workbench.titlebar-less:not(.fullscreen) .activitybar .content .monaco-action-bar { 82 | -webkit-app-region: no-drag; 83 | } 84 | 85 | /* Allow dragging on side-bar title... */ 86 | .monaco-workbench.titlebar-less:not(.fullscreen) .sidebar .composite.title { 87 | -webkit-app-region: drag; 88 | } 89 | /* ...but still allow to click actions without dragging. */ 90 | .monaco-workbench.titlebar-less:not(.fullscreen) .sidebar .title .title-actions .action-label { 91 | -webkit-app-region: no-drag; 92 | } 93 | 94 | /* Allow dragging on titles with tabs at the top of the window... */ 95 | .monaco-workbench.titlebar-less:not(.fullscreen) .titlebar-less-draggable.title { 96 | -webkit-app-region: drag; 97 | -webkit-user-drag: none; 98 | } 99 | 100 | /* ...but still allow dragging the tabs themselves to re-order. */ 101 | .monaco-workbench.titlebar-less:not(.fullscreen) .titlebar-less-draggable.title .tablist, 102 | .monaco-workbench.titlebar-less:not(.fullscreen) .titlebar-less-draggable.title .tab { 103 | -webkit-app-region: no-drag; 104 | } 105 | --------------------------------------------------------------------------------