├── .gitignore ├── LICENSE ├── README.md ├── landscape ├── landscape.css ├── landscape.jsx ├── screenshot.jpg └── workspaces ├── 1 ├── 2 ├── 3 ├── 4 ├── 5 ├── 6 ├── 7 ├── 8 ├── 9 └── 10 /.gitignore: -------------------------------------------------------------------------------- 1 | .cache 2 | 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Daniel Neemann 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 | # landscape 2 | 3 |  4 | 5 | A bar for macos that displays the current workspaces. 6 | 7 | ## Installation and requirements 8 | 9 | Requires [*yabai*](https://github.com/koekeishiya/yabai) and [Übersicht](http://tracesof.net/uebersicht/). 10 | 11 | Once Übersicht is installed, download this repository to wherever your widgets are stored (by default ~/Library/Application Support/Übersicht/widgets/). 12 | 13 | This can be done via the terminal like so: 14 | 15 | ```sh 16 | brew tap caskroom/cask 17 | brew cask install ubersicht 18 | git clone https://github.com/zzzeyez/landscape.git "$HOME/Library/Application Support/Übersicht/widgets/landscape" 19 | ``` 20 | 21 | And install *Yabai*: 22 | 23 | ```sh 24 | brew install yabai 25 | brew services start yabai 26 | ``` 27 | 28 | To create and destroy workspaces you will need to disable SIP. Instructions are on the page for *Yabai*. 29 | 30 | You will need the included `landscape` in your $PATH: 31 | 32 | ```sh 33 | ln -s "${HOME}/Library/Application Support/Übersicht/widgets/landscape/landscape" "/usr/local/bin/landscape" 34 | ``` 35 | 36 | And now when you switch workspaces via skhd you'll want to run `landscape` like so: 37 | 38 | ```sh 39 | ctrl - left : yabai -m space --focus prev && landscape 40 | ``` 41 | 42 | ## Use 43 | 44 | Click the button on the far right to create a new workspace. Right click that same button to delete current workspace. 45 | 46 | [](https://www.paypal.me/zzzeyez/) 47 | -------------------------------------------------------------------------------- /landscape: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | # author: daniel neemann (@zzzeyez) 4 | # 5 | # displays notifications on macos 6 | # requires `ubersicht` 7 | 8 | pre() { 9 | screencapture -x "$cache/$ws.png" 10 | } 11 | 12 | setup() { 13 | 14 | # get path 15 | SOURCE="${BASH_SOURCE[0]}" 16 | while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink 17 | DIR="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" 18 | SOURCE="$(readlink "$SOURCE")" 19 | [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located 20 | done 21 | dir="$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd )" 22 | 23 | # check if cache exists 24 | cache="$dir/.cache" 25 | if [[ ! -d "$cache" ]] ; then 26 | mkdir "$cache" 27 | fi 28 | 29 | # get workspace 30 | ws="$(/usr/local/bin/yabai -m query --spaces --space | grep index | sed 's/[^0-9]*//g')" 31 | 32 | # if not refreshing then take screenshot and exit 33 | if [[ "$#" -ne 0 ]]; then 34 | screencapture -x "$cache/$ws.png" 35 | exit 36 | fi 37 | wstotal="$(/usr/local/bin/yabai -m query --spaces | grep 'index' | wc -l | sed 's/[^0-9]*//g')" 38 | 39 | # check if ubersicht is running 40 | ps cax | grep sicht > /dev/null 41 | if [ $? -eq 0 ]; then 42 | # stupid umlaut character 43 | ubersicht="$(ps ax | grep sicht | awk '{print $5}' | head -1 | cut -d/ -f3 | cut -d. -f1)" 44 | if [[ "$ubersicht" = "UM-LM^Hbersicht" ]] ; then 45 | ubersicht="Übersicht" 46 | fi 47 | fi 48 | } 49 | 50 | refresh() { 51 | if [[ "$ubersicht" ]] ; then 52 | #osascript -e 'tell application "'$ubersicht'" to refresh widget id "'$1'"' 53 | osascript -e 'tell application "'$ubersicht'" to refresh' 54 | fi 55 | } 56 | 57 | main() { 58 | screencapture -x "$cache/$ws.png" 59 | cp "$dir/workspaces/$wstotal" "$dir/landscape.jsx" 60 | echo ":root { --wstotal: $wstotal; } .landscape li.ls$ws { box-shadow: inset 0 0 0 2px var(--color1, #fff); }" > "$cache/current.css" 61 | refresh landscape-landscape-jsx 62 | #osascript -e 'tell application "'$ubersicht'" to refresh widget id "'landscape-landscape-jsx'"' 63 | } 64 | 65 | setup "$@" 66 | main 67 | -------------------------------------------------------------------------------- /landscape.css: -------------------------------------------------------------------------------- 1 | /* landscape is a workspace switcher for macos */ 2 | /* written by daniel neemann (@zzzeyez) */ 3 | 4 | :root { 5 | 6 | /*todo*/ 7 | /*--landscape-ratio: ;*/ 8 | /*--landscape-border-radius: ;*/ 9 | 10 | --landscape-height: var(--height-landscape, var(--default-height-landscape, 50px)); 11 | --landscape-width: calc(var(--landscape-height, 50px) * 1.6); 12 | 13 | /*split this into vertical + horizontal*/ 14 | --landscape-margin: var(--margin-landscape, var(--default-margin-landscape)); 15 | --landscape-border: var(--border-landscape, var(--default-border-landscape)); 16 | --landscape-border-radius: var(--border-radius-landscape, var(--default-border-radius-landscape)); 17 | 18 | --landscape-width-total: calc( 19 | /*var(--landscape-width) * 1 */ 20 | var(--landscape-width) * var(--wstotal) 21 | + var(--landscape-border, 3px) * var(--wstotal) 22 | - var(--text-padding, 1ch) * 2 - 1ch - var(--landscape-border) 23 | /*add space*/ 24 | + var(--landscape-width) + var(--landscape-border) 25 | ); 26 | } 27 | 28 | /* Get monitor width */ 29 | .screen { 30 | width: 100vw; 31 | height: 100vh; 32 | } 33 | 34 | .landscape { 35 | height: var(--landscape-height); 36 | top: 0; 37 | right: auto; 38 | bottom: auto; 39 | left: 0; 40 | background: var(--landscape-background, var(--default-background-landscape, NONE)); 41 | display: block; 42 | position: absolute; 43 | margin: var(--landscape-margin, 0px); 44 | padding: var(--landscape-border, 10px); 45 | border-radius: 0px; 46 | z-index: 3; 47 | font: var(--font-size, 12px) var(--font, Menlo); 48 | } 49 | 50 | .landscape ul { 51 | height: var(--landscape-height); 52 | padding: 0px; 53 | margin: 0px; 54 | } 55 | 56 | .landscape li { 57 | width: var(--landscape-width); 58 | height: var(--landscape-height); 59 | line-height: var(--landscape-height); 60 | float: left; 61 | padding: 0; 62 | margin: 0 var(--landscape-border, 10px) 0 0; 63 | list-style-type: none; 64 | border-radius: var(--landscape-border-radius, 3px); 65 | box-shadow: var(--shadow-landscape, var(--default-shadow-landscape, 0px 1px 1px 1px rgba(0, 0, 0, 0.18))); 66 | text-align: center; 67 | } 68 | 69 | .landscape li:last-child { 70 | margin-right: 0px; 71 | } 72 | 73 | .landscape li:hover { 74 | box-shadow: inset 0 0 0 2px var(--color7, #fff); 75 | } 76 | 77 | .landscape li.add { 78 | background: var(--color8, #999); 79 | } 80 | 81 | .landscape li.ls1 { 82 | background: url('/landscape/.cache/1.png'); 83 | background-size: cover; 84 | background-repeat: no-repeat; 85 | background-position: center; 86 | } 87 | 88 | .landscape li.ls2 { 89 | /*outline: 3px solid var(--color1);*/ 90 | /*outline-offset: -3px;*/ 91 | background: url('/landscape/.cache/2.png'); 92 | background-size: cover; 93 | background-repeat: no-repeat; 94 | background-position: center; 95 | } 96 | 97 | .landscape li.ls3 { 98 | background: url('/landscape/.cache/3.png'); 99 | background-size: cover; 100 | background-repeat: no-repeat; 101 | background-position: center; 102 | } 103 | 104 | .landscape li.ls4 { 105 | background: url('/landscape/.cache/4.png'); 106 | background-size: cover; 107 | background-repeat: no-repeat; 108 | background-position: center; 109 | } 110 | 111 | .landscape li.ls5 { 112 | background: url('/landscape/.cache/5.png'); 113 | background-size: cover; 114 | background-repeat: no-repeat; 115 | background-position: center; 116 | } 117 | 118 | .landscape li.ls6 { 119 | background: url('/landscape/.cache/6.png'); 120 | background-size: cover; 121 | background-repeat: no-repeat; 122 | background-position: center; 123 | } 124 | 125 | .landscape li.ls7 { 126 | background: url('/landscape/.cache/7.png'); 127 | background-size: cover; 128 | background-repeat: no-repeat; 129 | background-position: center; 130 | } 131 | 132 | .landscape li.ls8 { 133 | background: url('/landscape/.cache/8.png'); 134 | background-size: cover; 135 | background-repeat: no-repeat; 136 | background-position: center; 137 | } 138 | 139 | .landscape li.ls9 { 140 | background: url('/landscape/.cache/9.png'); 141 | background-size: cover; 142 | background-repeat: no-repeat; 143 | background-position: center; 144 | } 145 | 146 | .landscape li.ls10 { 147 | background: url('/landscape/.cache/10.png'); 148 | background-size: cover; 149 | background-repeat: no-repeat; 150 | background-position: center; 151 | } 152 | -------------------------------------------------------------------------------- /landscape.jsx: -------------------------------------------------------------------------------- 1 | import { run } from 'uebersicht' 2 | 3 | const refreshFrequency = false; 4 | 5 | function selectSpace(ws) { 6 | run(`/usr/local/bin/yabai -m space --focus ${ws} && /usr/local/bin/landscape`) 7 | } 8 | 9 | function addSpace(ws) { 10 | run(`/usr/local/bin/yabai -m space --create && /usr/local/bin/yabai -m space --focus ${ws} && /usr/local/bin/landscape`) 11 | } 12 | 13 | function deleteSpace() { 14 | run(`/usr/local/bin/yabai -m space --destroy && /usr/local/bin/landscape`) 15 | } 16 | 17 | const render = () => { 18 | return ( 19 |