├── .conky └── conky-ev │ ├── .conkyrc │ ├── README │ ├── fonts │ └── DroidSans.ttf │ ├── screenshot.png │ ├── source.txt │ └── weather-icons │ ├── 0.png │ ├── 1.png │ ├── 10.png │ ├── 11.png │ ├── 12.png │ ├── 13.png │ ├── 14.png │ ├── 15.png │ ├── 16.png │ ├── 17.png │ ├── 18.png │ ├── 19.png │ ├── 2.png │ ├── 20.png │ ├── 21.png │ ├── 22.png │ ├── 23.png │ ├── 24.png │ ├── 25.png │ ├── 26.png │ ├── 27.png │ ├── 28.png │ ├── 29.png │ ├── 3.png │ ├── 30.png │ ├── 31.png │ ├── 32.png │ ├── 3200.png │ ├── 33.png │ ├── 34.png │ ├── 35.png │ ├── 36.png │ ├── 37.png │ ├── 38.png │ ├── 39.png │ ├── 4.png │ ├── 40.png │ ├── 41.png │ ├── 42.png │ ├── 43.png │ ├── 44.png │ ├── 45.png │ ├── 46.png │ ├── 47.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ ├── 9.png │ ├── humidity.png │ └── wind.png ├── .github └── workflows │ └── setup.yml ├── .gitignore ├── .gitmodules ├── .p10k.zsh ├── .vim ├── colors │ ├── molokai.vim │ └── solarized.vim └── startup │ ├── easymotion_vimrc │ ├── map_vimrc │ ├── python_vimrc │ └── vundle_vimrc ├── .vimrc ├── .zprofile ├── .zshrc ├── .zshrc.local ├── Brewfile ├── Brewfile-essentials ├── Makefile ├── README.md ├── aerospace └── aerospace.toml ├── backup ├── backup_tencent_cos.py ├── qiniu-backup-folder.sh ├── qiniu-backup.sh └── wordpress_backup.sh ├── bin ├── git-credit ├── git-delete-local-merged ├── git-edit-new ├── git-nuke ├── gitio ├── serve └── yt ├── config ├── apt.conf.yml ├── asdf-install.conf.yml ├── asdf.conf.yml ├── bootstrap.conf.yml ├── crontab.conf.yml ├── hammerspoon.conf.yml ├── install.conf.yml ├── macos.conf.yml ├── packages.conf.yml ├── projects.conf.yml ├── snap.conf.yml ├── termux.conf.yml ├── tmux.conf.yml └── update.conf.yml ├── espanso ├── config │ └── default.yml ├── default.yml └── match │ └── base.yml ├── git ├── gitignore_global ├── global.gitconfig └── work.gitconfig ├── hammerspoon ├── .gitignore ├── README.md ├── Spoons │ ├── AClock.spoon │ │ ├── docs.json │ │ └── init.lua │ ├── BingDaily.spoon │ │ ├── docs.json │ │ └── init.lua │ ├── Caffeine.spoon │ │ ├── caffeine-off.pdf │ │ ├── caffeine-on.pdf │ │ ├── docs.json │ │ └── init.lua │ ├── Calendar.spoon │ │ ├── docs.json │ │ └── init.lua │ ├── CircleClock.spoon │ │ ├── docs.json │ │ ├── init.lua │ │ └── watchbg.png │ ├── ClipShow.spoon │ │ ├── docs.json │ │ └── init.lua │ ├── CountDown.spoon │ │ ├── docs.json │ │ └── init.lua │ ├── HCalendar.spoon │ │ ├── docs.json │ │ └── init.lua │ ├── HSearch.spoon │ │ ├── docs.json │ │ ├── hs_btabs.lua │ │ ├── hs_datamuse.lua │ │ ├── hs_emoji.lua │ │ ├── hs_note.lua │ │ ├── hs_time.lua │ │ ├── hs_v2ex.lua │ │ ├── hs_yddict.lua │ │ ├── init.lua │ │ └── resources │ │ │ ├── chrome.png │ │ │ ├── emoji.png │ │ │ ├── justnote.png │ │ │ ├── menus.png │ │ │ ├── safari.png │ │ │ ├── tabs.png │ │ │ ├── taskkill.png │ │ │ ├── thesaurus.png │ │ │ ├── time.png │ │ │ ├── v2ex.png │ │ │ └── youdao.png │ ├── KSheet.spoon │ │ ├── docs.json │ │ └── init.lua │ ├── ModalMgr.spoon │ │ ├── docs.json │ │ └── init.lua │ ├── PomodoroTimer.spoon │ │ └── init.lua │ ├── ReloadConfiguration.spoon │ │ ├── docs.json │ │ └── init.lua │ ├── SpeedMenu.spoon │ │ ├── docs.json │ │ └── init.lua │ ├── TimeFlow.spoon │ │ ├── docs.json │ │ ├── init.lua │ │ └── timebg.png │ ├── UnsplashZ.spoon │ │ ├── docs.json │ │ └── init.lua │ ├── WinRectangle.spoon │ │ └── init.lua │ └── WinWin.spoon │ │ ├── docs.json │ │ └── init.lua ├── autoscript.lua ├── config-example.lua ├── ime.lua ├── init.lua └── usb.lua ├── idea ├── .ideavimrc └── actionlist ├── init ├── init_cinnamon_applets.sh ├── init_install.sh └── init_server.sh ├── install ├── karabiner ├── karabiner.edn └── karabiner.json ├── kitty ├── base16-solarized-dark-256.conf └── kitty.conf ├── mac_bootstrap.sh ├── mackup └── mackup.cfg ├── macos └── init_mac.sh ├── pulsedmedia ├── README.md └── app-installation.sh ├── python_scripts └── clone-all-gitlab.py ├── script ├── byzanz-record-gui.sh ├── byzanz-record-region.sh ├── byzanz-record-window.sh ├── git-log-by-day.sh ├── install.sh ├── install_android_decompiler.sh ├── install_byzanz_record.sh ├── install_conky.sh ├── qiniu-autosync.sh ├── ssh_login ├── tmux_local_install.sh └── ubuntu_init.sh ├── skhd └── skhdrc ├── style └── intellij-java-google-style.xml ├── termux ├── .termux │ ├── colors.properties │ ├── font.ttf │ └── termux.properties ├── README.md └── setup.sh ├── tmux ├── .tmux.conf ├── .tmux.conf.local └── scripts │ └── uptime.sh ├── yabai ├── yabai_focus_display └── yabairc └── zsh ├── alias.zsh ├── common.zsh ├── env.zsh ├── fzf.zsh ├── github-copilot-cli.zsh ├── keybindings.zsh └── osx.zsh /.conky/conky-ev/README: -------------------------------------------------------------------------------- 1 | 2 | Make sure conky is installed, and install this PPA: 3 | 4 | sudo apt-add-repository -y ppa:teejee2008/ppa 5 | sudo apt-get update 6 | sudo apt-get install conky-manager 7 | 8 | Make sure to install "curl" in order to display the weather! (sudo apt-get install curl) 9 | 10 | Install this conkyrc: 11 | 12 | 1. Install the "DroidSans" fonts. 13 | 2. Run `/.install_conky.sh` to copy files to .conky dir 14 | 3. Replace your own yahoo weather. 15 | - go to https://developer.yahoo.com/weather/ 16 | - select XML, copy and paste (with your woeid): select * from weather.forecast where woeid = "2151330" and u = "c" , you need to change the text content to your own city. 17 | - then copy the url to edit in .conkyrc file 18 | 19 | 4. The first day in the conky displays the forecast for today.(in blue color) 20 | 5. Run conky 21 | 22 | ------------------------------------------------------------------------------------------------------- 23 | NOTE* 24 | 25 | Be careful when changing fonts as well as font sizes!The images in conky have a set position, so if you change the font or size of the text, the text will move up/down while the images will stay in position, making it look out of place. 26 | 27 | If you wish to change the font you can fix the problem in 3 ways: 28 | 29 | 1. Either change the size of the font until your images are in position 30 | 31 | 2. Change the position off the corresponding images.Search for "-p" and manipulate the neccesary values. 32 | 33 | 3. You can also move the text up/down by adding empty lines in the conkyrc.(enter/backspace) 34 | 35 | ------------------------------------------------------------------------------------------------------- 36 | -------------------------------------------------------------------------------- /.conky/conky-ev/fonts/DroidSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/fonts/DroidSans.ttf -------------------------------------------------------------------------------- /.conky/conky-ev/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/screenshot.png -------------------------------------------------------------------------------- /.conky/conky-ev/source.txt: -------------------------------------------------------------------------------- 1 | https://einverne.github.io 2 | -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/0.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/1.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/10.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/11.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/12.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/13.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/14.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/15.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/16.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/17.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/18.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/19.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/2.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/20.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/21.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/22.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/23.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/24.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/25.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/26.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/27.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/28.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/29.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/3.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/30.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/31.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/32.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/3200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/3200.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/33.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/34.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/35.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/36.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/37.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/37.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/38.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/39.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/39.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/4.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/40.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/41.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/41.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/42.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/42.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/43.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/43.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/44.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/44.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/45.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/45.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/46.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/46.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/47.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/47.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/5.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/6.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/7.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/8.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/9.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/humidity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/humidity.png -------------------------------------------------------------------------------- /.conky/conky-ev/weather-icons/wind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/.conky/conky-ev/weather-icons/wind.png -------------------------------------------------------------------------------- /.github/workflows/setup.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: [push, pull_request] 3 | jobs: 4 | ubuntu: 5 | name: ubuntu-bootstrap 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@master 9 | - name: build 10 | env: 11 | CI_MODE: true 12 | run: | 13 | make bootstrap 14 | make tmux 15 | macos: 16 | name: macos-bootstrap 17 | runs-on: macos-latest 18 | steps: 19 | - uses: actions/checkout@master 20 | - name: build 21 | env: 22 | CI_MODE: true 23 | run: | 24 | make 25 | make bootstrap 26 | make dotfiles 27 | #make macos 28 | #make asdf 29 | #make brew 30 | make tmux 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vim74/* 2 | vimfiles/* 3 | .vim/bundle/* 4 | .vim/autoload/plug.vim 5 | 6 | [._]*.s[a-w][a-z] 7 | [._]s[a-w][a-z] 8 | *.un~ 9 | Session.vim 10 | .netrwhist 11 | *~ 12 | 13 | *.ba 14 | 15 | karabiner/automatic_backups/* 16 | .idea/* 17 | *.pyc 18 | .DS_Store 19 | dotfiles.iml 20 | .vscode -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "dotbot"] 2 | path = dotbot 3 | url = https://github.com/anishathalye/dotbot 4 | ignore = dirty 5 | [submodule "dotbot-brew"] 6 | path = dotbot-brew 7 | url = https://github.com/d12frosted/dotbot-brew.git 8 | [submodule "dotbot-asdf"] 9 | path = dotbot-asdf 10 | url = https://github.com/sobolevn/dotbot-asdf.git 11 | [submodule "dotbot-apt-get"] 12 | path = dotbot-apt-get 13 | url = https://github.com/rubenvereecken/dotbot-apt-get 14 | [submodule "dotbot-snap"] 15 | path = dotbot-snap 16 | url = https://github.com/DrDynamic/dotbot-snap.git 17 | [submodule "crontab-dotbot"] 18 | path = crontab-dotbot 19 | url = https://github.com/fundor333/crontab-dotbot.git 20 | [submodule "hammerspoon/Spoons/EnhancedSpaces.spoon"] 21 | path = hammerspoon/Spoons/EnhancedSpaces.spoon 22 | url = https://github.com/franzbu/EnhancedSpaces.spoon.git 23 | -------------------------------------------------------------------------------- /.vim/startup/easymotion_vimrc: -------------------------------------------------------------------------------- 1 | 2 | "Turn on case insensitive feature 3 | let g:EasyMotion_smartcase = 1 4 | "Use upper target labels and type as a lower case 5 | let g:EasyMotion_use_upper = 1 6 | 7 | " move to character 8 | nmap f (easymotion-s2) 9 | xmap f (easymotion-s2) 10 | omap f (easymotion-s2) 11 | " move to word 12 | nmap F (easymotion-bd-w) 13 | xmap F (easymotion-bd-w) 14 | omap F (easymotion-bd-w) 15 | " move to line 16 | nmap gl (easymotion-bd-jk) 17 | xmap gl (easymotion-bd-jk) 18 | omap gl (easymotion-bd-jk) 19 | -------------------------------------------------------------------------------- /.vim/startup/map_vimrc: -------------------------------------------------------------------------------- 1 | " vim map setting 2 | 3 | " Treat long lines as break lines (useful when moving around in them) 4 | map j gj 5 | map k gk 6 | nnoremap j gj 7 | nnoremap k gk 8 | 9 | " fast saving 10 | nmap w :w! 11 | 12 | " use tab as % 13 | nmap % 14 | vmap % 15 | 16 | " smart way to move between windows 17 | map j 18 | map k 19 | map h 20 | map l 21 | 22 | " Useful mappings for managin tabs 23 | map tn :tabnew 24 | map to :tabonly 25 | map tc :tabclose 26 | 27 | " Disable arrowkeys 28 | noremap 29 | noremap 30 | noremap 31 | noremap 32 | 33 | " visual mode copy and paste to system clipboard 34 | "vmap "+y 35 | "vmap "+c 36 | "vmap "+p 37 | 38 | " 将VIM中将内容复制到系统粘贴板,或者从系统粘贴板粘贴到VIM 39 | nnoremap "+y 40 | vnoremap "+y 41 | vnoremap "+gP 42 | nnoremap "+gP 43 | 44 | " 快速移动当前行 45 | nnoremap :m .+1== 46 | nnoremap :m .-2== 47 | inoremap :m .+1==gi 48 | inoremap :m .-2==gi 49 | vnoremap :m '>+1gv=gv 50 | vnoremap :m '<-2gv=gv 51 | 52 | " 空格关闭高亮,清空所有已经显示 53 | nnoremap :set hlsearch! hlsearch? 54 | 55 | " Save a file as root (,W) 56 | noremap W :w !sudo tee % > /dev/null 57 | 58 | " sudo write 59 | " https://catonmat.net/sudo-vim 60 | cnoremap sudow w !sudo tee % >/dev/null 61 | 62 | " Strip trailing whitespace (,ss) 63 | function! StripWhitespace() 64 | let save_cursor = getpos(".") 65 | let old_query = getreg('/') 66 | :%s/\s\+$//e 67 | call setpos('.', save_cursor) 68 | call setreg('/', old_query) 69 | endfunction 70 | noremap ss :call StripWhitespace() 71 | 72 | set hidden " 避免必须保存修改才可以跳转 buffer 73 | " buffer 74 | "nmap n :bnext 75 | "nmap p :bprev 76 | 77 | function! Xml() 78 | set filetype=xml 79 | :%s/>\r<替换成>回车< 80 | :normal gg=G 81 | endfunction 82 | map xml :call Xml() 83 | 84 | " plugin map setting 85 | 86 | " Normal mode F2 to call NERDTree 87 | nmap :NERDTreeToggle 88 | 89 | nnoremap i :PlugInstall 90 | nnoremap u :PlugUpdate 91 | 92 | " fzf 93 | nnoremap :GFiles 94 | nnoremap f :Files 95 | nnoremap C :Colors 96 | nnoremap :Buffers 97 | nnoremap fl :Lines 98 | nnoremap ag :Ag! 99 | nnoremap m :History 100 | -------------------------------------------------------------------------------- /.vim/startup/python_vimrc: -------------------------------------------------------------------------------- 1 | " vimrc file for following the coding standards specified in PEP 7 & 8. 2 | " 3 | " To use this file, source it in your own personal .vimrc file (``source 4 | " ``) or, if you don't have a .vimrc file, you can just symlink to it 5 | " (``ln -s ~/.vimrc``). All options are protected by autocmds 6 | " (read below for an explanation of the command) so blind sourcing of this file 7 | " is safe and will not affect your settings for non-Python or non-C files. 8 | " 9 | " 10 | " All setting are protected by 'au' ('autocmd') statements. Only files ending 11 | " in .py or .pyw will trigger the Python settings while files ending in *.c or 12 | " *.h will trigger the C settings. This makes the file "safe" in terms of only 13 | " adjusting settings for Python and C files. 14 | " 15 | " Only basic settings needed to enforce the style guidelines are set. 16 | " Some suggested options are listed but commented out at the end of this file. 17 | 18 | " Number of spaces that a pre-existing tab is equal to. 19 | " For the amount of space used for a new tab use shiftwidth. 20 | au BufRead,BufNewFile *py,*pyw,*.c,*.h set tabstop=8 21 | 22 | " What to use for an indent. 23 | " This will affect Ctrl-T and 'autoindent'. 24 | " Python: 4 spaces 25 | " C: tabs (pre-existing files) or 4 spaces (new files) 26 | au BufRead,BufNewFile *.py,*pyw set shiftwidth=4 27 | au BufRead,BufNewFile *.py,*.pyw set expandtab 28 | fu! Select_c_style() 29 | if search('^\t', 'n', 150) 30 | set shiftwidth=8 31 | set noexpandtab 32 | el 33 | set shiftwidth=4 34 | set expandtab 35 | en 36 | endf 37 | au BufRead,BufNewFile *.c,*.h call Select_c_style() 38 | au BufRead,BufNewFile Makefile* set noexpandtab 39 | 40 | " Use the below highlight group when displaying bad whitespace is desired. 41 | highlight BadWhitespace ctermbg=red guibg=red 42 | 43 | " Display tabs at the beginning of a line in Python mode as bad. 44 | au BufRead,BufNewFile *.py,*.pyw match BadWhitespace /^\t\+/ 45 | " Make trailing whitespace be flagged as bad. 46 | au BufRead,BufNewFile *.py,*.pyw,*.c,*.h match BadWhitespace /\s\+$/ 47 | 48 | " Wrap text after a certain number of characters 49 | " Python: 79 50 | " C: 79 51 | au BufRead,BufNewFile *.py,*.pyw,*.c,*.h set textwidth=79 52 | 53 | " Turn off settings in 'formatoptions' relating to comment formatting. 54 | " - c : do not automatically insert the comment leader when wrapping based on 55 | " 'textwidth' 56 | " - o : do not insert the comment leader when using 'o' or 'O' from command mode 57 | " - r : do not insert the comment leader when hitting in insert mode 58 | " Python: not needed 59 | " C: prevents insertion of '*' at the beginning of every line in a comment 60 | au BufRead,BufNewFile *.c,*.h set formatoptions-=c formatoptions-=o formatoptions-=r 61 | 62 | " Use UNIX (\n) line endings. 63 | " Only used for new files so as to not force existing files to change their 64 | " line endings. 65 | " Python: yes 66 | " C: yes 67 | au BufNewFile *.py,*.pyw,*.c,*.h set fileformat=unix 68 | 69 | " auto add #! /usr/bin/env python 70 | autocmd BufNewfile *.py call append(0,'#!/usr/bin/env python') 71 | autocmd BufNewfile *.py call append(1,'# -*- coding: UTF-8 -*-') 72 | 73 | " ---------------------------------------------------------------------------- 74 | " The following section contains suggested settings. While in no way required 75 | " to meet coding standards, they are helpful. 76 | 77 | " Set the default file encoding to UTF-8: ``set encoding=utf-8`` 78 | 79 | " Puts a marker at the beginning of the file to differentiate between UTF and 80 | " UCS encoding (WARNING: can trick shells into thinking a text file is actually 81 | " a binary file when executing the text file): ``set bomb`` 82 | 83 | " For full syntax highlighting: 84 | "``let python_highlight_all=1`` 85 | "``syntax on`` 86 | 87 | " Automatically indent based on file type: ``filetype indent on`` 88 | " Keep indentation level from previous line: ``set autoindent`` 89 | 90 | " Folding based on indentation: ``set foldmethod=indent`` 91 | -------------------------------------------------------------------------------- /.zprofile: -------------------------------------------------------------------------------- 1 | 2 | if [[ $(uname -m) == 'arm64' ]]; then 3 | # Set PATH, MANPATH, etc., for Homebrew. 4 | eval "$(/opt/homebrew/bin/brew shellenv)" 5 | fi 6 | 7 | # Added by OrbStack: command-line tools and integration 8 | source ~/.orbstack/shell/init.zsh 2>/dev/null || : 9 | -------------------------------------------------------------------------------- /.zshrc.local: -------------------------------------------------------------------------------- 1 | # custom alias and othet local config 2 | -------------------------------------------------------------------------------- /Brewfile: -------------------------------------------------------------------------------- 1 | tap "buo/cask-upgrade" 2 | tap "homebrew/cask" 3 | tap "homebrew/cask-drivers" 4 | tap "homebrew/cask-fonts" 5 | tap "homebrew/core" 6 | tap "homebrew/bundle" 7 | tap "homebrew/services" 8 | tap "homebrew/cask-versions" 9 | 10 | # primitives 11 | brew "axel" 12 | # https://en.wikipedia.org/wiki/List_of_GNU_Core_Utilities_commands 13 | brew "coreutils" 14 | brew "exa" 15 | brew "git" 16 | brew "neovim" 17 | brew "zsh" 18 | brew "zsh-completions" 19 | brew "tmux" 20 | brew "gnupg" 21 | brew "gnu-sed" 22 | # https://github.com/junegunn/fzf 23 | brew "fzf" 24 | # https://github.com/ggreer/the_silver_searcher 25 | brew "the_silver_searcher" 26 | brew "hub" 27 | brew "tmux" 28 | brew "trash" 29 | brew "tree" 30 | brew "wget" 31 | brew "rsync" 32 | 33 | # tools 34 | brew "ipinfo-cli" 35 | brew "youtube-dl" 36 | brew "ffmpeg" 37 | brew "imagemagick" 38 | brew "mas" 39 | brew "mise" 40 | brew "nmap" 41 | brew "p7zip" 42 | brew "dos2unix" 43 | brew "proxychains-ng" 44 | brew "htop" 45 | brew "neofetch" 46 | brew "readline" 47 | brew "cmake" 48 | brew "sqlite" 49 | brew "xz" 50 | brew "zlib" 51 | 52 | # productivity 53 | brew "ripgrep" 54 | brew "fd" 55 | # brew "hexyl" 56 | # brew "xsv" 57 | brew "jq" 58 | # brew "hledger" 59 | 60 | # dev 61 | # brew "ruby" 62 | # brew "go" 63 | # brew "postgresql" 64 | # brew "heroku" 65 | # brew "node" 66 | # brew "yarn" 67 | # brew "wrk" 68 | 69 | if ENV.key? 'CI_MODE' 70 | cask "alfred" 71 | cask "appcleaner" 72 | cask "bartender" 73 | cask "calibre" 74 | cask "charles" 75 | cask "cyberduck" 76 | cask "dash" 77 | cask "dozer" 78 | cask "day-o" 79 | cask "eudic" 80 | cask "font-fira-code" 81 | cask "font-source-code-pro" 82 | cask "google-chrome" 83 | cask "hammerspoon" 84 | cask "iina" 85 | cask "iterm2" 86 | cask "java" 87 | cask "jd-gui" 88 | cask "karabiner-elements" 89 | cask "keycastr" 90 | cask "licecap" 91 | cask "mos" 92 | cask "mpv" 93 | cask "nextcloud" 94 | cask "nitroshare" 95 | cask "visual-studio-code" 96 | cask "syncthing" 97 | cask "neteasemusic" 98 | # open broadcaster software 99 | cask "obs" 100 | # note-taking app 101 | cask "obsidian" 102 | # cask "dropbox" 103 | cask "postman" 104 | # cask "qmk-toolbox" 105 | cask "upic" 106 | cask "via" 107 | cask "vlc" 108 | cask "musicbrainz-picard" 109 | cask "plex" 110 | cask "plexamp" 111 | 112 | # Quick look 113 | cask "qlcolorcode" 114 | cask "qlstephen" 115 | cask "qlmarkdown" 116 | cask "quicklook-json" 117 | cask "qlimagesize" 118 | cask "suspicious-package" 119 | cask "quicklookase" 120 | cask "qlvideo" 121 | 122 | mas "WeChat", id: 836500024 123 | mas "Unsplash Wallpapers", id: 1284863847 124 | mas "Microsoft OneNote", id: 784801555 125 | # mas "Lungo", id: 1263070803 126 | mas "Pages", id: 409201541 127 | mas "Numbers", id: 409203825 128 | mas "Keynote", id: 409183694 129 | end 130 | -------------------------------------------------------------------------------- /Brewfile-essentials: -------------------------------------------------------------------------------- 1 | tap "homebrew/cask" 2 | tap "homebrew/cask-fonts" 3 | tap "github/gh" 4 | tap "koekeishiya/formulae" 5 | tap "narze/taps" 6 | tap "yqrashawn/goku" 7 | 8 | brew "awscli" 9 | brew "bat" 10 | # brew "diff-so-fancy" 11 | brew "ffmpeg" 12 | brew "ghq" 13 | brew "git" 14 | brew "git-delta" 15 | brew "git-flow" 16 | brew "gnu-sed" 17 | brew "jq" 18 | brew "mackup" 19 | brew "magic-wormhole" 20 | brew "mas" 21 | brew "mpc" 22 | brew "mpd" 23 | brew "ncmpcpp" 24 | brew "neofetch" 25 | brew "reattach-to-user-namespace" 26 | brew "terminal-notifier" 27 | brew "tldr" 28 | brew "tmux" 29 | brew "tree" 30 | brew "watch" 31 | brew "zsh" 32 | 33 | brew "github/gh/gh" 34 | brew "koekeishiya/formulae/skhd" 35 | brew "koekeishiya/formulae/yabai" 36 | brew "yqrashawn/goku/goku" 37 | 38 | cask "syncthing" 39 | cask "bitwarden" 40 | cask "raycast" 41 | cask "smartgit" 42 | cask "wechat" 43 | cask "obsidian" 44 | # cask "arq" # Wait for v5 compatability 45 | cask "beardedspice" 46 | cask "bitbar" 47 | cask "dash" 48 | cask "docker" 49 | cask "dropbox" 50 | cask "flux" 51 | cask "font-firacode-nerd-font" 52 | cask "font-fantasque-sans-mono-nerd-font" 53 | cask "google-backup-and-sync" 54 | cask "google-chrome" 55 | cask "hazel" 56 | cask "joplin" 57 | cask "kitty" 58 | cask "karabiner-elements" 59 | cask "keyboard-maestro" 60 | cask "ngrok" 61 | cask "omnifocus" 62 | cask "postgres" 63 | cask "postman" 64 | cask "rescuetime" 65 | cask "slack" 66 | cask "spotify" 67 | cask "startupizer" 68 | cask "telegram" 69 | cask "the-unarchiver" 70 | cask "visual-studio-code" 71 | cask "whatpulse" 72 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: help 2 | 3 | help: ## Print command list 4 | @perl -nle'print $& if m{^[a-zA-Z_-]+:.*?## .*$$}' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' 5 | 6 | _prepare: 7 | @git submodule update --init --recursive 8 | 9 | _bootstrap: 10 | @./install -c config/bootstrap.conf.yml 11 | 12 | bootstrap: _prepare dotfiles _bootstrap ## Bootstrap new machine 13 | 14 | termux: _prepare dotfiles termux ## Bootstrap new termux 15 | 16 | mac: 17 | @./install -c config/macos.conf.yml --plugin-dir dotbot-brew 18 | 19 | termux: 20 | @./install -c config/termux.conf.yml 21 | 22 | dotfiles: ## Update dotfiles 23 | @./install 24 | 25 | linux: 26 | @./install -c config/apt.conf.yml --plugin-dir dotbot-apt-get 27 | 28 | snap: 29 | @./install -c config/snap.conf.yml --plugin-dir dotbot-snap 30 | 31 | macos: ## Run macos script 32 | @./mac_bootstrap.sh 33 | @./macos/init_mac.sh 34 | 35 | crontab: 36 | @./install --plugin-dir crontab-dotbot -c config/crontab.conf.yml 37 | 38 | brew: ## Install brew & cask packages 39 | @./install -c config/packages.conf.yml --plugin-dir dotbot-brew 40 | 41 | tmux: ## Install non-brew tools eg. tmux package manager 42 | @./install -c config/tmux.conf.yml 43 | 44 | asdf: ## Install asdf-vm 45 | @./install -c config/asdf-install.conf.yml --plugin-dir dotbot-brew 46 | @./install -c config/asdf.conf.yml --plugin-dir dotbot-asdf 47 | 48 | update: ## Update everything 49 | @make _prepare 50 | @./install -c config/update.conf.yml 51 | 52 | vim: ## Setup vim 53 | @./install -c config/vim.conf.yml 54 | 55 | all: _prepare dotfiles _bootstrap brew tmux asdf ## Run all tasks at once 56 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is my personal dotfiles which contain config for vim, zsh, tmux, git, fzf etc; 2 | 3 | ## macOS setup 4 | Set up by using [dotbot](https://blog.einverne.info/post/2020/08/use-dotbot-dotfiles-management.html): 5 | 6 | cd ~ 7 | git clone git@github.com:einverne/dotfiles.git 8 | cd dotfiles 9 | # to bootstrap config for vim, zsh, tmux, git, fzf, etc 10 | make bootstrap 11 | # under Linux desktop, install essential packages 12 | make linux 13 | # under macOS, install applications by brew 14 | make mac 15 | ./install -c config/macos.conf.yaml 16 | 17 | Log out and log in again, zinit will install all plugins automatically. If you use vi to edit file at first time, the vim-plug will install all vim plugins automatically. 18 | 19 | ## Termux setup 20 | Install dependency first: 21 | 22 | pkg install make python vim git 23 | 24 | then: 25 | 26 | git clone git@github.com:einverne/dotfiles.git 27 | cd dotfiles 28 | make termux 29 | 30 | ## Overview 31 | 32 | - using [dotbot](https://github.com/anishathalye/dotbot/) to manage dotfiles, [read more](https://blog.einverne.info/post/2020/08/use-dotbot-dotfiles-management.html) 33 | - zsh, using [zinit](https://blog.einverne.info/post/2020/10/use-zinit-to-manage-zsh-plugins.html) as zsh plugin management 34 | - vim, using [vim-plug](https://github.com/junegunn/vim-plug) to manage vim plugins, vim-plug relate configuration is under `vim-plug_vimrc`. In Vim, `:PlugInstall` to install all vim plugins. 35 | - tmux, using [tpm](https://blog.einverne.info/post/2017/12/tmux-plugins.html) to manage tmux plugins, in tmux, press `Ctrl +B` + `I` to install all tmux plugins. 36 | - other useful tools, like [fzf](https://blog.einverne.info/post/2019/08/fzf-usage.html) to fuzzy search, ripgrep for recursively searching directories, zoxide to replace cd, exa to replace ls. 37 | 38 | GUI applications: 39 | 40 | - Kitty 41 | - Karabiner-Elements 42 | - Hammerspoon 43 | 44 | ### zsh config 45 | to see `.zshrc` file. 46 | 47 | ### Vim config 48 | vim-plug related configuration is under `vim-plug_vimrc`, to show all plugins list, use `:PluginList` in vim. 49 | 50 | python related configurations is under `python_vimrc`. 51 | 52 | ## Components 53 | 54 | - bin/: executable shell scripts, Anything in bin/ will get added to your $PATH and be made available everywhere. 55 | - conf/: configuration file of zsh etc 56 | 57 | ## Instruction for vim 58 | 59 | Enter the vim and then run `:PlugInstall` to install all plugins. 60 | 61 | ### install manually 62 | Or, you can do it manually follow the step: 63 | 64 | Enter vim, run `:PlugInstall`, after install all plugin, you will meet an error, 65 | 66 | > Taglist: Exuberant ctags (http://ctags.sf.net) not found in PATH. Plugin is not loaded. 67 | 68 | For Ubuntu and derivatives: 69 | 70 | sudo apt-get install exuberant-ctags 71 | 72 | with yum: 73 | 74 | sudo yum install ctags-etags 75 | 76 | ## Tmux config 77 | I take some Tmux config from [gpakosz](https://github.com/gpakosz/.tmux). If you want to learn more about tmux, you can check [this article](http://einverne.github.io/post/2017/07/tmux-introduction.html). 78 | 79 | You can manually install tmux plugins by `prefix + I`. 80 | 81 | Tmux need: 82 | 83 | - `tmux >= 2.1` 84 | - You should set `$TERM` environment for `xterm-256color` 85 | 86 | Tmux config: 87 | 88 | - You can use `C-b` as prefix, and use `C-a` as second choice 89 | - `prefix + |` to split panel vertically, `prefix + -` split panel horizontally 90 | - `C-hjkl` to switch pane 91 | - `prefix + Shift + HJKL` to adjust pane size 92 | 93 | I use Tmux Plugin Manager to manage tmux plugins, and by default I use following plugins: 94 | 95 | set -g @plugin 'tmux-plugins/tpm' 96 | set -g @plugin 'tmux-plugins/tmux-sensible' 97 | set -g @plugin 'tmux-plugins/tmux-yank' 98 | set -g @plugin 'tmux-plugins/tmux-resurrect' 99 | set -g @plugin 'tmux-plugins/tmux-continuum' 100 | set -g @plugin 'tmux-plugins/tmux-open' 101 | set -g @plugin 'tmux-plugins/tmux-copycat' 102 | 103 | ## fzf config 104 | There are following alias in `.zshrc` : 105 | 106 | - fe : open file using $EDITOR 107 | - fo : open file Ctrl-o using open, Ctrl-e use $EDITOR 108 | - fcd : cd path (fd to replace find) 109 | - fkill : kill process 110 | - tm : tm new tmux session 111 | - fs : tmux attach tmux session 112 | -------------------------------------------------------------------------------- /backup/backup_tencent_cos.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | # pip install -U cos-python-sdk-v5 4 | import os 5 | 6 | import sys 7 | import tarfile 8 | 9 | import subprocess 10 | from qcloud_cos import CosConfig 11 | from qcloud_cos import CosS3Client 12 | 13 | secret_id = '' 14 | secret_key = '' 15 | region = '' 16 | bucket = '' 17 | token = '' 18 | 19 | backup_dir = "/home/mi/Public" # 需要备份的目录 20 | backup_filename = "backup.tar.gz" 21 | temp_dir = "/tmp/backup/" 22 | sql_filename = "backup.sql" 23 | MYSQL_USER = "root" 24 | MYSQY_PASSWORD = "password" 25 | 26 | def put(client, file_path): 27 | if not os.path.exists(file_path): 28 | raise Exception("file not exist") 29 | with open(file_path, 'rb') as f: 30 | r = client.put_object( 31 | Bucket=bucket, 32 | Body=f, 33 | Key=os.path.basename(file_path), 34 | ) 35 | print r 36 | 37 | 38 | def get(client, file_name): 39 | r = client.get_object( 40 | Bucket=bucket, 41 | Key=file_name, 42 | ) 43 | r['Body'].get_stream_to_file(file_name) 44 | # r['Body'].get_raw_stream() 流 45 | 46 | if __name__ == '__main__': 47 | config = CosConfig(Secret_id=secret_id, Secret_key=secret_key, Region=region) 48 | client = CosS3Client(conf=config) 49 | 50 | try: 51 | if not os.path.isdir(temp_dir): 52 | os.mkdir(temp_dir) 53 | except IOError, err: 54 | print err 55 | sys.exit() 56 | 57 | full_temp = os.path.join(temp_dir, backup_filename) 58 | tar = tarfile.open(name=full_temp, mode='w:gz') 59 | for dirpath, dirnames, filenames in os.walk(backup_dir): 60 | for file in filenames: 61 | full_path = os.path.join(dirpath, file) 62 | tar.add(full_path, arcname=file, recursive=False) 63 | 64 | 65 | try: 66 | # 请自行更改mysqldump路径 67 | cmd = '/usr/bin/mysqldump -u' + MYSQL_USER + ' -p' + MYSQY_PASSWORD + ' --all-databases > ' + temp_dir + '/' + sql_filename 68 | h = subprocess.call(cmd) 69 | if h[0] != 0: 70 | print "error mysql backup" 71 | else: 72 | full_path = os.path.join(temp_dir, sql_filename) 73 | tar.add(full_path, arcname=sql_filename, recursive=False) 74 | except IOError, error: 75 | print error 76 | 77 | tar.close() 78 | 79 | put(client, full_temp) 80 | # get(client, '') 81 | os.remove(full_temp) 82 | 83 | -------------------------------------------------------------------------------- /backup/qiniu-backup-folder.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # https://github.com/Ecareyu/backup2qiniu 4 | # 在官网找最新版 https://developer.qiniu.com/kodo/tools/1302/qshell 5 | QSHELL_URL="https://dn-devtools.qbox.me/2.1.5/qshell-linux-x64" 6 | 7 | ## 备份配置信息 ## 8 | 9 | # qshell路径 10 | QSHELL="/usr/local/bin/qshell" 11 | # 备份名称,用于文件名标记 12 | BACKUP_NAME="test" 13 | # 需要备份的目录,多个请空格分隔 14 | BACKUP_SRC="/home/mi/Pictures" 15 | # 备份文件临时存放目录,一般不需要更改 16 | BACKUP_DIR="/tmp/backuptoqiniu" 17 | # 备份文件压缩密码确保压缩包的安全 18 | BACKUP_FILE_PASSWD="" 19 | # 子目录名,为空时获取服务器hostname作为子目录名 20 | SUB_DIR_NAME="" 21 | 22 | ## 备份配置信息 End ## 23 | 24 | ## 七牛配置信息 ## 25 | 26 | # 存放空间对应我们在七牛上创建的容器 27 | QINIU_BUCKET="" 28 | QINIU_ACCESS_KEY="" 29 | QINIU_SECRET_KEY="" 30 | 31 | ## 七牛配置信息 End ## 32 | 33 | # 修复crontab执行时的报错 34 | cd `dirname $0` 35 | 36 | # 设置子目录名 37 | if [ ! -n "$SUB_DIR_NAME" ]; then 38 | SUB_DIR_NAME=`hostname` 39 | fi 40 | 41 | if [ ! -f "$QSHELL" ]; then 42 | echo "qshell not found, install from this link https://github.com/qiniu/qshell" 43 | wget -O qshell $QSHELL_URL 44 | mv qshell /usr/local/bin/ 45 | chmod +x /usr/local/bin/qshell 46 | fi 47 | 48 | # qshell设置用户 49 | $QSHELL account $QINIU_ACCESS_KEY $QINIU_SECRET_KEY 50 | 51 | if [ 0 != $? ]; then 52 | echo "Authorization error" 53 | exit; 54 | fi 55 | 56 | #精确到秒,同一秒内上传的文件会被覆盖 57 | NOW=$(date +"%Y-%m-%d-%H-%M-%S") 58 | 59 | mkdir -p $BACKUP_DIR 60 | 61 | # 打包 62 | echo "start tar" 63 | BACKUP_FILENAME="$BACKUP_NAME-backup-$NOW.zip" 64 | tarCommand="zip -q -r" 65 | 66 | # 判定是否需要密码参数 67 | if [ -n "$BACKUP_FILE_PASSWD" ]; then 68 | tarCommand="$tarCommand -P $BACKUP_FILE_PASSWD" 69 | fi 70 | 71 | # 到目录中压缩,防止压缩包出现过多目录结构 72 | pushd $BACKUP_DIR && $tarCommand $BACKUP_FILENAME *.sql $BACKUP_SRC && popd 73 | echo "tar ok" 74 | 75 | # 上传,默认100条线程,管它呢 76 | echo "start upload" 77 | $QSHELL rput $QINIU_BUCKET $SUB_DIR_NAME/$BACKUP_FILENAME $BACKUP_DIR/$BACKUP_FILENAME 78 | echo "upload ok" 79 | 80 | # 清理备份文件 81 | rm -rf $BACKUP_DIR 82 | echo "backup clean done" 83 | -------------------------------------------------------------------------------- /backup/qiniu-backup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # https://github.com/Ecareyu/backup2qiniu 4 | # 在官网找最新版 https://developer.qiniu.com/kodo/tools/1302/qshell 5 | QSHELL_URL="https://dn-devtools.qbox.me/2.1.5/qshell-linux-x64" 6 | 7 | ## 备份配置信息 ## 8 | 9 | # qshell路径 10 | QSHELL="/usr/local/bin/qshell" 11 | # 备份名称,用于文件名标记 12 | BACKUP_NAME="" 13 | # 需要备份的目录,多个请空格分隔 14 | BACKUP_SRC="" 15 | # Mysql主机地址 16 | MYSQL_SERVER="localhost" 17 | # Mysql用户名 18 | MYSQL_USER="" 19 | # Mysql密码 20 | MYSQL_PASS="" 21 | # Mysql备份数据库,多个请空格分隔 22 | MYSQL_DBS="" 23 | # 备份文件临时存放目录,一般不需要更改 24 | BACKUP_DIR="/tmp/backuptoqiniu" 25 | # 备份文件压缩密码确保压缩包的安全 26 | BACKUP_FILE_PASSWD="" 27 | # 子目录名,为空时获取服务器hostname作为子目录名 28 | SUB_DIR_NAME="" 29 | 30 | ## 备份配置信息 End ## 31 | 32 | ## 七牛配置信息 ## 33 | 34 | # 存放空间对应我们在七牛上创建的容器 35 | QINIU_BUCKET="" 36 | QINIU_ACCESS_KEY="" 37 | QINIU_SECRET_KEY="" 38 | 39 | ## 七牛配置信息 End ## 40 | 41 | # 修复crontab执行时的报错 42 | cd `dirname $0` 43 | 44 | # 设置子目录名 45 | if [ ! -n "$SUB_DIR_NAME" ]; then 46 | SUB_DIR_NAME=`hostname` 47 | fi 48 | 49 | if [ ! -f "$QSHELL" ]; then 50 | echo "qshell not found, install from this link https://github.com/qiniu/qshell" 51 | wget -O qshell $QSHELL_URL 52 | mv qshell /usr/local/bin/ 53 | chmod +x /usr/local/bin/qshell 54 | fi 55 | 56 | # qshell设置用户 57 | $QSHELL account $QINIU_ACCESS_KEY $QINIU_SECRET_KEY 58 | 59 | if [ 0 != $? ]; then 60 | echo "Authorization error" 61 | exit; 62 | fi 63 | 64 | #精确到秒,同一秒内上传的文件会被覆盖 65 | NOW=$(date +"%Y-%m-%d-%H-%M-%S") 66 | 67 | mkdir -p $BACKUP_DIR 68 | 69 | # 备份Mysql 70 | echo "start dump mysql" 71 | for db_name in $MYSQL_DBS 72 | do 73 | mysqldump -u $MYSQL_USER -h $MYSQL_SERVER -p$MYSQL_PASS $db_name > "$BACKUP_DIR/$BACKUP_NAME-$db_name.sql" 74 | done 75 | echo "dump ok" 76 | 77 | # 打包 78 | echo "start tar" 79 | BACKUP_FILENAME="$BACKUP_NAME-backup-$NOW.zip" 80 | tarCommand="zip -q -r" 81 | 82 | # 判定是否需要密码参数 83 | if [ -n "$BACKUP_FILE_PASSWD" ]; then 84 | tarCommand="$tarCommand -P $BACKUP_FILE_PASSWD" 85 | fi 86 | 87 | # 到目录中压缩,防止压缩包出现过多目录结构 88 | pushd $BACKUP_DIR && $tarCommand $BACKUP_FILENAME *.sql $BACKUP_SRC && popd 89 | echo "tar ok" 90 | 91 | # 上传,默认100条线程,管它呢 92 | echo "start upload" 93 | $QSHELL rput $QINIU_BUCKET $SUB_DIR_NAME/$BACKUP_FILENAME $BACKUP_DIR/$BACKUP_FILENAME 94 | echo "upload ok" 95 | 96 | # 清理备份文件 97 | rm -rf $BACKUP_DIR 98 | echo "backup clean done" 99 | -------------------------------------------------------------------------------- /backup/wordpress_backup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 发邮件方式备份网站数据 4 | # 脚本会创建一个压缩包,包含备份的目录和MySQL 数据库备份 5 | # Feel free to use this script wherever you want, however you want. We produce open source, GPLv2 licensed stuff. 6 | # https://theme.fm/a-shell-script-for-a-complete-wordpress-backup/ 7 | 8 | # 设置备份文件名 9 | NOW=$(date +"%Y-%m-%d-%H-%M") 10 | FILE="www.einverne.info.$NOW.tar" 11 | GZ_FILE=$FILE.gz 12 | # 备份文件压缩包存放路径 13 | BACKUP_DIR="/root/backups" 14 | # 需要备份的路径文件夹 15 | WWW_DIR="/var/www/www.einverne.info/html" 16 | # 接受者邮箱 17 | EMAIL_ADDR="username@gmail.com" 18 | 19 | # MySQL 数据库相关配置 20 | DB_USER="" 21 | DB_PASS="" 22 | DB_NAME="" 23 | DB_FILE="www.einverne.info.$NOW.sql" 24 | 25 | # 将Tar压缩包内内容,分别保存到 html 和 database 两个文件夹下 26 | WWW_TRANSFORM='s,^var/www/www.einverne.info/html,html,' 27 | DB_TRANSFORM='s,^root/backups,database,' 28 | 29 | # 以上内容需要自定义 30 | 31 | mkdir -p $BACKUP_DIR 32 | # Create the archive and the MySQL dump 33 | tar -cvf $BACKUP_DIR/$FILE --transform $WWW_TRANSFORM $WWW_DIR 34 | mysqldump -u$DB_USER -p$DB_PASS $DB_NAME > $BACKUP_DIR/$DB_FILE 35 | 36 | # Append the dump to the archive, remove the dump and compress the whole archive. 37 | tar --append --file=$BACKUP_DIR/$FILE --transform $DB_TRANSFORM $BACKUP_DIR/$DB_FILE 38 | rm $BACKUP_DIR/$DB_FILE 39 | gzip -9 $BACKUP_DIR/$FILE 40 | 41 | # Split file and send by email 42 | split -b 5M $BACKUP_DIR/$GZ_FILE $BACKUP_DIR/$GZ_FILE. 43 | rm $BACKUP_DIR/$GZ_FILE 44 | 45 | for filename in $BACKUP_DIR/*; do 46 | echo $filename 47 | echo "backup" | mutt -s "$GZ_FILE" $EMAIL_ADDR -a $filename 48 | done 49 | 50 | rm -rf $BACKUP_DIR 51 | -------------------------------------------------------------------------------- /bin/git-credit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # A very slightly quicker way to credit an author on the latest commit. 4 | # 5 | # $1 - The full name of the author. 6 | # $2 - The email address of the author. 7 | # 8 | # Examples 9 | # 10 | # git credit "Zach Holman" zach@example.com 11 | # 12 | 13 | git commit --amend --author "$1 <$2>" -C HEAD 14 | -------------------------------------------------------------------------------- /bin/git-delete-local-merged: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Delete all local branches that have been merged into HEAD. Stolen from 4 | # our favorite @tekkub: 5 | # 6 | # https://plus.google.com/115587336092124934674/posts/dXsagsvLakJ 7 | 8 | git branch -d `git branch --merged | grep -v '^*' | grep -v 'master' | tr -d '\n'` 9 | -------------------------------------------------------------------------------- /bin/git-edit-new: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Open new, unstaged files in your $EDITOR. 4 | # 5 | # This is nice to have when you run a command line generator which generates a 6 | # file or three in your working directory, and you know you want to immediately 7 | # edit them in your editor next. Why waste time clicking around like some sort 8 | # of plebian when you can just run another command? 9 | 10 | $EDITOR $(git ls-files --others --exclude-standard) 11 | -------------------------------------------------------------------------------- /bin/git-nuke: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Nukes a branch locally and on the origin remote. 4 | # 5 | # $1 - Branch name. 6 | # 7 | # Examples 8 | # 9 | # git nuke add-git-nuke 10 | 11 | git branch -D $1 12 | git push origin :$1 13 | -------------------------------------------------------------------------------- /bin/gitio: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # Usage: gitio URL [CODE] 3 | # 4 | # Turns a github.com URL 5 | # into a git.io URL 6 | # 7 | # Created by @defunkt: 8 | # https://gist.github.com/1209316 9 | # 10 | # Copies the git.io URL to your clipboard. 11 | 12 | url = ARGV[0] 13 | code = ARGV[1] 14 | 15 | if url !~ /^(https?:\/\/)?(gist\.)?github.com/ 16 | abort "* github.com URLs only" 17 | end 18 | 19 | if url !~ /^http/ 20 | url = "https://#{url}" 21 | end 22 | 23 | if code 24 | code = "-F code=#{code}" 25 | end 26 | 27 | output = `curl -i https://git.io -F 'url=#{url}' #{code} 2> /dev/null` 28 | if output =~ /Location: (.+)\n?/ 29 | puts $1 30 | `echo #$1 | pbcopy` 31 | else 32 | puts output 33 | end 34 | -------------------------------------------------------------------------------- /bin/serve: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys, os 3 | import SimpleHTTPServer 4 | args = sys.argv[1:] 5 | 6 | if len(args) and (args[0] == "-h" or args[0] == "--help"): 7 | print """ 8 | Serve a file (or the current directory) 9 | http://benalman.com/ 10 | 11 | Usage: %s [PORT] [FILE] 12 | 13 | If a port isn't specified, use 8080. If a file isn't specified, serve the 14 | current directory. Once started, open the specified file (or the current 15 | directory) with the default web browser. 16 | 17 | Copyright (c) 2012 "Cowboy" Ben Alman 18 | Licensed under the MIT license. 19 | http://benalman.com/about/license/""" % os.path.basename(sys.argv[0]) 20 | sys.exit() 21 | 22 | # Get port, if specified. 23 | port = 8080 24 | if len(args) and args[0].isdigit(): 25 | port = int(args[0]) 26 | args = args[1:] # Shift args. 27 | 28 | # Get file, if specified. 29 | file = args[0] if len(args) else "" 30 | 31 | # If not in an SSH session, open the URL in the default handler. 32 | if not "SSH_TTY" in os.environ: 33 | os.system("open 'http://localhost:%d/%s'" % (port, file)) 34 | 35 | # Redefining the default content-type to text/plain instead of the default 36 | # application/octet-stream allows "unknown" files to be viewable in-browser 37 | # as text instead of being downloaded, which makes me happy. 38 | extensions_map = SimpleHTTPServer.SimpleHTTPRequestHandler.extensions_map 39 | # Set the default content type to text/plain. 40 | extensions_map[""] = "text/plain" 41 | # Serving everything as UTF-8 by default makes funky characters render 42 | # correctly and shouldn't break anything (per Mathias Bynens). 43 | for key, value in extensions_map.items(): 44 | extensions_map[key] = value + "; charset=UTF-8" 45 | 46 | # Start the server using the default .test method, because I'm lazy (the port 47 | # is still grabbed from sys.argv[1]). 48 | sys.argv = [sys.argv[0], port] 49 | SimpleHTTPServer.test() 50 | -------------------------------------------------------------------------------- /bin/yt: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Downloads the embedded video on any web page straight to the desktop. 4 | # 5 | # youtube-dl, which is awesome: 6 | # https://rg3.github.io/youtube-dl/ 7 | # 8 | 9 | cd ~/Videos && youtube-dl "$1" 10 | -------------------------------------------------------------------------------- /config/apt.conf.yml: -------------------------------------------------------------------------------- 1 | - apt: 2 | - git 3 | - curl 4 | - wget 5 | - tree 6 | - zsh 7 | - vim 8 | - tmux 9 | - jdupes 10 | - htop 11 | - zip 12 | - unzip 13 | - iftop 14 | - nethogs 15 | - ripgrep 16 | -------------------------------------------------------------------------------- /config/asdf-install.conf.yml: -------------------------------------------------------------------------------- 1 | # Asdf dependencies 2 | - brew: 3 | - coreutils 4 | - automake 5 | - autoconf 6 | - openssl 7 | - libyaml 8 | - readline 9 | - libxslt 10 | - libtool 11 | - unixodbc 12 | - unzip 13 | - curl 14 | 15 | - shell: 16 | - description: Clone asdf 17 | quiet: true 18 | command: | 19 | if cd ~/.asdf; 20 | then git pull; 21 | else git clone https://github.com/asdf-vm/asdf.git ~/.asdf; 22 | fi 23 | 24 | -------------------------------------------------------------------------------- /config/asdf.conf.yml: -------------------------------------------------------------------------------- 1 | - asdf: 2 | - plugin: python 3 | url: https://github.com/danhper/asdf-python.git 4 | global: 3.7.8 5 | versions: 6 | - 3.7.8 7 | - 3.6.1 8 | - plugin: ruby 9 | url: https://github.com/asdf-vm/asdf-ruby.git 10 | global: 2.6.5 11 | versions: 12 | - 2.6.5 13 | - plugin: neovim 14 | url: https://github.com/richin13/asdf-neovim.git 15 | global: nightly 16 | versions: 17 | - nightly 18 | -------------------------------------------------------------------------------- /config/bootstrap.conf.yml: -------------------------------------------------------------------------------- 1 | # bootstrap linux, macos 2 | - defaults: 3 | link: 4 | relink: true 5 | force: true 6 | 7 | # Setup zinit 8 | - create: 9 | - ~/.zinit 10 | - ~/.tmux 11 | 12 | - shell: 13 | - description: Clone zinit 14 | quiet: true 15 | command: | 16 | if cd ~/.zinit/bin; 17 | then git pull; 18 | else git clone https://github.com/zdharma-continuum/zinit.git ~/.zinit/bin; fi 19 | 20 | # Setup tmux plugin manager 21 | - shell: 22 | - description: Clone TPM 23 | quiet: true 24 | command: | 25 | if cd ~/.tmux/plugins/tpm; 26 | then git pull; 27 | else git clone https://github.com/tmux-plugins/tpm ~/.tmux/plugins/tpm; fi 28 | 29 | # Change default shell to zsh 30 | - shell: 31 | - description: Change default shell to zsh 32 | quiet: true 33 | command: | 34 | update_shell() { 35 | local shell_path; 36 | shell_path="$(command -v zsh)" 37 | 38 | fancy_echo "Changing your shell to zsh ..." 39 | if ! grep "$shell_path" /etc/shells > /dev/null 2>&1 ; then 40 | fancy_echo "Adding '$shell_path' to /etc/shells" 41 | sudo sh -c "echo $shell_path >> /etc/shells" 42 | fi 43 | sudo chsh -s "$shell_path" "$USER" 44 | } 45 | 46 | update_shell 47 | 48 | - create: 49 | - ~/.config/ 50 | 51 | - link: 52 | ~/.config/espanso: 53 | if: '[ `uname` = Linux ]' 54 | path: espanso 55 | -------------------------------------------------------------------------------- /config/crontab.conf.yml: -------------------------------------------------------------------------------- 1 | - crontab: 2 | - cron: "*/1 * * * *" 3 | command: mv ~/Downloads/*.md ~/Sync/wiki/WebClip/ 4 | - cron: "*/1 * * * *" 5 | command: mv ~/Downloads/Attachments/* ~/Sync/wiki/Attachments/ 6 | -------------------------------------------------------------------------------- /config/hammerspoon.conf.yml: -------------------------------------------------------------------------------- 1 | - defaults: 2 | link: 3 | relink: true 4 | force: true 5 | 6 | - brew: 7 | - hammerspoon 8 | 9 | - link: 10 | ~/.hammerspoon: 11 | if: '[ `uname` = Darwin ]' 12 | path: hammerspoon -------------------------------------------------------------------------------- /config/install.conf.yml: -------------------------------------------------------------------------------- 1 | - defaults: 2 | link: 3 | relink: true 4 | force: true 5 | 6 | - clean: [ '~' ] 7 | 8 | - link: 9 | ~/.dotfiles: 10 | force: true 11 | path: '' 12 | 13 | - link: 14 | ~/.zshrc: 15 | path: .zshrc 16 | ~/.zprofile: 17 | path: .zprofile 18 | ~/.p10k.zsh: 19 | path: .p10k.zsh 20 | ~/.gitconfig: 21 | path: git/global.gitconfig 22 | ~/.gitconfig-work: 23 | path: git/work.gitconfig 24 | ~/.gitignore_global: 25 | path: git/gitignore_global 26 | ~/.tmux.conf: 27 | path: tmux/.tmux.conf 28 | ~/.tmux.conf.local: 29 | path: tmux/.tmux.conf.local 30 | ~/.vim: 31 | path: .vim 32 | ~/.vimrc: 33 | path: .vimrc 34 | ~/.ideavimrc: 35 | if: '[ `uname` = Darwin ]' 36 | path: idea/.ideavimrc 37 | ~/.aerospace.toml: 38 | if: '[ `uname` = Darwin ]' 39 | path: aerospace/aerospace.toml 40 | -------------------------------------------------------------------------------- /config/macos.conf.yml: -------------------------------------------------------------------------------- 1 | # bootstrap: macos 2 | - shell: 3 | - description: Install Homebrew 4 | quiet: true 5 | command: | 6 | if ! command -v brew &> /dev/null 7 | then 8 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 9 | fi 10 | 11 | - create: 12 | - ~/.config/kitty 13 | 14 | - link: 15 | ~/Library/Preferences/espanso: 16 | if: '[ `uname` = Darwin ]' 17 | path: espanso 18 | ~/.config/karabiner.edn: 19 | if: '[ `uname` = Darwin ]' 20 | path: karabiner/karabiner.edn 21 | ~/.config/karabiner: 22 | if: '[ `uname` = Darwin ]' 23 | path: karabiner 24 | ~/.hammerspoon: 25 | if: '[ `uname` = Darwin ]' 26 | path: hammerspoon 27 | ~/.mackup.cfg: 28 | if: '[ `uname` = Darwin ]' 29 | path: mackup/mackup.cfg 30 | ~/.Brewfile: 31 | if: '[ `uname` = Darwin ]' 32 | path: Brewfile 33 | ~/.config/kitty/kitty.conf: 34 | if: '[ `uname` = Darwin ]' 35 | path: kitty/kitty.conf 36 | ~/.config/kitty/base16-solarized-dark-256.conf: 37 | if: '[ `uname` = Darwin ]' 38 | path: kitty/base16-solarized-dark-256.conf 39 | ~/.skhdrc: 40 | if: '[ `uname` = Darwin ]' 41 | path: skhd/skhdrc 42 | ~/.yabairc: 43 | if: '[ `uname` = Darwin ]' 44 | path: yabai/yabairc 45 | ~/.yabai_focus_display: 46 | if: '[ `uname` = Darwin ]' 47 | path: yabai/yabai_focus_display 48 | 49 | - brew: 50 | - assh 51 | - coreutils 52 | - curl 53 | - diff-so-fancy 54 | - espanso 55 | - git 56 | - fd 57 | - fzf 58 | - htop 59 | - tmux 60 | - pyenv 61 | - pyenv-virtualenv 62 | - ripgrep 63 | - ffmpeg 64 | - zsh 65 | - yqrashawn/goku/goku 66 | 67 | - tap: 68 | - homebrew/cask-fonts 69 | 70 | - cask: 71 | - anydesk 72 | - appcleaner 73 | - bitwarden 74 | - contexts 75 | - bigwig-club/brew/upic 76 | - font-fira-code 77 | - google-chrome 78 | - hammerspoon 79 | - iina 80 | - istat-menus 81 | - iterm2 82 | - itsycal 83 | - jetbrains-toolbox 84 | - karabiner-elements 85 | - kitty 86 | - keka 87 | - maczip 88 | - menubarx 89 | - mos 90 | - obs 91 | - obsidian 92 | - orbstack 93 | - postman 94 | - raycast 95 | - smartgit 96 | - shottr 97 | - sonixd 98 | - syncthing 99 | - tailscale 100 | - telegram-desktop 101 | - visual-studio-code 102 | - warp 103 | - youtube-music 104 | - zerotier-one 105 | - nikitabobko/tap/aerospace 106 | -------------------------------------------------------------------------------- /config/packages.conf.yml: -------------------------------------------------------------------------------- 1 | - brew: 2 | - mackup 3 | - mas 4 | 5 | - cask: 6 | - syncthing 7 | 8 | - brewfile: 9 | - ~/.Brewfile 10 | -------------------------------------------------------------------------------- /config/projects.conf.yml: -------------------------------------------------------------------------------- 1 | - defaults: 2 | link: 3 | relink: true 4 | 5 | - create: 6 | - ~/Git 7 | - ~/projects 8 | 9 | - shell: 10 | - cd ~/Git && git clone git@github.com:einverne/einverne.github.io.git 11 | -------------------------------------------------------------------------------- /config/snap.conf.yml: -------------------------------------------------------------------------------- 1 | - snap: 2 | - bitwarden 3 | - espanso 4 | - obsidian 5 | - postman 6 | - redis-desktop-manager 7 | -------------------------------------------------------------------------------- /config/termux.conf.yml: -------------------------------------------------------------------------------- 1 | - shell: 2 | - description: backup .termux 3 | command: | 4 | mv "$HOME/.termux" "$HOME/.termux.bak.$(date +%Y.%m.%d-%H:%M:%S)" 5 | - description: install tools 6 | command: | 7 | pkg install -y termux-tools libcurl wget curl openssh vim git zsh unrar unzip less tree htop tsu neofetch fzf ncurses-utils 8 | 9 | - link: 10 | ~/.termux: 11 | path: termux/.termux 12 | 13 | - shell: 14 | - description: Clone zinit 15 | quiet: true 16 | command: | 17 | if cd ~/.zinit/bin; 18 | then git pull; 19 | else git clone https://github.com/zdharma-continuum/zinit.git ~/.zinit/bin; fi 20 | - chsh -s zsh 21 | - termux-reload-settings 22 | - echo "Done!" 23 | -------------------------------------------------------------------------------- /config/tmux.conf.yml: -------------------------------------------------------------------------------- 1 | - brew: 2 | - tmux 3 | 4 | # Setup tpm : tmux plugin manager 5 | - create: 6 | - ~/.tmux/plugins 7 | 8 | - shell: 9 | - description: Clone tpm 10 | quiet: true 11 | command: | 12 | if cd ~/.tmux/plugins/tpm; 13 | then git pull; 14 | else git clone https://github.com/tmux-plugins/tpm.git ~/.tmux/plugins/tpm; 15 | fi 16 | -------------------------------------------------------------------------------- /config/update.conf.yml: -------------------------------------------------------------------------------- 1 | - shell: 2 | - description: Zinit self update 3 | command: zsh -i -c "zinit self-update" 4 | - description: Zinit update 5 | command: zsh -i -c "zinit update" 6 | 7 | - description: Brew update 8 | command: brew update 9 | - description: Cask update 10 | command: brew cu -acy 11 | -------------------------------------------------------------------------------- /espanso/config/default.yml: -------------------------------------------------------------------------------- 1 | # espanso configuration file 2 | 3 | # For a complete introduction, visit the official docs at: https://espanso.org/docs/ 4 | 5 | # You can use this file to define the global configuration options for espanso. 6 | # These are the parameters that will be used by default on every application, 7 | # but you can also override them on a per-application basis. 8 | 9 | # To make customization easier, this file contains some of the commonly used 10 | # parameters. Feel free to uncomment and tune them to fit your needs! 11 | 12 | # --- Toggle key 13 | 14 | # Customize the key used to disable and enable espanso (when double tapped) 15 | # Available options: CTRL, SHIFT, ALT, CMD, OFF 16 | # You can also specify the key variant, such as LEFT_CTRL, RIGHT_SHIFT, etc... 17 | # toggle_key: ALT 18 | # You can also disable the toggle key completely with 19 | # toggle_key: OFF 20 | 21 | # --- Injection Backend 22 | 23 | # Espanso supports multiple ways of injecting text into applications. Each of 24 | # them has its quirks, therefore you may want to change it if you are having problems. 25 | # By default, espanso uses the "Auto" backend which should work well in most cases, 26 | # but you may want to try the "Clipboard" or "Inject" backend in case of issues. 27 | # backend: Clipboard 28 | 29 | # --- Auto-restart 30 | 31 | # Enable/disable the config auto-reload after a file change is detected. 32 | # auto_restart: false 33 | 34 | # --- Clipboard threshold 35 | 36 | # Because injecting long texts char-by-char is a slow operation, espanso automatically 37 | # uses the clipboard if the text is longer than 'clipboard_threshold' characters. 38 | # clipboard_threshold: 100 39 | 40 | # For a list of all the available options, visit the official docs at: https://espanso.org/docs/ -------------------------------------------------------------------------------- /espanso/default.yml: -------------------------------------------------------------------------------- 1 | # espanso configuration file 2 | 3 | # This is the default configuration file, change it as you like it 4 | # You can refer to the official documentation: 5 | # https://espanso.org/docs/ 6 | 7 | # Matches are the substitution rules, when you type the "trigger" string 8 | # it gets replaced by the "replace" string. 9 | matches: 10 | # Simple text replacement 11 | - trigger: ":espanso" 12 | replace: "Hi there!" 13 | - trigger: ":blog" 14 | replace: "https://blog.einverne.info" 15 | - trigger: ":photo" 16 | replace: "https://photo.einverne.info" 17 | - trigger: ":homer" 18 | replace: "https://homer.einverne.info" 19 | - trigger: ":ev" 20 | replace: "einverne" 21 | - trigger: ":name" 22 | replace: "einverne" 23 | word: true 24 | - trigger: ":div" 25 | replace: "
$|$
" 26 | - trigger: ":pve" 27 | replace: "Proxmox VE" 28 | 29 | # Dates 30 | - trigger: ":date" 31 | replace: "{{mydate}}" 32 | vars: 33 | - name: mydate 34 | type: date 35 | params: 36 | format: "%m/%d/%Y" 37 | - trigger: ":now" 38 | replace: "{{mytime}}" 39 | vars: 40 | - name: mytime 41 | type: date 42 | params: 43 | format: "%H:%M" 44 | 45 | # Shell commands 46 | - trigger: ":ip" 47 | replace: "{{output}}" 48 | vars: 49 | - name: output 50 | type: shell 51 | params: 52 | cmd: "curl 'ip.gs'" 53 | -------------------------------------------------------------------------------- /espanso/match/base.yml: -------------------------------------------------------------------------------- 1 | # espanso match file 2 | 3 | # For a complete introduction, visit the official docs at: https://espanso.org/docs/ 4 | 5 | # You can use this file to define the base matches (aka snippets) 6 | # that will be available in every application when using espanso. 7 | 8 | # Matches are substitution rules: when you type the "trigger" string 9 | # it gets replaced by the "replace" string. 10 | matches: 11 | # Simple text replacement 12 | - trigger: ":espanso" 13 | replace: "Hi there!" 14 | 15 | # NOTE: espanso uses YAML to define matches, so pay attention to the indentation! 16 | 17 | # But matches can also be dynamic: 18 | 19 | # Print the current date 20 | - trigger: ":date" 21 | replace: "{{mydate}}" 22 | vars: 23 | - name: mydate 24 | type: date 25 | params: 26 | format: "%m/%d/%Y" 27 | 28 | # Print the output of a shell command 29 | - trigger: ":shell" 30 | replace: "{{output}}" 31 | vars: 32 | - name: output 33 | type: shell 34 | params: 35 | cmd: "echo 'Hello from your shell'" 36 | - trigger: ":hotfix" 37 | replace: "{{output}}" 38 | vars: 39 | - name: output 40 | type: shell 41 | params: 42 | cmd: "echo 'Hello from your shell'" 43 | # And much more! For more information, visit the docs: https://espanso.org/docs/ 44 | -------------------------------------------------------------------------------- /git/gitignore_global: -------------------------------------------------------------------------------- 1 | # Compiled source # 2 | ################### 3 | *.com 4 | *.class 5 | *.dll 6 | *.exe 7 | *.o 8 | *.so 9 | 10 | # Packages # 11 | ############ 12 | # it's better to unpack these files and commit the raw source 13 | # git has its own built in compression methods 14 | *.7z 15 | *.dmg 16 | *.gz 17 | *.iso 18 | *.jar 19 | *.rar 20 | *.tar 21 | *.zip 22 | 23 | # Logs and databases # 24 | ###################### 25 | *.log 26 | /*.sql 27 | *.sqlite 28 | 29 | # OS generated files # 30 | ###################### 31 | .DS_Store 32 | .DS_Store? 33 | ._* 34 | .Spotlight-V100 35 | .Trashes 36 | ehthumbs.db 37 | Thumbs.db 38 | 39 | # Redis snapshot 40 | dump.rdb 41 | 42 | .vscode 43 | .elixir_ls 44 | 45 | .githooks 46 | .local 47 | .history 48 | 49 | *.gitignored.* 50 | -------------------------------------------------------------------------------- /git/global.gitconfig: -------------------------------------------------------------------------------- 1 | [user] 2 | email = git@einverne.info 3 | name = Ein Verne 4 | signingkey = 926634D64ACAD792 5 | [push] 6 | default = matching 7 | #[http] 8 | #postBuffer = 524288000 9 | [alias] 10 | unstage = reset HEAD -- 11 | a = add 12 | b = branch 13 | c = commit 14 | d = diff 15 | f = fetch 16 | g = grep 17 | l = log 18 | m = merge 19 | o = checkout 20 | p = pull 21 | r = remote 22 | s = status 23 | st = status 24 | w = whatchanged 25 | 26 | ### commit ### 27 | ca = commit --amend 28 | 29 | ### checkout ### 30 | co = checkout 31 | 32 | ### cherry-pick ### 33 | 34 | ### diff ### 35 | # diff - show changes not yet staged 36 | dc = diff --cache 37 | 38 | # diff - changes about to be commited 39 | ds = diff --staged 40 | 41 | ### log ### 42 | # log key - our favorite way to show our key performance indicators, i.e. our most useful summary. 43 | lk = log --graph --topo-order --abbrev-commit --date=short --decorate --all --boundary --pretty=format:'%Cgreen%ad %Cred%h%Creset -%C(yellow)%d%Creset %s %Cblue[%cn]%Creset %Cblue%G?%Creset' 44 | 45 | ### merge ### 46 | 47 | ### pull ### 48 | # pull if a merge can be resolved as a fast-forward, otherwise fail. 49 | pf = pull --ff-only 50 | 51 | # pull with rebase - to provide a cleaner, linear, bisectable history. 52 | # 53 | # To integrate changes between branches, you can merge or rebase. 54 | # 55 | # When we use "git pull", git does a fetch then a merge. 56 | # If we've made changes locally and someone else has pushed changes 57 | # to our git host then git will automatically merge these together 58 | # and create a merge commit that looks like this in the history: 59 | # 60 | # 12345678 - Merge branch 'foo' of bar into master 61 | # 62 | # When we use "git pull --rebase", git does a fetch then a rebase. 63 | # A rebase resets the HEAD of your local branch to be the same as 64 | # the remote HEAD, then replays your local commits back into repo. 65 | # This means you don't get any noisy merge messages in your history. 66 | # This gives us a linear history, and also helps with git bisect. 67 | # 68 | # To automatically do "pull --rebase" for any branch based on master: 69 | # 70 | # git config branch.master.rebase true 71 | # 72 | # To automatically do "pull --rebase" for all branches: 73 | # 74 | # git config --global branch.autosetuprebase always 75 | # 76 | # pr = pull --rebase 77 | pr="!f() { \ 78 | BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD); \ 79 | git push -u origin $BRANCH_NAME; \ 80 | hub pull-request; \ 81 | };f " 82 | 83 | ### rebase ### 84 | 85 | # rebase - forward-port local commits to the updated upstream head. 86 | rb = rebase 87 | 88 | # rebase - continue the rebasing process after resolving a conflict manually and updating the index with the resolution. 89 | rbc = rebase --continue 90 | 91 | # rebase - restart the rebasing process by skipping the current patch. 92 | rbs = rebase --skip 93 | 94 | ### remote ### 95 | 96 | 97 | ### tags ### 98 | lasttag = describe --tags --abbrev=0 99 | tags = tag -n1 --list 100 | 101 | # Stash aliases 102 | save = stash save 103 | pop = stash pop 104 | [commit] 105 | gpgSign = false 106 | 107 | [branch] 108 | autosetuprebase = always 109 | [core] 110 | editor = vim 111 | autocrlf = input 112 | quotepath = false 113 | [pull] 114 | rebase = true 115 | 116 | [merge] 117 | tool = meld 118 | conflictstyle = diff3 119 | [mergetool "meld"] 120 | cmd = meld $LOCAL $BASE $REMOTE --output=$MERGED --auto-merge 121 | [gpg] 122 | program = gpg 123 | [hub] 124 | protocol = ssh 125 | 126 | [includeIf "gitdir:~/projects/"] 127 | path = ~/projects/.gitconfig 128 | 129 | [includeIf "gitdir:~/smart/"] 130 | path = ~/smart/.gitconfig 131 | 132 | [filter "lfs"] 133 | clean = git-lfs clean -- %f 134 | smudge = git-lfs smudge -- %f 135 | process = git-lfs filter-process 136 | required = true 137 | [init] 138 | defaultBranch = master 139 | [log] 140 | date = local 141 | [http] 142 | postBuffer = 524288000 143 | -------------------------------------------------------------------------------- /git/work.gitconfig: -------------------------------------------------------------------------------- 1 | [user] 2 | name = Ein Verne 3 | email = git@einverne.info 4 | -------------------------------------------------------------------------------- /hammerspoon/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | private 3 | -------------------------------------------------------------------------------- /hammerspoon/README.md: -------------------------------------------------------------------------------- 1 | # hammerspoon configuration 2 | 3 | hammerspoon is my configuration for [Hammerspoon](http://www.hammerspoon.org/). It has highly modal-based, vim-style keybindings, provides some functionality like desktop widgets, window management, application launcher, instant search, ... etc. 4 | 5 | ## Get started 6 | 7 | - Install [Hammerspoon](http://www.hammerspoon.org/) first `brew install --cask hammerspoon` 8 | - `git clone https://github.com/einverne/dotfiles.git ~/dotfiles` 9 | - `ln -s ~/dotfiles/hammerspoon ~/.hammerspoon` 10 | - Reload the configuration. 11 | 12 | ## Keep update 13 | 14 | `cd ~/dotfiles && git pull` 15 | 16 | ## File structure 17 | 18 | - `autoscript.lua`, auto commit and push my personal notes. 19 | - `ime.lua`, auto switch different Input methods in different applications 20 | 21 | ## How to use 22 | Use Karabiner-Elements to set caps lock as hyper key. Press caps lock is just like press Cmd+Control+Option+Shift at the same time. 23 | 24 | ## Reload config 25 | 26 | - Hyper key + R, reload hammerspoon config 27 | 28 | ## Hyper key windows management 29 | 30 | - Hyper key + H, set windows to left half of screen 31 | - Hyper key + L, right half of screen 32 | - Hyper key + J, bottom half 33 | - Hyper key + K, top half 34 | - Hyper key + F, full screen 35 | 36 | ### Windows management mode 37 | 38 | Option+r Enter windows management: 39 | 40 | - ASDW to move windows position 41 | - HL/JK to set windows to left, right, up, down half of screen 42 | - Y/O/U/I to set windows to LeftUp, RightUp, LeftDown, LeftDown corner 43 | - Left/Right/Up/Down same as HL/JK 44 | - F to set windows to full screen 45 | - C to set windows to center 46 | - Esc/Q to exit 47 | - Tab to show help 48 | 49 | ### Switch windows 50 | I personally use the application called [Context](https://contexts.co/) to switch between different windows, however this config provide another way to quickly switch between windows. Try with `Option+Tab`. 51 | 52 | ## Toggle hammerspoon console 53 | 54 | `Option+z` 55 | 56 | ## Move windows between monitors 57 | If you have multiple monitors, you can use the following shortcut to move window to different monitor: 58 | 59 | - Hyper key + N, to move current window to next monitor 60 | - Hpper key + P, to move current window to previous monitor 61 | 62 | ### Application launcher 63 | 64 | Press `option + a` to enter application launcher. The shorcut information will show on the center of the screen. But I personally prefer [Alfred](https://www.alfredapp.com/). 65 | 66 | ## Hammerspoon API manual 67 | 68 | `Option+h` open Hammerspoon API manual. 69 | 70 | ## Lock screen 71 | `Option+l` to lock screen. 72 | 73 | ## Show time in the middle of screen 74 | 75 | `Option+t` to toggle the time in screen. 76 | 77 | ## Auto type url in markdown format 78 | 79 | `Option+v` to auto type url in markdown format. 80 | 81 | ## Toggle Hammerspoon console 82 | 83 | `Option+z` to toggle Hammerspoon console. 84 | 85 | ### Screenshots 86 | 87 | These screenshots demostrate what awesome-hammerspoon is capable of. Learn more about [built-in Spoons](https://github.com/ashfinal/awesome-hammerspoon/wiki/The-built-in-Spoons). 88 | 89 | ## Customization 90 | 91 | 92 | ```shell 93 | cp ~/.hammerspoon/config-example.lua ~/.hammerspoon/private/config.lua 94 | ``` 95 | 96 | Then modify the file `~/.hammerspoon/private/config.lua`: 97 | 98 | - Add/remove Spoons. 99 | 100 | Define `hspoon_list` to decide which Spoons (a distributing format of Hammerspoon module) to be loaded. There are 15 built-in Spoons, learn about them at [here](https://github.com/ashfinal/awesome-hammerspoon/wiki/The-built-in-Spoons). 101 | 102 | *There are more Spoons at [official spoon repository](http://www.hammerspoon.org/Spoons/) (you may need a little config before using them).* 103 | 104 | - Customize keybindings 105 | 106 | Please read `~/.hammerspoon/private/config.lua`for more details. 107 | 108 | 109 | ## Reference 110 | Some resources you may find helpful: 111 | 112 | - [Learn Lua in Y minutes](http://learnxinyminutes.com/docs/lua/) 113 | 114 | - [Getting Started with Hammerspoon](http://www.hammerspoon.org/go/) 115 | 116 | - [Hammerspoon API Docs](http://www.hammerspoon.org/docs/index.html) 117 | 118 | - [hammerspoon/SPOONS.md at master · Hammerspoon/hammerspoon](https://github.com/Hammerspoon/hammerspoon/blob/master/SPOONS.md) 119 | 120 | 121 | ## Thanks to 122 | 123 | - [ashfinal](https://github.com/ashfinal/awesome-hammerspoon/) 124 | 125 | - [https://github.com/zzamboni/oh-my-hammerspoon](https://github.com/zzamboni/oh-my-hammerspoon) 126 | 127 | - [https://github.com/scottcs/dot_hammerspoon](https://github.com/scottcs/dot_hammerspoon) 128 | 129 | - [https://github.com/dharmapoudel/hammerspoon-config](https://github.com/dharmapoudel/hammerspoon-config) 130 | 131 | - [http://tracesof.net/uebersicht/](http://tracesof.net/uebersicht/) 132 | 133 | -------------------------------------------------------------------------------- /hammerspoon/Spoons/AClock.spoon/docs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Constant" : [ 4 | 5 | ], 6 | "submodules" : [ 7 | 8 | ], 9 | "Function" : [ 10 | 11 | ], 12 | "Variable" : [ 13 | 14 | ], 15 | "stripped_doc" : [ 16 | 17 | ], 18 | "Deprecated" : [ 19 | 20 | ], 21 | "type" : "Module", 22 | "desc" : "Just another clock, floating above all", 23 | "Constructor" : [ 24 | 25 | ], 26 | "doc" : "Just another clock, floating above all\n\nDownload: [https:\/\/github.com\/Hammerspoon\/Spoons\/raw\/master\/Spoons\/AClock.spoon.zip](https:\/\/github.com\/Hammerspoon\/Spoons\/raw\/master\/Spoons\/AClock.spoon.zip)", 27 | "Field" : [ 28 | 29 | ], 30 | "items" : [ 31 | { 32 | "doc" : "Show AClock, if already showing, just hide it.", 33 | "stripped_doc" : [ 34 | "Show AClock, if already showing, just hide it." 35 | ], 36 | "def" : "AClock:toggleShow()", 37 | "parameters" : [ 38 | 39 | ], 40 | "notes" : [ 41 | 42 | ], 43 | "signature" : "AClock:toggleShow()", 44 | "type" : "Method", 45 | "returns" : [ 46 | 47 | ], 48 | "name" : "toggleShow", 49 | "desc" : "Show AClock, if already showing, just hide it." 50 | } 51 | ], 52 | "Method" : [ 53 | { 54 | "doc" : "Show AClock, if already showing, just hide it.", 55 | "stripped_doc" : [ 56 | "Show AClock, if already showing, just hide it." 57 | ], 58 | "def" : "AClock:toggleShow()", 59 | "parameters" : [ 60 | 61 | ], 62 | "notes" : [ 63 | 64 | ], 65 | "signature" : "AClock:toggleShow()", 66 | "type" : "Method", 67 | "returns" : [ 68 | 69 | ], 70 | "name" : "toggleShow", 71 | "desc" : "Show AClock, if already showing, just hide it." 72 | } 73 | ], 74 | "Command" : [ 75 | 76 | ], 77 | "name" : "AClock" 78 | } 79 | ] -------------------------------------------------------------------------------- /hammerspoon/Spoons/AClock.spoon/init.lua: -------------------------------------------------------------------------------- 1 | --- === AClock === 2 | --- 3 | --- Just another clock, floating above all 4 | --- 5 | --- Download: [https://github.com/Hammerspoon/Spoons/raw/master/Spoons/AClock.spoon.zip](https://github.com/Hammerspoon/Spoons/raw/master/Spoons/AClock.spoon.zip) 6 | 7 | local obj={} 8 | obj.__index = obj 9 | 10 | -- Metadata 11 | obj.name = "AClock" 12 | obj.version = "1.0" 13 | obj.author = "ashfinal " 14 | obj.homepage = "https://github.com/Hammerspoon/Spoons" 15 | obj.license = "MIT - https://opensource.org/licenses/MIT" 16 | 17 | function obj:init() 18 | self.canvas = hs.canvas.new({x=0, y=0, w=0, h=0}):show() 19 | self.canvas[1] = { 20 | type = "text", 21 | text = "", 22 | textFont = "Impact", 23 | textSize = 130, 24 | textColor = {hex="#1891C3"}, 25 | textAlignment = "center", 26 | } 27 | end 28 | 29 | --- AClock:toggleShow() 30 | --- Method 31 | --- Show AClock, if already showing, just hide it. 32 | --- 33 | 34 | function obj:toggleShow() 35 | if self.timer then 36 | self.timer:stop() 37 | self.timer = nil 38 | self.canvas:hide() 39 | else 40 | local mainScreen = hs.screen.mainScreen() 41 | local mainRes = mainScreen:fullFrame() 42 | self.canvas:frame({ 43 | x = (mainRes.w-300)/2, 44 | y = (mainRes.h-230)/2, 45 | w = 300, 46 | h = 230 47 | }) 48 | self.canvas[1].text = os.date("%H:%M") 49 | self.canvas:show() 50 | self.timer = hs.timer.doAfter(4, function() 51 | self.canvas:hide() 52 | self.timer = nil 53 | end) 54 | end 55 | end 56 | 57 | return obj 58 | -------------------------------------------------------------------------------- /hammerspoon/Spoons/BingDaily.spoon/docs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Constant" : [ 4 | 5 | ], 6 | "submodules" : [ 7 | 8 | ], 9 | "Function" : [ 10 | 11 | ], 12 | "Variable" : [ 13 | 14 | ], 15 | "stripped_doc" : [ 16 | 17 | ], 18 | "desc" : "Use Bing daily picture as your wallpaper, automatically.", 19 | "type" : "Module", 20 | "Deprecated" : [ 21 | 22 | ], 23 | "Constructor" : [ 24 | 25 | ], 26 | "doc" : "Use Bing daily picture as your wallpaper, automatically.\n\nDownload: [https:\/\/github.com\/Hammerspoon\/Spoons\/raw\/master\/Spoons\/BingDaily.spoon.zip](https:\/\/github.com\/Hammerspoon\/Spoons\/raw\/master\/Spoons\/BingDaily.spoon.zip)", 27 | "items" : [ 28 | 29 | ], 30 | "Command" : [ 31 | 32 | ], 33 | "Method" : [ 34 | 35 | ], 36 | "Field" : [ 37 | 38 | ], 39 | "name" : "BingDaily" 40 | } 41 | ] -------------------------------------------------------------------------------- /hammerspoon/Spoons/BingDaily.spoon/init.lua: -------------------------------------------------------------------------------- 1 | --- === BingDaily === 2 | --- 3 | --- Use Bing daily picture as your wallpaper, automatically. 4 | --- 5 | --- Download: [https://github.com/Hammerspoon/Spoons/raw/master/Spoons/BingDaily.spoon.zip](https://github.com/Hammerspoon/Spoons/raw/master/Spoons/BingDaily.spoon.zip) 6 | local log = hs.logger.new('BingDaily', 'debug') 7 | 8 | local obj={} 9 | obj.__index = obj 10 | 11 | -- Metadata 12 | obj.name = "BingDaily" 13 | obj.version = "1.0" 14 | obj.author = "ashfinal " 15 | obj.homepage = "https://github.com/Hammerspoon/Spoons" 16 | obj.license = "MIT - https://opensource.org/licenses/MIT" 17 | obj.bing_path = os.getenv("HOME") .. "/Pictures/Bing/" 18 | 19 | local function curl_callback(exitCode, stdOut, stdErr) 20 | if exitCode == 0 then 21 | obj.task = nil 22 | local allScreens = hs.screen.allScreens() 23 | for i = 1, #allScreens do 24 | local newScreen = allScreens[i]:desktopImageURL("file://" .. obj.localpath) 25 | log.i(newScreen:desktopImageURL()) 26 | end 27 | --hs.screen.mainScreen():desktopImageURL("file://" .. obj.localpath) 28 | else 29 | print(stdOut, stdErr) 30 | end 31 | end 32 | 33 | local function writeDescToFile(filename, content) 34 | f = io.open(obj.bing_path .. "/" .. filename .. ".txt", "w") 35 | io.output(f) 36 | io.write(content) 37 | io.close(f) 38 | end 39 | 40 | local function bingRequest() 41 | local user_agent_str = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/603.2.4 (KHTML, like Gecko) Version/10.1.1 Safari/603.2.4" 42 | local json_req_url = "https://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1" 43 | hs.http.asyncGet(json_req_url, {["User-Agent"]=user_agent_str}, function(stat,body,header) 44 | if stat == 200 then 45 | if pcall(function() hs.json.decode(body) end) then 46 | local decode_data = hs.json.decode(body) 47 | local pic_url = decode_data.images[1].url 48 | -- local pic_name = hs.http.urlParts(pic_url).lastPathComponent 49 | local pic_content_name = decode_data.images[1].copyright 50 | local fullstartdate = decode_data.images[1].fullstartdate 51 | local pic_name = fullstartdate .. ".jpg" 52 | writeDescToFile(fullstartdate, pic_content_name) 53 | local pic_name = pic_name:gsub("/", "_") 54 | local pic_name = pic_name:gsub(" ", "_") 55 | local localpath = obj.bing_path .. pic_name 56 | if obj.localpath ~= localpath then 57 | obj.full_url = "https://www.bing.com" .. pic_url 58 | if obj.task then 59 | obj.task:terminate() 60 | obj.task = nil 61 | end 62 | obj.localpath = localpath 63 | obj.task = hs.task.new("/usr/bin/curl", curl_callback, {"-A", user_agent_str, obj.full_url, "-o", localpath}) 64 | obj.task:start() 65 | end 66 | end 67 | else 68 | log.i("Bing URL request failed!") 69 | end 70 | end) 71 | end 72 | 73 | function create_dir(path) 74 | if hs.fs.displayName(path) == nil then 75 | local result = hs.fs.mkdir(path) 76 | if result == true then 77 | log.i("path: " .. path .. " create successfully!") 78 | else 79 | log.i("path: " .. path .. " create failed!") 80 | end 81 | end 82 | end 83 | 84 | function obj:init() 85 | create_dir(obj.bing_path) 86 | if obj.timer == nil then 87 | obj.timer = hs.timer.doEvery(7200, function() bingRequest() end) 88 | obj.timer:setNextTrigger(5) 89 | else 90 | obj.timer:start() 91 | end 92 | end 93 | 94 | return obj 95 | -------------------------------------------------------------------------------- /hammerspoon/Spoons/Caffeine.spoon/caffeine-off.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/hammerspoon/Spoons/Caffeine.spoon/caffeine-off.pdf -------------------------------------------------------------------------------- /hammerspoon/Spoons/Caffeine.spoon/caffeine-on.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/hammerspoon/Spoons/Caffeine.spoon/caffeine-on.pdf -------------------------------------------------------------------------------- /hammerspoon/Spoons/Caffeine.spoon/init.lua: -------------------------------------------------------------------------------- 1 | --- === Caffeine === 2 | --- 3 | --- Prevent the screen from going to sleep 4 | --- Download: [https://github.com/Hammerspoon/Spoons/raw/master/Spoons/Caffeine.spoon.zip](https://github.com/Hammerspoon/Spoons/raw/master/Spoons/Caffeine.spoon.zip) 5 | local obj = { __gc = true } 6 | --obj.__index = obj 7 | setmetatable(obj, obj) 8 | obj.__gc = function(t) 9 | t:stop() 10 | end 11 | 12 | -- Metadata 13 | obj.name = "Caffeine" 14 | obj.version = "1.0" 15 | obj.author = "Chris Jones " 16 | obj.homepage = "https://github.com/Hammerspoon/Spoons" 17 | obj.license = "MIT - https://opensource.org/licenses/MIT" 18 | 19 | obj.menuBarItem = nil 20 | obj.hotkeyToggle = nil 21 | 22 | -- Internal function used to find our location, so we know where to load files from 23 | local function script_path() 24 | local str = debug.getinfo(2, "S").source:sub(2) 25 | return str:match("(.*/)") 26 | end 27 | obj.spoonPath = script_path() 28 | 29 | function obj:init() 30 | end 31 | 32 | --- Caffeine:bindHotkeys(mapping) 33 | --- Method 34 | --- Binds hotkeys for Caffeine 35 | --- 36 | --- Parameters: 37 | --- * mapping - A table containing hotkey modifier/key details for the following items: 38 | --- * toggle - This will toggle the state of display sleep prevention, and update the menubar graphic 39 | --- 40 | --- Returns: 41 | --- * The Caffeine object 42 | function obj:bindHotkeys(mapping) 43 | if (self.hotkeyToggle) then 44 | self.hotkeyToggle:delete() 45 | end 46 | local toggleMods = mapping["toggle"][1] 47 | local toggleKey = mapping["toggle"][2] 48 | self.hotkeyToggle = hs.hotkey.new(toggleMods, toggleKey, function() self.clicked() end) 49 | 50 | return self 51 | end 52 | 53 | --- Caffeine:start() 54 | --- Method 55 | --- Starts Caffeine 56 | --- 57 | --- Parameters: 58 | --- * None 59 | --- 60 | --- Returns: 61 | --- * The Caffeine object 62 | function obj:start() 63 | if self.menuBarItem then self:stop() end 64 | self.menuBarItem = hs.menubar.new() 65 | self.menuBarItem:setClickCallback(self.clicked) 66 | if (self.hotkeyToggle) then 67 | self.hotkeyToggle:enable() 68 | end 69 | self.setDisplay(hs.caffeinate.get("displayIdle")) 70 | 71 | return self 72 | end 73 | 74 | --- Caffeine:stop() 75 | --- Method 76 | --- Stops Caffeine 77 | --- 78 | --- Parameters: 79 | --- * None 80 | --- 81 | --- Returns: 82 | --- * The Caffeine object 83 | function obj:stop() 84 | if self.menuBarItem then self.menuBarItem:delete() end 85 | if (self.hotkeyToggle) then 86 | self.hotkeyToggle:disable() 87 | end 88 | self.menuBarItem = nil 89 | return self 90 | end 91 | 92 | function obj.setDisplay(state) 93 | local result 94 | if state then 95 | result = obj.menuBarItem:setIcon(obj.spoonPath.."/caffeine-on.pdf") 96 | else 97 | result = obj.menuBarItem:setIcon(obj.spoonPath.."/caffeine-off.pdf") 98 | end 99 | end 100 | 101 | function obj.clicked() 102 | obj.setDisplay(hs.caffeinate.toggle("displayIdle")) 103 | end 104 | 105 | --- Caffeine:setState(on) 106 | --- Method 107 | --- Sets whether or not caffeination should be enabled 108 | --- 109 | --- Parameters: 110 | --- * on - A boolean, true if screens should be kept awake, false to let macOS send them to sleep 111 | --- 112 | --- Returns: 113 | --- * None 114 | function obj:setState(on) 115 | hs.caffeinate.set("displayIdle", on) 116 | obj.setDisplay(on) 117 | end 118 | 119 | return obj 120 | -------------------------------------------------------------------------------- /hammerspoon/Spoons/Calendar.spoon/docs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Constant" : [ 4 | 5 | ], 6 | "submodules" : [ 7 | 8 | ], 9 | "Function" : [ 10 | 11 | ], 12 | "Variable" : [ 13 | 14 | ], 15 | "stripped_doc" : [ 16 | 17 | ], 18 | "Deprecated" : [ 19 | 20 | ], 21 | "type" : "Module", 22 | "desc" : "A calendar inset into the desktop", 23 | "Constructor" : [ 24 | 25 | ], 26 | "doc" : "A calendar inset into the desktop\n\nDownload: [https:\/\/github.com\/Hammerspoon\/Spoons\/raw\/master\/Spoons\/Calendar.spoon.zip](https:\/\/github.com\/Hammerspoon\/Spoons\/raw\/master\/Spoons\/Calendar.spoon.zip)", 27 | "Field" : [ 28 | 29 | ], 30 | "Command" : [ 31 | 32 | ], 33 | "Method" : [ 34 | 35 | ], 36 | "items" : [ 37 | 38 | ], 39 | "name" : "Calendar" 40 | } 41 | ] -------------------------------------------------------------------------------- /hammerspoon/Spoons/CircleClock.spoon/docs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Constant" : [ 4 | 5 | ], 6 | "submodules" : [ 7 | 8 | ], 9 | "Function" : [ 10 | 11 | ], 12 | "Variable" : [ 13 | 14 | ], 15 | "stripped_doc" : [ 16 | 17 | ], 18 | "type" : "Module", 19 | "Deprecated" : [ 20 | 21 | ], 22 | "desc" : "A circleclock inset into the desktop", 23 | "Constructor" : [ 24 | 25 | ], 26 | "Field" : [ 27 | 28 | ], 29 | "Method" : [ 30 | 31 | ], 32 | "Command" : [ 33 | 34 | ], 35 | "items" : [ 36 | 37 | ], 38 | "doc" : "A circleclock inset into the desktop\n\nDownload: [https:\/\/github.com\/Hammerspoon\/Spoons\/raw\/master\/Spoons\/CircleClock.spoon.zip](https:\/\/github.com\/Hammerspoon\/Spoons\/raw\/master\/Spoons\/CircleClock.spoon.zip)", 39 | "name" : "CircleClock" 40 | } 41 | ] -------------------------------------------------------------------------------- /hammerspoon/Spoons/CircleClock.spoon/init.lua: -------------------------------------------------------------------------------- 1 | --- === CircleClock === 2 | --- 3 | --- A circleclock inset into the desktop 4 | --- 5 | --- Download: [https://github.com/Hammerspoon/Spoons/raw/master/Spoons/CircleClock.spoon.zip](https://github.com/Hammerspoon/Spoons/raw/master/Spoons/CircleClock.spoon.zip) 6 | 7 | local obj={} 8 | obj.__index = obj 9 | 10 | -- Metadata 11 | obj.name = "CircleClock" 12 | obj.version = "1.0" 13 | obj.author = "ashfinal " 14 | obj.homepage = "https://github.com/Hammerspoon/Spoons" 15 | obj.license = "MIT - https://opensource.org/licenses/MIT" 16 | 17 | -- Internal function used to find our location, so we know where to load files from 18 | local function script_path() 19 | local str = debug.getinfo(2, "S").source:sub(2) 20 | return str:match("(.*/)") 21 | end 22 | 23 | obj.spoonPath = script_path() 24 | 25 | local function updateClock() 26 | local secnum = math.tointeger(os.date("%S")) 27 | local minnum = math.tointeger(os.date("%M")) 28 | local hournum = math.tointeger(os.date("%I")) 29 | local secangle = 6*secnum 30 | local minangle = 6*minnum+6/60*secnum 31 | local hourangle = 30*hournum+30/60*minnum+30/60/60*secnum 32 | 33 | obj.canvas[3].endAngle = secangle 34 | obj.canvas[7].endAngle = minangle 35 | -- hourangle may be larger than 360 at 12pm-1pm 36 | if hourangle >= 360 then 37 | hourangle = hourangle-360 38 | end 39 | obj.canvas[5].endAngle = hourangle 40 | end 41 | 42 | function obj:init() 43 | local cscreen = hs.screen.mainScreen() 44 | local cres = cscreen:fullFrame() 45 | self.canvas = hs.canvas.new({ 46 | x = cres.w-300-20, 47 | y = 100, 48 | w = 200, 49 | h = 200 50 | }):show() 51 | obj.canvas:behavior(hs.canvas.windowBehaviors.canJoinAllSpaces) 52 | obj.canvas:level(hs.canvas.windowLevels.desktopIcon) 53 | obj.canvas[1] = { 54 | id = "watch_image", 55 | type = "image", 56 | image = hs.image.imageFromPath(self.spoonPath .. "/watchbg.png"), 57 | } 58 | obj.canvas[2] = { 59 | id = "watch_circle", 60 | type = "circle", 61 | radius = "40%", 62 | action = "stroke", 63 | strokeColor = {hex="#9E9E9E", alpha=0.3}, 64 | } 65 | obj.canvas[3] = { 66 | id = "watch_sechand", 67 | type = "arc", 68 | radius = "40%", 69 | fillColor = {hex="#9E9E9E", alpha=0.1}, 70 | strokeColor = {hex="#9E9E9E", alpha=0.3}, 71 | endAngle = 0, 72 | } 73 | obj.canvas[4] = { 74 | id = "watch_hourcircle", 75 | type = "circle", 76 | action = "stroke", 77 | radius = "20%", 78 | strokeWidth = 3, 79 | strokeColor = {hex="#FFFFFF", alpha=0.1}, 80 | } 81 | obj.canvas[5] = { 82 | id = "watch_hourarc", 83 | type = "arc", 84 | action = "stroke", 85 | radius = "20%", 86 | arcRadii = false, 87 | strokeWidth = 3, 88 | strokeColor = {hex="#EC6D27", alpha=0.75}, 89 | endAngle = 0, 90 | } 91 | obj.canvas[6] = { 92 | id = "watch_mincircle", 93 | type = "circle", 94 | action = "stroke", 95 | radius = "27%", 96 | strokeWidth = 3, 97 | strokeColor = {hex="#FFFFFF", alpha=0.1}, 98 | } 99 | obj.canvas[7] = { 100 | id = "watch_minarc", 101 | type = "arc", 102 | action = "stroke", 103 | radius = "27%", 104 | arcRadii = false, 105 | strokeWidth = 3, 106 | strokeColor = {hex="#1891C3", alpha=0.75}, 107 | endAngle = 0, 108 | } 109 | if obj.timer == nil then 110 | obj.timer = hs.timer.doEvery(1, function() updateClock() end) 111 | else 112 | obj.timer:start() 113 | end 114 | end 115 | 116 | return obj 117 | -------------------------------------------------------------------------------- /hammerspoon/Spoons/CircleClock.spoon/watchbg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/hammerspoon/Spoons/CircleClock.spoon/watchbg.png -------------------------------------------------------------------------------- /hammerspoon/Spoons/CountDown.spoon/docs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Constant" : [ 4 | 5 | ], 6 | "submodules" : [ 7 | 8 | ], 9 | "Function" : [ 10 | 11 | ], 12 | "Variable" : [ 13 | 14 | ], 15 | "stripped_doc" : [ 16 | 17 | ], 18 | "Deprecated" : [ 19 | 20 | ], 21 | "type" : "Module", 22 | "desc" : "Tiny countdown with visual indicator", 23 | "Constructor" : [ 24 | 25 | ], 26 | "Field" : [ 27 | 28 | ], 29 | "Method" : [ 30 | { 31 | "parameters" : [ 32 | " * minutes - How many minutes" 33 | ], 34 | "stripped_doc" : [ 35 | "Start a countdown for `minutes` minutes immediately. Calling this method again will kill the existing countdown instance.", 36 | "" 37 | ], 38 | "desc" : "Start a countdown for `minutes` minutes immediately. Calling this method again will kill the existing countdown instance.", 39 | "doc" : "Start a countdown for `minutes` minutes immediately. Calling this method again will kill the existing countdown instance.\n\nParameters:\n * minutes - How many minutes", 40 | "notes" : [ 41 | 42 | ], 43 | "signature" : "CountDown:startFor(minutes)", 44 | "type" : "Method", 45 | "returns" : [ 46 | 47 | ], 48 | "name" : "startFor", 49 | "def" : "CountDown:startFor(minutes)" 50 | }, 51 | { 52 | "parameters" : [ 53 | 54 | ], 55 | "stripped_doc" : [ 56 | "Pause or resume the existing countdown." 57 | ], 58 | "desc" : "Pause or resume the existing countdown.", 59 | "doc" : "Pause or resume the existing countdown.", 60 | "notes" : [ 61 | 62 | ], 63 | "signature" : "CountDown:pauseOrResume()", 64 | "type" : "Method", 65 | "returns" : [ 66 | 67 | ], 68 | "name" : "pauseOrResume", 69 | "def" : "CountDown:pauseOrResume()" 70 | }, 71 | { 72 | "parameters" : [ 73 | " * progress - an number specifying the value of progress (0.0 - 1.0)" 74 | ], 75 | "stripped_doc" : [ 76 | "Set the progress of visual indicator to `progress`.", 77 | "" 78 | ], 79 | "desc" : "Set the progress of visual indicator to `progress`.", 80 | "doc" : "Set the progress of visual indicator to `progress`.\n\nParameters:\n * progress - an number specifying the value of progress (0.0 - 1.0)", 81 | "notes" : [ 82 | 83 | ], 84 | "signature" : "CountDown:setProgress(progress)", 85 | "type" : "Method", 86 | "returns" : [ 87 | 88 | ], 89 | "name" : "setProgress", 90 | "def" : "CountDown:setProgress(progress)" 91 | } 92 | ], 93 | "Command" : [ 94 | 95 | ], 96 | "items" : [ 97 | { 98 | "parameters" : [ 99 | 100 | ], 101 | "stripped_doc" : [ 102 | "Pause or resume the existing countdown." 103 | ], 104 | "desc" : "Pause or resume the existing countdown.", 105 | "doc" : "Pause or resume the existing countdown.", 106 | "notes" : [ 107 | 108 | ], 109 | "signature" : "CountDown:pauseOrResume()", 110 | "type" : "Method", 111 | "returns" : [ 112 | 113 | ], 114 | "name" : "pauseOrResume", 115 | "def" : "CountDown:pauseOrResume()" 116 | }, 117 | { 118 | "parameters" : [ 119 | " * progress - an number specifying the value of progress (0.0 - 1.0)" 120 | ], 121 | "stripped_doc" : [ 122 | "Set the progress of visual indicator to `progress`.", 123 | "" 124 | ], 125 | "desc" : "Set the progress of visual indicator to `progress`.", 126 | "doc" : "Set the progress of visual indicator to `progress`.\n\nParameters:\n * progress - an number specifying the value of progress (0.0 - 1.0)", 127 | "notes" : [ 128 | 129 | ], 130 | "signature" : "CountDown:setProgress(progress)", 131 | "type" : "Method", 132 | "returns" : [ 133 | 134 | ], 135 | "name" : "setProgress", 136 | "def" : "CountDown:setProgress(progress)" 137 | }, 138 | { 139 | "parameters" : [ 140 | " * minutes - How many minutes" 141 | ], 142 | "stripped_doc" : [ 143 | "Start a countdown for `minutes` minutes immediately. Calling this method again will kill the existing countdown instance.", 144 | "" 145 | ], 146 | "desc" : "Start a countdown for `minutes` minutes immediately. Calling this method again will kill the existing countdown instance.", 147 | "doc" : "Start a countdown for `minutes` minutes immediately. Calling this method again will kill the existing countdown instance.\n\nParameters:\n * minutes - How many minutes", 148 | "notes" : [ 149 | 150 | ], 151 | "signature" : "CountDown:startFor(minutes)", 152 | "type" : "Method", 153 | "returns" : [ 154 | 155 | ], 156 | "name" : "startFor", 157 | "def" : "CountDown:startFor(minutes)" 158 | } 159 | ], 160 | "doc" : "Tiny countdown with visual indicator\n\nDownload: [https:\/\/github.com\/Hammerspoon\/Spoons\/raw\/master\/Spoons\/CountDown.spoon.zip](https:\/\/github.com\/Hammerspoon\/Spoons\/raw\/master\/Spoons\/CountDown.spoon.zip)", 161 | "name" : "CountDown" 162 | } 163 | ] -------------------------------------------------------------------------------- /hammerspoon/Spoons/CountDown.spoon/init.lua: -------------------------------------------------------------------------------- 1 | --- === CountDown === 2 | --- 3 | --- Tiny countdown with visual indicator 4 | --- 5 | --- Download: [https://github.com/Hammerspoon/Spoons/raw/master/Spoons/CountDown.spoon.zip](https://github.com/Hammerspoon/Spoons/raw/master/Spoons/CountDown.spoon.zip) 6 | 7 | local obj = {} 8 | obj.__index = obj 9 | 10 | -- Metadata 11 | obj.name = "CountDown" 12 | obj.version = "1.0" 13 | obj.author = "ashfinal " 14 | obj.homepage = "https://github.com/Hammerspoon/Spoons" 15 | obj.license = "MIT - https://opensource.org/licenses/MIT" 16 | 17 | obj.canvas = nil 18 | obj.timer = nil 19 | 20 | function obj:init() 21 | self.canvas = hs.canvas.new({x=0, y=0, w=0, h=0}):show() 22 | self.canvas:behavior(hs.canvas.windowBehaviors.canJoinAllSpaces) 23 | self.canvas:level(hs.canvas.windowLevels.status) 24 | self.canvas:alpha(0.35) 25 | self.canvas[1] = { 26 | type = "rectangle", 27 | action = "fill", 28 | fillColor = hs.drawing.color.osx_red, 29 | frame = {x="0%", y="0%", w="0%", h="100%"} 30 | } 31 | self.canvas[2] = { 32 | type = "rectangle", 33 | action = "fill", 34 | fillColor = hs.drawing.color.osx_green, 35 | frame = {x="0%", y="0%", w="0%", h="100%"} 36 | } 37 | end 38 | 39 | --- CountDown:startFor(minutes) 40 | --- Method 41 | --- Start a countdown for `minutes` minutes immediately. Calling this method again will kill the existing countdown instance. 42 | --- 43 | --- Parameters: 44 | --- * minutes - How many minutes 45 | 46 | local function canvasCleanup() 47 | if obj.timer then 48 | obj.timer:stop() 49 | obj.timer = nil 50 | end 51 | obj.canvas[1].frame.w = "0%" 52 | obj.canvas[2].frame.x = "0%" 53 | obj.canvas[2].frame.w = "0%" 54 | obj.canvas:frame({x=0, y=0, w=0, h=0}) 55 | end 56 | 57 | function obj:startFor(minutes) 58 | if obj.timer then 59 | canvasCleanup() 60 | else 61 | local mainScreen = hs.screen.mainScreen() 62 | local mainRes = mainScreen:fullFrame() 63 | obj.canvas:frame({x=mainRes.x, y=mainRes.y+mainRes.h-5, w=mainRes.w, h=5}) 64 | -- Set minimum visual step to 2px (i.e. Make sure every trigger updates 2px on screen at least.) 65 | local minimumStep = 2 66 | local secCount = math.ceil(60*minutes) 67 | obj.loopCount = 0 68 | if mainRes.w/secCount >= 2 then 69 | obj.timer = hs.timer.doEvery(1, function() 70 | obj.loopCount = obj.loopCount+1/secCount 71 | obj:setProgress(obj.loopCount, minutes) 72 | end) 73 | else 74 | local interval = 2/(mainRes.w/secCount) 75 | obj.timer = hs.timer.doEvery(interval, function() 76 | obj.loopCount = obj.loopCount+1/mainRes.w*2 77 | obj:setProgress(obj.loopCount, minutes) 78 | end) 79 | end 80 | end 81 | 82 | return self 83 | end 84 | 85 | --- CountDown:pauseOrResume() 86 | --- Method 87 | --- Pause or resume the existing countdown. 88 | --- 89 | 90 | function obj:pauseOrResume() 91 | if obj.timer then 92 | if obj.timer:running() then 93 | obj.timer:stop() 94 | else 95 | obj.timer:start() 96 | end 97 | end 98 | end 99 | 100 | --- CountDown:setProgress(progress) 101 | --- Method 102 | --- Set the progress of visual indicator to `progress`. 103 | --- 104 | --- Parameters: 105 | --- * progress - an number specifying the value of progress (0.0 - 1.0) 106 | 107 | function obj:setProgress(progress, notifystr) 108 | if obj.canvas:frame().h == 0 then 109 | -- Make the canvas actully visible 110 | local mainScreen = hs.screen.mainScreen() 111 | local mainRes = mainScreen:fullFrame() 112 | obj.canvas:frame({x=mainRes.x, y=mainRes.y+mainRes.h-5, w=mainRes.w, h=5}) 113 | end 114 | if progress >= 1 then 115 | canvasCleanup() 116 | if notifystr then 117 | hs.notify.new({ 118 | title = "Time(" .. notifystr .. " mins) is up!", 119 | informativeText = "Now is " .. os.date("%X") 120 | }):send() 121 | end 122 | else 123 | obj.canvas[1].frame.w = tostring(progress) 124 | obj.canvas[2].frame.x = tostring(progress) 125 | obj.canvas[2].frame.w = tostring(1-progress) 126 | end 127 | end 128 | 129 | return obj 130 | -------------------------------------------------------------------------------- /hammerspoon/Spoons/HCalendar.spoon/docs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Constant" : [ 4 | 5 | ], 6 | "submodules" : [ 7 | 8 | ], 9 | "Function" : [ 10 | 11 | ], 12 | "Variable" : [ 13 | 14 | ], 15 | "stripped_doc" : [ 16 | 17 | ], 18 | "Deprecated" : [ 19 | 20 | ], 21 | "type" : "Module", 22 | "desc" : "A horizonal calendar inset into the desktop", 23 | "Constructor" : [ 24 | 25 | ], 26 | "items" : [ 27 | 28 | ], 29 | "Field" : [ 30 | 31 | ], 32 | "Command" : [ 33 | 34 | ], 35 | "Method" : [ 36 | 37 | ], 38 | "doc" : "A horizonal calendar inset into the desktop\n\nDownload: [https:\/\/github.com\/Hammerspoon\/Spoons\/raw\/master\/Spoons\/HCalendar.spoon.zip](https:\/\/github.com\/Hammerspoon\/Spoons\/raw\/master\/Spoons\/HCalendar.spoon.zip)", 39 | "name" : "HCalendar" 40 | } 41 | ] -------------------------------------------------------------------------------- /hammerspoon/Spoons/HSearch.spoon/docs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Constant" : [ 4 | 5 | ], 6 | "submodules" : [ 7 | 8 | ], 9 | "Function" : [ 10 | 11 | ], 12 | "Variable" : [ 13 | 14 | ], 15 | "stripped_doc" : [ 16 | 17 | ], 18 | "Deprecated" : [ 19 | 20 | ], 21 | "type" : "Module", 22 | "desc" : "Hammerspoon Search", 23 | "Constructor" : [ 24 | 25 | ], 26 | "Field" : [ 27 | 28 | ], 29 | "Method" : [ 30 | { 31 | "doc" : "Trigger new source according to hs.chooser's query string and keyword. Only for debug purpose in usual.", 32 | "stripped_doc" : [ 33 | "Trigger new source according to hs.chooser's query string and keyword. Only for debug purpose in usual." 34 | ], 35 | "parameters" : [ 36 | 37 | ], 38 | "def" : "HSearch:switchSource()", 39 | "notes" : [ 40 | 41 | ], 42 | "signature" : "HSearch:switchSource()", 43 | "type" : "Method", 44 | "returns" : [ 45 | 46 | ], 47 | "desc" : "Trigger new source according to hs.chooser's query string and keyword. Only for debug purpose in usual.", 48 | "name" : "switchSource" 49 | }, 50 | { 51 | "doc" : "Load new sources from `HSearch.search_path`, the search_path defaults to `~\/.hammerspoon\/private\/hsearch_dir` and the HSearch Spoon directory. Only for debug purpose in usual.", 52 | "stripped_doc" : [ 53 | "Load new sources from `HSearch.search_path`, the search_path defaults to `~\/.hammerspoon\/private\/hsearch_dir` and the HSearch Spoon directory. Only for debug purpose in usual." 54 | ], 55 | "parameters" : [ 56 | 57 | ], 58 | "def" : "HSearch:loadSources()", 59 | "notes" : [ 60 | 61 | ], 62 | "signature" : "HSearch:loadSources()", 63 | "type" : "Method", 64 | "returns" : [ 65 | 66 | ], 67 | "desc" : "Load new sources from `HSearch.search_path`, the search_path defaults to `~\/.hammerspoon\/private\/hsearch_dir` and the HSearch Spoon directory. Only for debug purpose in usual.", 68 | "name" : "loadSources" 69 | }, 70 | { 71 | "doc" : "Toggle the display of HSearch", 72 | "stripped_doc" : [ 73 | "Toggle the display of HSearch" 74 | ], 75 | "parameters" : [ 76 | 77 | ], 78 | "def" : "HSearch:toggleShow()", 79 | "notes" : [ 80 | 81 | ], 82 | "signature" : "HSearch:toggleShow()", 83 | "type" : "Method", 84 | "returns" : [ 85 | 86 | ], 87 | "desc" : "Toggle the display of HSearch", 88 | "name" : "toggleShow" 89 | } 90 | ], 91 | "Command" : [ 92 | 93 | ], 94 | "doc" : "Hammerspoon Search\n\nDownload: [https:\/\/github.com\/Hammerspoon\/Spoons\/raw\/master\/Spoons\/HSearch.spoon.zip](https:\/\/github.com\/Hammerspoon\/Spoons\/raw\/master\/Spoons\/HSearch.spoon.zip)", 95 | "items" : [ 96 | { 97 | "doc" : "Load new sources from `HSearch.search_path`, the search_path defaults to `~\/.hammerspoon\/private\/hsearch_dir` and the HSearch Spoon directory. Only for debug purpose in usual.", 98 | "stripped_doc" : [ 99 | "Load new sources from `HSearch.search_path`, the search_path defaults to `~\/.hammerspoon\/private\/hsearch_dir` and the HSearch Spoon directory. Only for debug purpose in usual." 100 | ], 101 | "parameters" : [ 102 | 103 | ], 104 | "def" : "HSearch:loadSources()", 105 | "notes" : [ 106 | 107 | ], 108 | "signature" : "HSearch:loadSources()", 109 | "type" : "Method", 110 | "returns" : [ 111 | 112 | ], 113 | "desc" : "Load new sources from `HSearch.search_path`, the search_path defaults to `~\/.hammerspoon\/private\/hsearch_dir` and the HSearch Spoon directory. Only for debug purpose in usual.", 114 | "name" : "loadSources" 115 | }, 116 | { 117 | "doc" : "Trigger new source according to hs.chooser's query string and keyword. Only for debug purpose in usual.", 118 | "stripped_doc" : [ 119 | "Trigger new source according to hs.chooser's query string and keyword. Only for debug purpose in usual." 120 | ], 121 | "parameters" : [ 122 | 123 | ], 124 | "def" : "HSearch:switchSource()", 125 | "notes" : [ 126 | 127 | ], 128 | "signature" : "HSearch:switchSource()", 129 | "type" : "Method", 130 | "returns" : [ 131 | 132 | ], 133 | "desc" : "Trigger new source according to hs.chooser's query string and keyword. Only for debug purpose in usual.", 134 | "name" : "switchSource" 135 | }, 136 | { 137 | "doc" : "Toggle the display of HSearch", 138 | "stripped_doc" : [ 139 | "Toggle the display of HSearch" 140 | ], 141 | "parameters" : [ 142 | 143 | ], 144 | "def" : "HSearch:toggleShow()", 145 | "notes" : [ 146 | 147 | ], 148 | "signature" : "HSearch:toggleShow()", 149 | "type" : "Method", 150 | "returns" : [ 151 | 152 | ], 153 | "desc" : "Toggle the display of HSearch", 154 | "name" : "toggleShow" 155 | } 156 | ], 157 | "name" : "HSearch" 158 | } 159 | ] 160 | -------------------------------------------------------------------------------- /hammerspoon/Spoons/HSearch.spoon/hs_btabs.lua: -------------------------------------------------------------------------------- 1 | local obj={} 2 | obj.__index = obj 3 | 4 | obj.name = "browserTabs" 5 | obj.version = "1.0" 6 | obj.author = "ashfinal " 7 | 8 | -- Internal function used to find our location, so we know where to load files from 9 | local function script_path() 10 | local str = debug.getinfo(2, "S").source:sub(2) 11 | return str:match("(.*/)") 12 | end 13 | 14 | obj.spoonPath = script_path() 15 | 16 | -- Define the source's overview. A unique `keyword` key should exist, so this source can be found. 17 | obj.overview = {text="Type t ⇥ to search safari/chrome Tabs.", image=hs.image.imageFromPath(obj.spoonPath .. "/resources/tabs.png"), keyword="t"} 18 | -- Define the notice when a long-time request is being executed. It could be `nil`. 19 | obj.notice = {text="Requesting data, please wait a while …"} 20 | 21 | local function browserTabsRequest() 22 | local safari_running = hs.application.applicationsForBundleID("com.apple.Safari") 23 | local chooser_data = {} 24 | if #safari_running > 0 then 25 | local stat, data= hs.osascript.applescript('tell application "Safari"\nset winlist to tabs of windows\nset tablist to {}\nrepeat with i in winlist\nif (count of i) > 0 then\nrepeat with currenttab in i\nset tabinfo to {name of currenttab as unicode text, URL of currenttab}\ncopy tabinfo to the end of tablist\nend repeat\nend if\nend repeat\nreturn tablist\nend tell') 26 | -- Notice `output` key and its `arg`. The built-in output contains `browser`, `safari`, `chrome`, `firefon`, `clipboard`, `keystrokes`. You can define new output type if you like. 27 | if stat then 28 | chooser_data = hs.fnutils.imap(data, function(item) 29 | return {text=item[1], subText=item[2], image=hs.image.imageFromPath(obj.spoonPath .. "/resources/safari.png"), output="safari", arg=item[2]} 30 | end) 31 | end 32 | end 33 | local chrome_running = hs.application.applicationsForBundleID("com.google.Chrome") 34 | if #chrome_running > 0 then 35 | local stat, data= hs.osascript.applescript('tell application "Google Chrome"\nset winlist to tabs of windows\nset tablist to {}\nrepeat with i in winlist\nif (count of i) > 0 then\nrepeat with currenttab in i\nset tabinfo to {name of currenttab as unicode text, URL of currenttab}\ncopy tabinfo to the end of tablist\nend repeat\nend if\nend repeat\nreturn tablist\nend tell') 36 | if stat then 37 | for idx,val in pairs(data) do 38 | -- Usually we want to open chrome tabs in Google Chrome. 39 | table.insert(chooser_data, {text=val[1], subText=val[2], image=hs.image.imageFromPath(obj.spoonPath .. "/resources/chrome.png"), output="chrome", arg=val[2]}) 40 | end 41 | end 42 | end 43 | -- Return specific table as hs.chooser's data, other keys except for `text` could be optional. 44 | return chooser_data 45 | end 46 | 47 | -- Define the function which will be called when the `keyword` triggers a new source. The returned value is a table. Read more: http://www.hammerspoon.org/docs/hs.chooser.html#choices 48 | obj.init_func = browserTabsRequest 49 | -- Insert a friendly tip at the head so users know what to do next. 50 | obj.description = {text="Browser Tabs Search", subText="Search and select one item to open in corresponding browser.", image=hs.image.imageFromPath(obj.spoonPath .. "/resources/tabs.png")} 51 | 52 | -- As the user is typing, the callback function will be called for every keypress. The returned value is a table. 53 | obj.callback = nil 54 | 55 | return obj 56 | -------------------------------------------------------------------------------- /hammerspoon/Spoons/HSearch.spoon/hs_datamuse.lua: -------------------------------------------------------------------------------- 1 | local obj={} 2 | obj.__index = obj 3 | 4 | obj.name = "thesaurusDM" 5 | obj.version = "1.0" 6 | obj.author = "ashfinal " 7 | 8 | -- Internal function used to find our location, so we know where to load files from 9 | local function script_path() 10 | local str = debug.getinfo(2, "S").source:sub(2) 11 | return str:match("(.*/)") 12 | end 13 | 14 | obj.spoonPath = script_path() 15 | 16 | -- Define the source's overview. A unique `keyword` key should exist, so this source can be found. 17 | obj.overview = {text="Type s ⇥ to request English Thesaurus.", image=hs.image.imageFromPath(obj.spoonPath .. "/resources/thesaurus.png"), keyword="s"} 18 | -- Define the notice when a long-time request is being executed. It could be `nil`. 19 | obj.notice = nil 20 | 21 | local function dmTips() 22 | local chooser_data = { 23 | {text="Datamuse Thesaurus", subText="Type something to get more words like it …", image=hs.image.imageFromPath(obj.spoonPath .. "/resources/thesaurus.png")} 24 | } 25 | return chooser_data 26 | end 27 | 28 | -- Define the function which will be called when the `keyword` triggers a new source. The returned value is a table. Read more: http://www.hammerspoon.org/docs/hs.chooser.html#choices 29 | obj.init_func = dmTips 30 | -- Insert a friendly tip at the head so users know what to do next. 31 | -- As this source highly relys on queryChangedCallback, we'd better tip users in callback instead of here 32 | obj.description = nil 33 | 34 | -- As the user is typing, the callback function will be called for every keypress. The returned value is a table. 35 | 36 | local function thesaurusRequest(querystr) 37 | local datamuse_baseurl = 'http://api.datamuse.com' 38 | if string.len(querystr) > 0 then 39 | local encoded_query = hs.http.encodeForQuery(querystr) 40 | local query_url = datamuse_baseurl .. '/words?ml=' .. encoded_query .. '&max=20' 41 | 42 | hs.http.asyncGet(query_url, nil, function(status, data) 43 | if status == 200 then 44 | if pcall(function() hs.json.decode(data) end) then 45 | local decoded_data = hs.json.decode(data) 46 | if #decoded_data > 0 then 47 | local chooser_data = hs.fnutils.imap(decoded_data, function(item) 48 | return {text = item.word, image=hs.image.imageFromPath(obj.spoonPath .. "/resources/thesaurus.png"), output="keystrokes", arg=item.word} 49 | end) 50 | -- Because we don't know when asyncGet will return data, we have to refresh hs.chooser choices in this callback. 51 | if spoon.HSearch then 52 | -- Make sure HSearch spoon is running now 53 | spoon.HSearch.chooser:choices(chooser_data) 54 | spoon.HSearch.chooser:refreshChoicesCallback() 55 | end 56 | end 57 | end 58 | end 59 | end) 60 | else 61 | local chooser_data = { 62 | {text="Datamuse Thesaurus", subText="Type something to get more words like it …", image=hs.image.imageFromPath(obj.spoonPath .. "/resources/thesaurus.png")} 63 | } 64 | if spoon.HSearch then 65 | spoon.HSearch.chooser:choices(chooser_data) 66 | spoon.HSearch.chooser:refreshChoicesCallback() 67 | end 68 | end 69 | end 70 | 71 | obj.callback = thesaurusRequest 72 | 73 | return obj 74 | -------------------------------------------------------------------------------- /hammerspoon/Spoons/HSearch.spoon/hs_emoji.lua: -------------------------------------------------------------------------------- 1 | local obj={} 2 | obj.__index = obj 3 | 4 | obj.name = "MLemoji" 5 | obj.version = "1.0" 6 | obj.author = "ashfinal " 7 | 8 | -- Internal function used to find our location, so we know where to load files from 9 | local function script_path() 10 | local str = debug.getinfo(2, "S").source:sub(2) 11 | return str:match("(.*/)") 12 | end 13 | 14 | obj.spoonPath = script_path() 15 | 16 | -- Define the source's overview. A unique `keyword` key should exist, so this source can be found. 17 | obj.overview = {text="Type e ⇥ to find relevant Emoji.", image=hs.image.imageFromPath(obj.spoonPath .. "/resources/emoji.png"), keyword="e"} 18 | -- Define the notice when a long-time request is being executed. It could be `nil`. 19 | obj.notice = nil 20 | 21 | local function emojiTips() 22 | local chooser_data = { 23 | {text="Relevant Emoji", subText="Type something to find relevant emoji from text …", image=hs.image.imageFromPath(obj.spoonPath .. "/resources/emoji.png")} 24 | } 25 | return chooser_data 26 | end 27 | -- Define the function which will be called when the `keyword` triggers a new source. The returned value is a table. Read more: http://www.hammerspoon.org/docs/hs.chooser.html#choices 28 | obj.init_func = emojiTips 29 | -- Insert a friendly tip at the head so users know what to do next. 30 | -- As this source highly relys on queryChangedCallback, we'd better tip users in callback instead of here 31 | obj.description = nil 32 | 33 | -- As the user is typing, the callback function will be called for every keypress. The returned value is a table. 34 | 35 | -- Some global objects 36 | local emoji_database_path = "/System/Library/Input Methods/CharacterPalette.app/Contents/Resources/CharacterDB.sqlite3" 37 | obj.database = hs.sqlite3.open(emoji_database_path) 38 | obj.canvas = hs.canvas.new({x=0, y=0, w=96, h=96}) 39 | 40 | local function getEmojiDesc(arg) 41 | for w in obj.database:rows("SELECT info FROM unihan_dict WHERE uchr=\'" .. arg .. "\'") do 42 | return w[1] 43 | end 44 | end 45 | 46 | local function emojiRequest(querystr) 47 | local emoji_baseurl = 'https://emoji.getdango.com' 48 | if string.len(querystr) > 0 then 49 | local encoded_query = hs.http.encodeForQuery(querystr) 50 | local query_url = emoji_baseurl .. '/api/emoji?q=' .. encoded_query 51 | 52 | hs.http.asyncGet(query_url, nil, function(status, data) 53 | if status == 200 then 54 | if pcall(function() hs.json.decode(data) end) then 55 | local decoded_data = hs.json.decode(data) 56 | if decoded_data.results and #decoded_data.results > 0 then 57 | local chooser_data = hs.fnutils.imap(decoded_data.results, function(item) 58 | obj.canvas[1] = {type="text", text=item.text, textSize=64, frame={x="15%", y="10%", w="100%", h="100%"}} 59 | local hexcode = string.format("%#X", utf8.codepoint(item.text)) 60 | local emoji_description = getEmojiDesc(item.text) 61 | local formatted_desc = string.gsub(emoji_description, "|||||||||||||||", "") 62 | return {text = formatted_desc, image=obj.canvas:imageFromCanvas(), subText="Hex Code: " .. hexcode, outputType="keystrokes", arg=item.text} 63 | end) 64 | -- Because we don't know when asyncGet will return data, we have to refresh hs.chooser choices in this callback. 65 | if spoon.HSearch then 66 | -- Make sure HSearch spoon is running now 67 | spoon.HSearch.chooser:choices(chooser_data) 68 | spoon.HSearch.chooser:refreshChoicesCallback() 69 | end 70 | end 71 | end 72 | end 73 | end) 74 | else 75 | local chooser_data = { 76 | {text="Relevant Emoji", subText="Type something to find relevant emoji from text …", image=hs.image.imageFromPath(obj.spoonPath .. "/resources/emoji.png")} 77 | } 78 | if spoon.HSearch then 79 | spoon.HSearch.chooser:choices(chooser_data) 80 | spoon.HSearch.chooser:refreshChoicesCallback() 81 | end 82 | end 83 | end 84 | 85 | obj.callback = emojiRequest 86 | 87 | return obj 88 | -------------------------------------------------------------------------------- /hammerspoon/Spoons/HSearch.spoon/hs_note.lua: -------------------------------------------------------------------------------- 1 | local obj={} 2 | obj.__index = obj 3 | 4 | obj.name = "justNote" 5 | obj.version = "1.0" 6 | obj.author = "ashfinal " 7 | 8 | -- Internal function used to find our location, so we know where to load files from 9 | local function script_path() 10 | local str = debug.getinfo(2, "S").source:sub(2) 11 | return str:match("(.*/)") 12 | end 13 | 14 | obj.spoonPath = script_path() 15 | 16 | -- Define the source's overview. A unique `keyword` key should exist, so this source can be found. 17 | obj.overview = {text="Type n ⇥ to Note something.", image=hs.image.imageFromPath(obj.spoonPath .. "/resources/justnote.png"), keyword="n"} 18 | -- Define the notice when a long-time request is being executed. It could be `nil`. 19 | obj.notice = nil 20 | -- Define the hotkeys, which will be enabled/disabled automatically. You need to add your keybindings into this table manually. 21 | obj.hotkeys = {} 22 | 23 | local function justNoteRequest() 24 | local note_history = hs.settings.get("just.another.note") or {} 25 | if #note_history == 0 then 26 | local chooser_data = {{text="Write something and press Enter.", subText="Your notes is automatically saved, selected item will be erased.", image=hs.image.imageFromPath(obj.spoonPath .. "/resources/justnote.png")}} 27 | return chooser_data 28 | else 29 | local chooser_data = hs.fnutils.imap(note_history, function(item) 30 | return {uuid=item.uuid, text=item.content, subText=item.ctime, image=hs.image.imageFromPath(obj.spoonPath .. "/resources/justnote.png"), output="noteremove", arg=item.uuid} 31 | end) 32 | return chooser_data 33 | end 34 | end 35 | -- Define the function which will be called when the `keyword` triggers a new source. The returned value is a table. Read more: http://www.hammerspoon.org/docs/hs.chooser.html#choices 36 | obj.init_func = justNoteRequest 37 | -- Insert a friendly tip at the head so users know what to do next. 38 | obj.description = nil 39 | -- As the user is typing, the callback function will be called for every keypress. The returned value is a table. 40 | 41 | local function isInNoteHistory(value, tbl) 42 | for idx,val in pairs(tbl) do 43 | if val.uuid == value then 44 | return true 45 | end 46 | end 47 | return false 48 | end 49 | 50 | local function justNoteStore() 51 | if spoon.HSearch then 52 | local querystr = string.gsub(spoon.HSearch.chooser:query(), "%s+$", "") 53 | if string.len(querystr) > 0 then 54 | local query_hash = hs.hash.SHA1(querystr) 55 | local note_history = hs.settings.get("just.another.note") or {} 56 | if not isInNoteHistory(query_hash, note_history) then 57 | table.insert(note_history, {uuid=query_hash, ctime="Created at "..os.date(), content=querystr}) 58 | hs.settings.set("just.another.note", note_history) 59 | end 60 | end 61 | end 62 | end 63 | 64 | local store_trigger = hs.hotkey.new("", "return", nil, function() 65 | justNoteStore() 66 | if spoon.HSearch then 67 | local chooser_data = justNoteRequest() 68 | spoon.HSearch.chooser:choices(chooser_data) 69 | spoon.HSearch.chooser:query("") 70 | end 71 | end) 72 | table.insert(obj.hotkeys, store_trigger) 73 | 74 | obj.callback = nil 75 | 76 | -- Define a new output type 77 | local function removeNote(arg) 78 | local note_history = hs.settings.get("just.another.note") or {} 79 | for idx,val in pairs(note_history) do 80 | if val.uuid == arg then 81 | table.remove(note_history, idx) 82 | hs.settings.set("just.another.note", note_history) 83 | end 84 | local chooser_data = justNoteRequest() 85 | if spoon.HSearch then 86 | spoon.HSearch.chooser:choices(chooser_data) 87 | end 88 | end 89 | end 90 | obj.new_output = { 91 | name = "noteremove", 92 | func = removeNote 93 | } 94 | 95 | 96 | return obj 97 | -------------------------------------------------------------------------------- /hammerspoon/Spoons/HSearch.spoon/hs_time.lua: -------------------------------------------------------------------------------- 1 | local obj={} 2 | obj.__index = obj 3 | 4 | obj.name = "timeDelta" 5 | obj.version = "1.0" 6 | obj.author = "ashfinal " 7 | 8 | -- Internal function used to find our location, so we know where to load files from 9 | local function script_path() 10 | local str = debug.getinfo(2, "S").source:sub(2) 11 | return str:match("(.*/)") 12 | end 13 | 14 | obj.spoonPath = script_path() 15 | 16 | -- Define the source's overview. A unique `keyword` key should exist, so this source can be found. 17 | obj.overview = {text="Type d ⇥ to format/query Date.", image=hs.image.imageFromPath(obj.spoonPath .. "/resources/time.png"), keyword="d"} 18 | -- Define the notice when a long-time request is being executed. It could be `nil`. 19 | obj.notice = nil 20 | 21 | -- Some global objects 22 | obj.exec_args = { 23 | '+"%Y-%m-%d"', 24 | '+"%H:%M:%S %p"', 25 | '+"%A, %B %d, %Y"', 26 | '+"%Y-%m-%d %H:%M:%S %p"', 27 | '+"%a, %b %d, %y"', 28 | '+"%m/%d/%y %H:%M %p"', 29 | '', 30 | '-u', 31 | } 32 | 33 | local function timeRequest() 34 | local chooser_data = hs.fnutils.imap(obj.exec_args, function(item) 35 | local exec_result = hs.execute("date " .. item) 36 | return {text=exec_result, subText="date " .. item, image=hs.image.imageFromPath(obj.spoonPath .. "/resources/time.png"), output="keystrokes", arg=exec_result} 37 | end) 38 | return chooser_data 39 | end 40 | -- Define the function which will be called when the `keyword` triggers a new source. The returned value is a table. Read more: http://www.hammerspoon.org/docs/hs.chooser.html#choices 41 | obj.init_func = timeRequest 42 | -- Insert a friendly tip at the head so users know what to do next. 43 | -- As this source highly relys on queryChangedCallback, we'd better tip users in callback instead of here 44 | obj.description = {text="Date Query", subText="Type +/-1d (or y, m, w, H, M, S) to query date forward or backward.", image=hs.image.imageFromPath(obj.spoonPath .. "/resources/time.png")} 45 | 46 | -- As the user is typing, the callback function will be called for every keypress. The returned value is a table. 47 | 48 | local function splitBySpace(str) 49 | local tmptbl = {} 50 | for w in string.gmatch(str,"[+-]?%d+[ymdwHMS]") do table.insert(tmptbl,w) end 51 | return tmptbl 52 | end 53 | 54 | local function timeDeltaRequest(querystr) 55 | if string.len(querystr) > 0 then 56 | local valid_inputs = splitBySpace(querystr) 57 | if #valid_inputs > 0 then 58 | local addv_before = hs.fnutils.imap(valid_inputs, function(item) 59 | return "-v" .. item 60 | end) 61 | local vv_var = table.concat(addv_before, " ") 62 | local chooser_data = hs.fnutils.imap(obj.exec_args, function(item) 63 | local new_exec_command = "date " .. vv_var .. " " .. item 64 | local new_exec_result = hs.execute(new_exec_command) 65 | return {text=new_exec_result, subText=new_exec_command, image=hs.image.imageFromPath(obj.spoonPath .. "/resources/time.png"), output="keystrokes", arg=new_exec_result} 66 | end) 67 | local source_desc = {text="Date Query", subText="Type +/-1d (or y, m, w, H, M, S) to query date forward or backward.", image=hs.image.imageFromPath(obj.spoonPath .. "/resources/time.png")} 68 | table.insert(chooser_data, 1, source_desc) 69 | if spoon.HSearch then 70 | -- Make sure HSearch spoon is running now 71 | spoon.HSearch.chooser:choices(chooser_data) 72 | end 73 | end 74 | else 75 | local chooser_data = timeRequest() 76 | local source_desc = {text="Date Query", subText="Type +/-1d (or y, m, w, H, M, S) to query date forward or backward.", image=hs.image.imageFromPath(obj.spoonPath .. "/resources/time.png")} 77 | table.insert(chooser_data, 1, source_desc) 78 | if spoon.HSearch then 79 | -- Make sure HSearch spoon is running now 80 | spoon.HSearch.chooser:choices(chooser_data) 81 | end 82 | end 83 | end 84 | 85 | obj.callback = timeDeltaRequest 86 | 87 | return obj 88 | -------------------------------------------------------------------------------- /hammerspoon/Spoons/HSearch.spoon/hs_v2ex.lua: -------------------------------------------------------------------------------- 1 | local obj={} 2 | obj.__index = obj 3 | 4 | obj.name = "v2exPosts" 5 | obj.version = "1.0" 6 | obj.author = "ashfinal " 7 | 8 | -- Internal function used to find our location, so we know where to load files from 9 | local function script_path() 10 | local str = debug.getinfo(2, "S").source:sub(2) 11 | return str:match("(.*/)") 12 | end 13 | 14 | obj.spoonPath = script_path() 15 | 16 | -- Define the source's overview. A unique `keyword` key should exist, so this source can be found. 17 | obj.overview = {text="Type v ⇥ to fetch v2ex posts.", image=hs.image.imageFromPath(obj.spoonPath .. "/resources/v2ex.png"), keyword="v"} 18 | -- Define the notice when a long-time request is being executed. It could be `nil`. 19 | obj.notice = {text="Requesting data, please wait a while …"} 20 | 21 | local function v2exRequest() 22 | local query_url = 'https://www.v2ex.com/api/topics/latest.json' 23 | local stat, body = hs.http.asyncGet(query_url, nil, function(status, data) 24 | if status == 200 then 25 | if pcall(function() hs.json.decode(data) end) then 26 | local decoded_data = hs.json.decode(data) 27 | if #decoded_data > 0 then 28 | local chooser_data = hs.fnutils.imap(decoded_data, function(item) 29 | local sub_content = string.gsub(item.content, "\r\n", " ") 30 | local function trim_content() 31 | if utf8.len(sub_content) > 40 then 32 | return string.sub(sub_content, 1, utf8.offset(sub_content, 40)-1) 33 | else 34 | return sub_content 35 | end 36 | end 37 | local final_content = trim_content() 38 | return {text=item.title, subText=final_content, image=hs.image.imageFromPath(obj.spoonPath .. "/resources/v2ex.png"), outputType="browser", arg=item.url} 39 | end) 40 | local source_desc = {text="v2ex Posts", subText="Select some item to get it opened in default browser …", image=hs.image.imageFromPath(obj.spoonPath .. "/resources/v2ex.png")} 41 | table.insert(chooser_data, 1, source_desc) 42 | -- Because we don't know when asyncGet will return data, we have to refresh hs.chooser choices in this callback. 43 | if spoon.HSearch then 44 | -- Make sure HSearch spoon is running now 45 | spoon.HSearch.chooser:choices(chooser_data) 46 | spoon.HSearch.chooser:refreshChoicesCallback() 47 | end 48 | end 49 | end 50 | end 51 | end) 52 | end 53 | -- Define the function which will be called when the `keyword` triggers a new source. The returned value is a table. Read more: http://www.hammerspoon.org/docs/hs.chooser.html#choices 54 | obj.init_func = v2exRequest 55 | -- Insert a friendly tip at the head so users know what to do next. 56 | -- As this source highly relys on queryChangedCallback, we'd better tip users in callback instead of here 57 | obj.description = nil 58 | 59 | -- As the user is typing, the callback function will be called for every keypress. The returned value is a table. 60 | 61 | obj.callback = nil 62 | 63 | return obj 64 | -------------------------------------------------------------------------------- /hammerspoon/Spoons/HSearch.spoon/hs_yddict.lua: -------------------------------------------------------------------------------- 1 | local obj={} 2 | obj.__index = obj 3 | 4 | obj.name = "youdaoDict" 5 | obj.version = "1.0" 6 | obj.author = "ashfinal " 7 | 8 | -- Internal function used to find our location, so we know where to load files from 9 | local function script_path() 10 | local str = debug.getinfo(2, "S").source:sub(2) 11 | return str:match("(.*/)") 12 | end 13 | 14 | obj.spoonPath = script_path() 15 | 16 | -- Define the source's overview. A unique `keyword` key should exist, so this source can be found. 17 | obj.overview = {text="Type y ⇥ to use Yaodao dictionary.", image=hs.image.imageFromPath(obj.spoonPath .. "/resources/youdao.png"), keyword="y"} 18 | -- Define the notice when a long-time request is being executed. It could be `nil`. 19 | obj.notice = nil 20 | 21 | local function youdaoTips() 22 | local chooser_data = { 23 | {text="Youdao Dictionary", subText="Type something to get it translated …", image=hs.image.imageFromPath(obj.spoonPath .. "/resources/youdao.png")} 24 | } 25 | return chooser_data 26 | end 27 | 28 | -- Define the function which will be called when the `keyword` triggers a new source. The returned value is a table. Read more: http://www.hammerspoon.org/docs/hs.chooser.html#choices 29 | obj.init_func = youdaoTips 30 | -- Insert a friendly tip at the head so users know what to do next. 31 | -- As this source highly relys on queryChangedCallback, we'd better tip users in callback instead of here 32 | obj.description = nil 33 | 34 | -- As the user is typing, the callback function will be called for every keypress. The returned value is a table. 35 | 36 | local function basic_extract(arg) 37 | if arg then return arg.explains else return {} end 38 | end 39 | local function web_extract(arg) 40 | if arg then 41 | local value = hs.fnutils.imap(arg, function(item) 42 | return item.key .. table.concat(item.value, ",") 43 | end) 44 | return value 45 | else 46 | return {} 47 | end 48 | end 49 | 50 | local function youdaoInstantTrans(querystr) 51 | local youdao_keyfrom = 'hsearch' 52 | local youdao_apikey = '1199732752' 53 | local youdao_baseurl = 'http://fanyi.youdao.com/openapi.do?keyfrom=' .. youdao_keyfrom .. '&key=' .. youdao_apikey .. '&type=data&doctype=json&version=1.1&q=' 54 | if string.len(querystr) > 0 then 55 | local encoded_query = hs.http.encodeForQuery(querystr) 56 | local query_url = youdao_baseurl .. encoded_query 57 | 58 | hs.http.asyncGet(query_url, nil, function(status, data) 59 | if status == 200 then 60 | if pcall(function() hs.json.decode(data) end) then 61 | local decoded_data = hs.json.decode(data) 62 | if decoded_data.errorCode == 0 then 63 | local basictrans = basic_extract(decoded_data.basic) 64 | local webtrans = web_extract(decoded_data.web) 65 | local dictpool = hs.fnutils.concat(basictrans, webtrans) 66 | if #dictpool > 0 then 67 | local chooser_data = hs.fnutils.imap(dictpool, function(item) 68 | return {text=item, image=hs.image.imageFromPath(obj.spoonPath .. "/resources/youdao.png"), output="clipboard", arg=item} 69 | end) 70 | -- Because we don't know when asyncGet will return data, we have to refresh hs.chooser choices in this callback. 71 | if spoon.HSearch then 72 | -- Make sure HSearch spoon is running now 73 | spoon.HSearch.chooser:choices(chooser_data) 74 | spoon.HSearch.chooser:refreshChoicesCallback() 75 | end 76 | end 77 | end 78 | end 79 | end 80 | end) 81 | else 82 | local chooser_data = { 83 | {text="Youdao Dictionary", subText="Type something to get it translated …", image=hs.image.imageFromPath(obj.spoonPath .. "/resources/youdao.png")} 84 | } 85 | if spoon.HSearch then 86 | spoon.HSearch.chooser:choices(chooser_data) 87 | spoon.HSearch.chooser:refreshChoicesCallback() 88 | end 89 | end 90 | end 91 | 92 | obj.callback = youdaoInstantTrans 93 | 94 | return obj 95 | -------------------------------------------------------------------------------- /hammerspoon/Spoons/HSearch.spoon/resources/chrome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/hammerspoon/Spoons/HSearch.spoon/resources/chrome.png -------------------------------------------------------------------------------- /hammerspoon/Spoons/HSearch.spoon/resources/emoji.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/hammerspoon/Spoons/HSearch.spoon/resources/emoji.png -------------------------------------------------------------------------------- /hammerspoon/Spoons/HSearch.spoon/resources/justnote.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/hammerspoon/Spoons/HSearch.spoon/resources/justnote.png -------------------------------------------------------------------------------- /hammerspoon/Spoons/HSearch.spoon/resources/menus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/hammerspoon/Spoons/HSearch.spoon/resources/menus.png -------------------------------------------------------------------------------- /hammerspoon/Spoons/HSearch.spoon/resources/safari.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/hammerspoon/Spoons/HSearch.spoon/resources/safari.png -------------------------------------------------------------------------------- /hammerspoon/Spoons/HSearch.spoon/resources/tabs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/hammerspoon/Spoons/HSearch.spoon/resources/tabs.png -------------------------------------------------------------------------------- /hammerspoon/Spoons/HSearch.spoon/resources/taskkill.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/hammerspoon/Spoons/HSearch.spoon/resources/taskkill.png -------------------------------------------------------------------------------- /hammerspoon/Spoons/HSearch.spoon/resources/thesaurus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/hammerspoon/Spoons/HSearch.spoon/resources/thesaurus.png -------------------------------------------------------------------------------- /hammerspoon/Spoons/HSearch.spoon/resources/time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/hammerspoon/Spoons/HSearch.spoon/resources/time.png -------------------------------------------------------------------------------- /hammerspoon/Spoons/HSearch.spoon/resources/v2ex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/hammerspoon/Spoons/HSearch.spoon/resources/v2ex.png -------------------------------------------------------------------------------- /hammerspoon/Spoons/HSearch.spoon/resources/youdao.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/hammerspoon/Spoons/HSearch.spoon/resources/youdao.png -------------------------------------------------------------------------------- /hammerspoon/Spoons/KSheet.spoon/docs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Constant" : [ 4 | 5 | ], 6 | "submodules" : [ 7 | 8 | ], 9 | "Function" : [ 10 | 11 | ], 12 | "Variable" : [ 13 | 14 | ], 15 | "stripped_doc" : [ 16 | 17 | ], 18 | "desc" : "Keybindings cheatsheet for current application", 19 | "Deprecated" : [ 20 | 21 | ], 22 | "type" : "Module", 23 | "Constructor" : [ 24 | 25 | ], 26 | "doc" : "Keybindings cheatsheet for current application\n\nDownload: [https:\/\/github.com\/Hammerspoon\/Spoons\/raw\/master\/Spoons\/KSheet.spoon.zip](https:\/\/github.com\/Hammerspoon\/Spoons\/raw\/master\/Spoons\/KSheet.spoon.zip)", 27 | "Method" : [ 28 | { 29 | "doc" : "Show current application's keybindings in a webview", 30 | "name" : "show", 31 | "desc" : "Show current application's keybindings in a webview", 32 | "stripped_doc" : [ 33 | "Show current application's keybindings in a webview" 34 | ], 35 | "notes" : [ 36 | 37 | ], 38 | "signature" : "KSheet:show()", 39 | "type" : "Method", 40 | "returns" : [ 41 | 42 | ], 43 | "def" : "KSheet:show()", 44 | "parameters" : [ 45 | 46 | ] 47 | }, 48 | { 49 | "doc" : "Hide the cheatsheet webview", 50 | "name" : "hide", 51 | "desc" : "Hide the cheatsheet webview", 52 | "stripped_doc" : [ 53 | "Hide the cheatsheet webview" 54 | ], 55 | "notes" : [ 56 | 57 | ], 58 | "signature" : "KSheet:hide()", 59 | "type" : "Method", 60 | "returns" : [ 61 | 62 | ], 63 | "def" : "KSheet:hide()", 64 | "parameters" : [ 65 | 66 | ] 67 | } 68 | ], 69 | "Command" : [ 70 | 71 | ], 72 | "Field" : [ 73 | 74 | ], 75 | "items" : [ 76 | { 77 | "doc" : "Hide the cheatsheet webview", 78 | "name" : "hide", 79 | "desc" : "Hide the cheatsheet webview", 80 | "stripped_doc" : [ 81 | "Hide the cheatsheet webview" 82 | ], 83 | "notes" : [ 84 | 85 | ], 86 | "signature" : "KSheet:hide()", 87 | "type" : "Method", 88 | "returns" : [ 89 | 90 | ], 91 | "def" : "KSheet:hide()", 92 | "parameters" : [ 93 | 94 | ] 95 | }, 96 | { 97 | "doc" : "Show current application's keybindings in a webview", 98 | "name" : "show", 99 | "desc" : "Show current application's keybindings in a webview", 100 | "stripped_doc" : [ 101 | "Show current application's keybindings in a webview" 102 | ], 103 | "notes" : [ 104 | 105 | ], 106 | "signature" : "KSheet:show()", 107 | "type" : "Method", 108 | "returns" : [ 109 | 110 | ], 111 | "def" : "KSheet:show()", 112 | "parameters" : [ 113 | 114 | ] 115 | } 116 | ], 117 | "name" : "KSheet" 118 | } 119 | ] -------------------------------------------------------------------------------- /hammerspoon/Spoons/PomodoroTimer.spoon/init.lua: -------------------------------------------------------------------------------- 1 | local obj = {} 2 | obj.__index = obj 3 | 4 | -- Metadata 5 | obj.name = "PomodoroTimer" 6 | obj.version = "1.0" 7 | obj.author = "Ein Verne" 8 | obj.homepage = "https://github.com/einverne/dotfiles" 9 | obj.license = "MIT - https://opensource.org/licenses/MIT" 10 | 11 | -- 设置默认值 12 | obj.workDuration = 25 * 60 -- 25分钟工作时间 13 | obj.breakDuration = 5 * 60 -- 5分钟休息时间 14 | obj.timer = nil 15 | obj.isWorking = true 16 | 17 | obj.startSound = hs.sound.getByName("Submarine") -- 开始声音 18 | obj.endSound = hs.sound.getByName("Glass") -- 结束声音 19 | 20 | function obj:toggle() 21 | if self.timer == nil then 22 | self:start() 23 | log.d("PomodoroTimer started") 24 | else 25 | self:stop() 26 | log.d("PomodoroTimer stopped") 27 | end 28 | end 29 | 30 | function obj:start() 31 | log.d("PomodoroTimer start") 32 | hs.alert.show("番茄时钟已启动", 2) 33 | self.startSound:play() -- 播放开始声音 34 | self:startTimer() 35 | end 36 | 37 | function obj:startTimer() 38 | local duration = self.isWorking and self.workDuration or self.breakDuration 39 | self.timer = hs.timer.doAfter(duration, function() 40 | self.endSound:play() -- 播放结束声音 41 | if self.isWorking then 42 | hs.alert.show("工作时间结束,开始休息!", 5) 43 | hs.notify.new({title="番茄时钟", informativeText="工作时间结束,开始休息!"}):send() 44 | self.isWorking = false 45 | else 46 | hs.alert.show("休息时间结束,开始工作!", 5) 47 | hs.notify.new({title="番茄时钟", informativeText="休息时间结束,开始工作!"}):send() 48 | self.isWorking = true 49 | end 50 | self:startTimer() -- 开始下一个计时周期 51 | end) 52 | end 53 | 54 | function obj:stop() 55 | if self.timer then 56 | self.timer:stop() 57 | self.timer = nil 58 | end 59 | self.isWorking = true -- 重置为工作状态 60 | hs.alert.show("番茄时钟已停止", 2) 61 | self.endSound:play() -- 播放结束声音 62 | end 63 | 64 | return obj -------------------------------------------------------------------------------- /hammerspoon/Spoons/ReloadConfiguration.spoon/docs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Command": [], 4 | "Constant": [], 5 | "Constructor": [], 6 | "Deprecated": [], 7 | "Field": [], 8 | "Function": [], 9 | "Method": [ 10 | { 11 | "def": "ReloadConfiguration:bindHotkeys(mapping)", 12 | "desc": "Binds hotkeys for ReloadConfiguration", 13 | "doc": "Binds hotkeys for ReloadConfiguration\n\nParameters:\n * mapping - A table containing hotkey modifier/key details for the following items:\n * reloadConfiguration - This will cause the configuration to be reloaded", 14 | "name": "bindHotkeys", 15 | "parameters": [ 16 | " * mapping - A table containing hotkey modifier/key details for the following items:", 17 | " * reloadConfiguration - This will cause the configuration to be reloaded" 18 | ], 19 | "signature": "ReloadConfiguration:bindHotkeys(mapping)", 20 | "stripped_doc": "", 21 | "type": "Method" 22 | }, 23 | { 24 | "def": "ReloadConfiguration:start()", 25 | "desc": "Start ReloadConfiguration", 26 | "doc": "Start ReloadConfiguration\n\nParameters:\n * None", 27 | "name": "start", 28 | "parameters": [ 29 | " * None" 30 | ], 31 | "signature": "ReloadConfiguration:start()", 32 | "stripped_doc": "", 33 | "type": "Method" 34 | } 35 | ], 36 | "Variable": [ 37 | { 38 | "def": "ReloadConfiguration.watch_paths", 39 | "desc": "List of directories to watch for changes, defaults to hs.configdir", 40 | "doc": "List of directories to watch for changes, defaults to hs.configdir", 41 | "name": "watch_paths", 42 | "signature": "ReloadConfiguration.watch_paths", 43 | "stripped_doc": "", 44 | "type": "Variable" 45 | } 46 | ], 47 | "desc": "Adds a hotkey to reload the hammerspoon configuration, and a pathwatcher to automatically reload on changes.", 48 | "doc": "Adds a hotkey to reload the hammerspoon configuration, and a pathwatcher to automatically reload on changes.\n\nDownload: [https://github.com/Hammerspoon/Spoons/raw/master/Spoons/ReloadConfiguration.spoon.zip](https://github.com/Hammerspoon/Spoons/raw/master/Spoons/ReloadConfiguration.spoon.zip)", 49 | "items": [ 50 | { 51 | "def": "ReloadConfiguration:bindHotkeys(mapping)", 52 | "desc": "Binds hotkeys for ReloadConfiguration", 53 | "doc": "Binds hotkeys for ReloadConfiguration\n\nParameters:\n * mapping - A table containing hotkey modifier/key details for the following items:\n * reloadConfiguration - This will cause the configuration to be reloaded", 54 | "name": "bindHotkeys", 55 | "parameters": [ 56 | " * mapping - A table containing hotkey modifier/key details for the following items:", 57 | " * reloadConfiguration - This will cause the configuration to be reloaded" 58 | ], 59 | "signature": "ReloadConfiguration:bindHotkeys(mapping)", 60 | "stripped_doc": "", 61 | "type": "Method" 62 | }, 63 | { 64 | "def": "ReloadConfiguration:start()", 65 | "desc": "Start ReloadConfiguration", 66 | "doc": "Start ReloadConfiguration\n\nParameters:\n * None", 67 | "name": "start", 68 | "parameters": [ 69 | " * None" 70 | ], 71 | "signature": "ReloadConfiguration:start()", 72 | "stripped_doc": "", 73 | "type": "Method" 74 | }, 75 | { 76 | "def": "ReloadConfiguration.watch_paths", 77 | "desc": "List of directories to watch for changes, defaults to hs.configdir", 78 | "doc": "List of directories to watch for changes, defaults to hs.configdir", 79 | "name": "watch_paths", 80 | "signature": "ReloadConfiguration.watch_paths", 81 | "stripped_doc": "", 82 | "type": "Variable" 83 | } 84 | ], 85 | "name": "ReloadConfiguration", 86 | "stripped_doc": "\nDownload: [https://github.com/Hammerspoon/Spoons/raw/master/Spoons/ReloadConfiguration.spoon.zip](https://github.com/Hammerspoon/Spoons/raw/master/Spoons/ReloadConfiguration.spoon.zip)", 87 | "submodules": [], 88 | "type": "Module" 89 | } 90 | ] -------------------------------------------------------------------------------- /hammerspoon/Spoons/ReloadConfiguration.spoon/init.lua: -------------------------------------------------------------------------------- 1 | --- === ReloadConfiguration === 2 | --- 3 | --- Adds a hotkey to reload the hammerspoon configuration, and a pathwatcher to automatically reload on changes. 4 | --- 5 | --- Download: [https://github.com/Hammerspoon/Spoons/raw/master/Spoons/ReloadConfiguration.spoon.zip](https://github.com/Hammerspoon/Spoons/raw/master/Spoons/ReloadConfiguration.spoon.zip) 6 | 7 | local obj = {} 8 | obj.__index = obj 9 | 10 | -- Metadata 11 | obj.name = "ReloadConfiguration" 12 | obj.version = "1.0" 13 | obj.author = "Jon Lorusso " 14 | obj.homepage = "https://github.com/Hammerspoon/Spoons" 15 | obj.license = "MIT - https://opensource.org/licenses/MIT" 16 | 17 | 18 | --- ReloadConfiguration.watch_paths 19 | --- Variable 20 | --- List of directories to watch for changes, defaults to hs.configdir 21 | obj.watch_paths = { hs.configdir } 22 | 23 | --- ReloadConfiguration:bindHotkeys(mapping) 24 | --- Method 25 | --- Binds hotkeys for ReloadConfiguration 26 | --- 27 | --- Parameters: 28 | --- * mapping - A table containing hotkey modifier/key details for the following items: 29 | --- * reloadConfiguration - This will cause the configuration to be reloaded 30 | function obj:bindHotkeys(mapping) 31 | local def = { reloadConfiguration = hs.fnutils.partial(hs.reload, self) } 32 | hs.spoons.bindHotkeysToSpec(def, mapping) 33 | end 34 | 35 | --- ReloadConfiguration:start() 36 | --- Method 37 | --- Start ReloadConfiguration 38 | --- 39 | --- Parameters: 40 | --- * None 41 | function obj:start() 42 | self.watchers = {} 43 | for _,dir in pairs(self.watch_paths) do 44 | self.watchers[dir] = hs.pathwatcher.new(dir, hs.reload):start() 45 | end 46 | return self 47 | end 48 | 49 | return obj 50 | -------------------------------------------------------------------------------- /hammerspoon/Spoons/SpeedMenu.spoon/docs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Constant" : [ 4 | 5 | ], 6 | "submodules" : [ 7 | 8 | ], 9 | "Function" : [ 10 | 11 | ], 12 | "Variable" : [ 13 | 14 | ], 15 | "stripped_doc" : [ 16 | 17 | ], 18 | "desc" : "Menubar netspeed meter", 19 | "Deprecated" : [ 20 | 21 | ], 22 | "type" : "Module", 23 | "Constructor" : [ 24 | 25 | ], 26 | "doc" : "Menubar netspeed meter\n\nDownload: [https:\/\/github.com\/Hammerspoon\/Spoons\/raw\/master\/Spoons\/SpeedMenu.spoon.zip](https:\/\/github.com\/Hammerspoon\/Spoons\/raw\/master\/Spoons\/SpeedMenu.spoon.zip)", 27 | "Field" : [ 28 | 29 | ], 30 | "Command" : [ 31 | 32 | ], 33 | "items" : [ 34 | { 35 | "doc" : "Redetect the active interface, darkmode …And redraw everything.", 36 | "desc" : "Redetect the active interface, darkmode …And redraw everything.", 37 | "parameters" : [ 38 | 39 | ], 40 | "stripped_doc" : [ 41 | "Redetect the active interface, darkmode …And redraw everything." 42 | ], 43 | "notes" : [ 44 | 45 | ], 46 | "signature" : "SpeedMenu:rescan()", 47 | "type" : "Method", 48 | "returns" : [ 49 | 50 | ], 51 | "def" : "SpeedMenu:rescan()", 52 | "name" : "rescan" 53 | } 54 | ], 55 | "Method" : [ 56 | { 57 | "doc" : "Redetect the active interface, darkmode …And redraw everything.", 58 | "desc" : "Redetect the active interface, darkmode …And redraw everything.", 59 | "parameters" : [ 60 | 61 | ], 62 | "stripped_doc" : [ 63 | "Redetect the active interface, darkmode …And redraw everything." 64 | ], 65 | "notes" : [ 66 | 67 | ], 68 | "signature" : "SpeedMenu:rescan()", 69 | "type" : "Method", 70 | "returns" : [ 71 | 72 | ], 73 | "def" : "SpeedMenu:rescan()", 74 | "name" : "rescan" 75 | } 76 | ], 77 | "name" : "SpeedMenu" 78 | } 79 | ] -------------------------------------------------------------------------------- /hammerspoon/Spoons/SpeedMenu.spoon/init.lua: -------------------------------------------------------------------------------- 1 | --- === SpeedMenu === 2 | --- 3 | --- Menubar netspeed meter 4 | --- 5 | --- Download: [https://github.com/Hammerspoon/Spoons/raw/master/Spoons/SpeedMenu.spoon.zip](https://github.com/Hammerspoon/Spoons/raw/master/Spoons/SpeedMenu.spoon.zip) 6 | 7 | local obj={} 8 | obj.__index = obj 9 | 10 | -- Metadata 11 | obj.name = "SpeedMenu" 12 | obj.version = "1.0" 13 | obj.author = "ashfinal " 14 | obj.homepage = "https://github.com/Hammerspoon/Spoons" 15 | obj.license = "MIT - https://opensource.org/licenses/MIT" 16 | 17 | function obj:init() 18 | self.menubar = hs.menubar.new() 19 | obj:rescan() 20 | end 21 | 22 | local function data_diff() 23 | local in_seq = hs.execute(obj.instr) 24 | local out_seq = hs.execute(obj.outstr) 25 | local in_diff = in_seq - obj.inseq 26 | local out_diff = out_seq - obj.outseq 27 | if in_diff/1024 > 1024 then 28 | obj.kbin = string.format("%6.2f", in_diff/1024/1024) .. ' mb/s' 29 | else 30 | obj.kbin = string.format("%6.2f", in_diff/1024) .. ' kb/s' 31 | end 32 | if out_diff/1024 > 1024 then 33 | obj.kbout = string.format("%6.2f", out_diff/1024/1024) .. ' mb/s' 34 | else 35 | obj.kbout = string.format("%6.2f", out_diff/1024) .. ' kb/s' 36 | end 37 | local disp_str = '⥄ ' .. obj.kbout .. '\n⥂ ' .. obj.kbin 38 | if obj.darkmode then 39 | obj.disp_str = hs.styledtext.new(disp_str, {font={size=9.0, color={hex="#FFFFFF"}}}) 40 | else 41 | obj.disp_str = hs.styledtext.new(disp_str, {font={size=9.0, color={hex="#000000"}}}) 42 | end 43 | obj.menubar:setTitle(obj.disp_str) 44 | obj.inseq = in_seq 45 | obj.outseq = out_seq 46 | end 47 | 48 | --- SpeedMenu:rescan() 49 | --- Method 50 | --- Redetect the active interface, darkmode …And redraw everything. 51 | --- 52 | 53 | function obj:rescan() 54 | obj.interface = hs.network.primaryInterfaces() 55 | obj.darkmode = hs.osascript.applescript('tell application "System Events"\nreturn dark mode of appearance preferences\nend tell') 56 | local menuitems_table = {} 57 | if obj.interface then 58 | -- Inspect active interface and create menuitems 59 | local interface_detail = hs.network.interfaceDetails(obj.interface) 60 | if interface_detail.AirPort then 61 | local ssid = interface_detail.AirPort.SSID 62 | table.insert(menuitems_table, { 63 | title = "SSID: " .. ssid, 64 | tooltip = "Copy SSID to clipboard", 65 | fn = function() hs.pasteboard.setContents(ssid) end 66 | }) 67 | end 68 | if interface_detail.IPv4 then 69 | local ipv4 = interface_detail.IPv4.Addresses[1] 70 | table.insert(menuitems_table, { 71 | title = "IPv4: " .. ipv4, 72 | tooltip = "Copy IPv4 to clipboard", 73 | fn = function() hs.pasteboard.setContents(ipv4) end 74 | }) 75 | end 76 | if interface_detail.IPv6 then 77 | local ipv6 = interface_detail.IPv6.Addresses[1] 78 | table.insert(menuitems_table, { 79 | title = "IPv6: " .. ipv6, 80 | tooltip = "Copy IPv6 to clipboard", 81 | fn = function() hs.pasteboard.setContents(ipv6) end 82 | }) 83 | end 84 | local macaddr = hs.execute('ifconfig ' .. obj.interface .. ' | grep ether | awk \'{print $2}\'') 85 | table.insert(menuitems_table, { 86 | title = "MAC Addr: " .. macaddr, 87 | tooltip = "Copy MAC Address to clipboard", 88 | fn = function() hs.pasteboard.setContents(macaddr) end 89 | }) 90 | -- Start watching the netspeed delta 91 | obj.instr = 'netstat -ibn | grep -e ' .. obj.interface .. ' -m 1 | awk \'{print $7}\'' 92 | obj.outstr = 'netstat -ibn | grep -e ' .. obj.interface .. ' -m 1 | awk \'{print $10}\'' 93 | 94 | obj.inseq = hs.execute(obj.instr) 95 | obj.outseq = hs.execute(obj.outstr) 96 | 97 | if obj.timer then 98 | obj.timer:stop() 99 | obj.timer = nil 100 | end 101 | obj.timer = hs.timer.doEvery(1, data_diff) 102 | end 103 | table.insert(menuitems_table, { 104 | title = "Rescan Network Interfaces", 105 | fn = function() obj:rescan() end 106 | }) 107 | obj.menubar:setTitle("⚠︎") 108 | obj.menubar:setMenu(menuitems_table) 109 | end 110 | 111 | return obj 112 | -------------------------------------------------------------------------------- /hammerspoon/Spoons/TimeFlow.spoon/docs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Constant" : [ 4 | 5 | ], 6 | "submodules" : [ 7 | 8 | ], 9 | "Function" : [ 10 | 11 | ], 12 | "Variable" : [ 13 | 14 | ], 15 | "stripped_doc" : [ 16 | 17 | ], 18 | "Deprecated" : [ 19 | 20 | ], 21 | "type" : "Module", 22 | "desc" : "A widget showing time flown in one year.", 23 | "Constructor" : [ 24 | 25 | ], 26 | "doc" : "A widget showing time flown in one year.\n\nDownload: [https:\/\/github.com\/Hammerspoon\/Spoons\/raw\/master\/Spoons\/TimeFlow.spoon.zip](https:\/\/github.com\/Hammerspoon\/Spoons\/raw\/master\/Spoons\/TimeFlow.spoon.zip)", 27 | "Field" : [ 28 | 29 | ], 30 | "items" : [ 31 | 32 | ], 33 | "Command" : [ 34 | 35 | ], 36 | "Method" : [ 37 | 38 | ], 39 | "name" : "TimeFlow" 40 | } 41 | ] -------------------------------------------------------------------------------- /hammerspoon/Spoons/TimeFlow.spoon/init.lua: -------------------------------------------------------------------------------- 1 | --- === TimeFlow === 2 | --- 3 | --- A widget showing time flown in one year. 4 | --- 5 | --- Download: [https://github.com/Hammerspoon/Spoons/raw/master/Spoons/TimeFlow.spoon.zip](https://github.com/Hammerspoon/Spoons/raw/master/Spoons/TimeFlow.spoon.zip) 6 | 7 | local obj={} 8 | obj.__index = obj 9 | 10 | -- Metadata 11 | obj.name = "TimeFlow" 12 | obj.version = "1.0" 13 | obj.author = "ashfinal " 14 | obj.homepage = "https://github.com/Hammerspoon/Spoons" 15 | obj.license = "MIT - https://opensource.org/licenses/MIT" 16 | 17 | -- Internal function used to find our location, so we know where to load files from 18 | local function script_path() 19 | local str = debug.getinfo(2, "S").source:sub(2) 20 | return str:match("(.*/)") 21 | end 22 | 23 | obj.spoonPath = script_path() 24 | 25 | local function updateElapsedCanvas() 26 | local nowtable = os.date("*t") 27 | local nowyday = nowtable.yday 28 | local nowhour = string.format("%2s", nowtable.hour) 29 | local nowmin = string.format("%2s", nowtable.min) 30 | local nowsec = string.format("%2s", nowtable.sec) 31 | local timestr = nowyday.." days "..nowhour.." hours "..nowmin.." min "..nowsec.." sec" 32 | local secs_since_epoch = os.time() 33 | local nowyear = nowtable.year 34 | local yearstartsecs_since_epoch = os.time({year=nowyear, month=1, day=1, hour=0}) 35 | local nowyear_elapsed_secs = secs_since_epoch - yearstartsecs_since_epoch 36 | local yearendsecs_since_epoch = os.time({year=nowyear+1, month=1, day=1, hour=0}) 37 | local nowyear_total_secs = yearendsecs_since_epoch - yearstartsecs_since_epoch 38 | local elapsed_percent = nowyear_elapsed_secs/nowyear_total_secs 39 | if obj.canvas:isShowing() then 40 | obj.canvas[3].text = timestr 41 | obj.canvas[6].frame.w = tostring(240/280*elapsed_percent) 42 | end 43 | end 44 | 45 | function obj:init() 46 | local cscreen = hs.screen.mainScreen() 47 | local cres = cscreen:fullFrame() 48 | obj.canvas = hs.canvas.new({ 49 | x = cres.w-280-20, 50 | y = 400, 51 | w = 280, 52 | h = 125 53 | }):show() 54 | obj.canvas:behavior(hs.canvas.windowBehaviors.canJoinAllSpaces) 55 | obj.canvas:level(hs.canvas.windowLevels.desktopIcon) 56 | -- canvas background 57 | obj.canvas[1] = { 58 | action = "fill", 59 | type = "rectangle", 60 | fillColor = {hex="#000000", alpha=0.2}, 61 | roundedRectRadii = {xRadius=5, yRadius=5}, 62 | } 63 | -- title 64 | obj.canvas[2] = { 65 | type = "text", 66 | text = "Time Elapsed", 67 | textSize = 14, 68 | textColor = {hex="#FFFFFF", alpha=0.3}, 69 | frame = { 70 | x = tostring(10/280), 71 | y = tostring(10/125), 72 | w = tostring(260/280), 73 | h = tostring(25/125), 74 | } 75 | } 76 | -- time 77 | obj.canvas[3] = { 78 | type = "text", 79 | text = "", 80 | textColor = {hex="#A6AAC3"}, 81 | textSize = 17, 82 | textAlignment = "center", 83 | frame = { 84 | x = tostring(0/280), 85 | y = tostring(35/125), 86 | w = tostring(280/280), 87 | h = tostring(25/125), 88 | } 89 | } 90 | -- indicator background 91 | obj.canvas[4] = { 92 | type = "image", 93 | image = hs.image.imageFromPath(self.spoonPath .. "/timebg.png"), 94 | frame = { 95 | x = tostring(10/280), 96 | y = tostring(65/125), 97 | w = tostring(260/280), 98 | h = tostring(50/125), 99 | } 100 | } 101 | -- light indicator 102 | obj.canvas[5] = { 103 | action = "fill", 104 | type = "rectangle", 105 | fillColor = {hex="#FFFFFF", alpha=0.2}, 106 | frame = { 107 | x = tostring(20/280), 108 | y = tostring(75/125), 109 | w = tostring(240/280), 110 | h = tostring(20/125), 111 | } 112 | } 113 | -- indicator mask 114 | obj.canvas[6] = { 115 | action = "fill", 116 | type = "rectangle", 117 | frame = { 118 | x = tostring(20/280), 119 | y = tostring(75/125), 120 | w = tostring(240/280), 121 | h = tostring(20/125), 122 | } 123 | } 124 | -- color indicator 125 | obj.canvas[7] = { 126 | action = "fill", 127 | type = "rectangle", 128 | frame = { 129 | x = tostring(20/280), 130 | y = tostring(75/125), 131 | w = tostring(240/280), 132 | h = tostring(20/125), 133 | }, 134 | fillGradient="linear", 135 | fillGradientColors = { 136 | {hex = "#00A0F7"}, 137 | {hex = "#92D2E5"}, 138 | {hex = "#4BE581"}, 139 | {hex = "#EAF25E"}, 140 | {hex = "#F4CA55"}, 141 | {hex = "#E04E4E"}, 142 | }, 143 | } 144 | obj.canvas[7].compositeRule = "sourceAtop" 145 | 146 | if obj.timer == nil then 147 | obj.timer = hs.timer.doEvery(1, function() updateElapsedCanvas() end) 148 | else 149 | obj.timer:start() 150 | end 151 | end 152 | 153 | return obj 154 | -------------------------------------------------------------------------------- /hammerspoon/Spoons/TimeFlow.spoon/timebg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/hammerspoon/Spoons/TimeFlow.spoon/timebg.png -------------------------------------------------------------------------------- /hammerspoon/Spoons/UnsplashZ.spoon/docs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "Constant" : [ 4 | 5 | ], 6 | "submodules" : [ 7 | 8 | ], 9 | "Function" : [ 10 | 11 | ], 12 | "Variable" : [ 13 | 14 | ], 15 | "stripped_doc" : [ 16 | 17 | ], 18 | "Deprecated" : [ 19 | 20 | ], 21 | "desc" : "Use unsplash images as wallpaper", 22 | "type" : "Module", 23 | "Constructor" : [ 24 | 25 | ], 26 | "doc" : "Use unsplash images as wallpaper\n\nDownload: [https:\/\/github.com\/Hammerspoon\/Spoons\/raw\/master\/Spoons\/UnsplashZ.spoon.zip](https:\/\/github.com\/Hammerspoon\/Spoons\/raw\/master\/Spoons\/UnsplashZ.spoon.zip)", 27 | "Method" : [ 28 | 29 | ], 30 | "Command" : [ 31 | 32 | ], 33 | "items" : [ 34 | 35 | ], 36 | "Field" : [ 37 | 38 | ], 39 | "name" : "UnsplashZ" 40 | } 41 | ] -------------------------------------------------------------------------------- /hammerspoon/Spoons/UnsplashZ.spoon/init.lua: -------------------------------------------------------------------------------- 1 | --- === UnsplashZ === 2 | --- 3 | --- Use unsplash images as wallpaper 4 | --- 5 | --- Download: [https://github.com/Hammerspoon/Spoons/raw/master/Spoons/UnsplashZ.spoon.zip](https://github.com/Hammerspoon/Spoons/raw/master/Spoons/UnsplashZ.spoon.zip) 6 | 7 | local obj={} 8 | obj.__index = obj 9 | 10 | -- Metadata 11 | obj.name = "UnsplashZ" 12 | obj.version = "1.0" 13 | obj.author = "ashfinal " 14 | obj.homepage = "https://github.com/Hammerspoon/Spoons" 15 | obj.license = "MIT - https://opensource.org/licenses/MIT" 16 | 17 | local function curl_callback(exitCode, stdOut, stdErr) 18 | if exitCode == 0 then 19 | obj.task = nil 20 | obj.last_pic = hs.http.urlParts(obj.pic_url).lastPathComponent 21 | local localpath = os.getenv("HOME") .. "/.Trash/" .. hs.http.urlParts(obj.pic_url).lastPathComponent 22 | 23 | hs.screen.mainScreen():desktopImageURL("file://" .. localpath) 24 | else 25 | print(stdOut, stdErr) 26 | end 27 | end 28 | 29 | local function unsplashRequest() 30 | local user_agent_str = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/603.2.4 (KHTML, like Gecko) Version/10.1.1 Safari/603.2.4" 31 | obj.pic_url = hs.execute([[ /usr/bin/curl 'https://source.unsplash.com/1600x900/?nature' | perl -ne ' print "$1" if /href="([^"]+)"/ ' ]]) 32 | if obj.last_pic ~= obj.pic_url then 33 | if obj.task then 34 | obj.task:terminate() 35 | obj.task = nil 36 | end 37 | local localpath = os.getenv("HOME") .. "/.Trash/" .. hs.http.urlParts(obj.pic_url).lastPathComponent 38 | obj.task = hs.task.new("/usr/bin/curl", curl_callback, {"-A", user_agent_str, obj.pic_url, "-o", localpath}) 39 | obj.task:start() 40 | end 41 | end 42 | 43 | function obj:init() 44 | if obj.timer == nil then 45 | obj.timer = hs.timer.doEvery(3*60*60, function() unsplashRequest() end) 46 | obj.timer:setNextTrigger(5) 47 | else 48 | obj.timer:start() 49 | end 50 | end 51 | 52 | return obj 53 | -------------------------------------------------------------------------------- /hammerspoon/autoscript.lua: -------------------------------------------------------------------------------- 1 | -- log = hs.logger.new('autoscript', 'debug') 2 | -- 3 | -- function taskCallback(exitCode, stdOut, stdErr) 4 | -- log.i("task call back ", exitCode, stdOut, stdErr) 5 | -- end 6 | -- 7 | -- function runAutoScripts() 8 | -- args = {"-c", "/Users/einverne/Sync/wiki/auto-push.sh"} 9 | -- autoCommitTask = hs.task.new("/bin/bash", taskCallback, args) 10 | -- autoCommitTask:setWorkingDirectory("/Users/einverne/Sync/wiki/") 11 | -- autoCommitTask:start() 12 | -- end 13 | -- 14 | -- myTimer = hs.timer.doEvery(7200, runAutoScripts) 15 | -- myTimer:start() 16 | -------------------------------------------------------------------------------- /hammerspoon/config-example.lua: -------------------------------------------------------------------------------- 1 | -- Specify Spoons which will be loaded 2 | hspoon_list = { 3 | "AClock", 4 | "BingDaily", 5 | -- "Calendar", 6 | "CircleClock", 7 | "ClipShow", 8 | "CountDown", 9 | "FnMate", 10 | "HCalendar", 11 | "HSaria2", 12 | "HSearch", 13 | -- "KSheet", 14 | "SpeedMenu", 15 | -- "TimeFlow", 16 | -- "UnsplashZ", 17 | "WinWin", 18 | } 19 | 20 | -- appM environment keybindings. Bundle `id` is prefered, but application `name` will be ok. 21 | hsapp_list = { 22 | {key = 'a', name = 'Atom'}, 23 | {key = 'c', id = 'com.google.Chrome'}, 24 | {key = 'e', name = 'Emacs'}, 25 | {key = 'f', name = 'Finder'}, 26 | {key = 'i', name = 'iTerm'}, 27 | {key = 'k', name = 'KeyCastr'}, 28 | {key = 'l', name = 'Sublime Text'}, 29 | {key = 'm', name = 'MacVim'}, 30 | {key = 'o', name = 'LibreOffice'}, 31 | {key = 'p', name = 'mpv'}, 32 | {key = 'r', name = 'VimR'}, 33 | {key = 's', name = 'Safari'}, 34 | {key = 't', name = 'Terminal'}, 35 | {key = 'v', id = 'com.apple.ActivityMonitor'}, 36 | {key = 'y', id = 'com.apple.systempreferences'}, 37 | } 38 | 39 | -- Modal supervisor keybinding, which can be used to temporarily disable ALL modal environments. 40 | hsupervisor_keys = {{"cmd", "shift", "ctrl"}, "Q"} 41 | 42 | -- Reload Hammerspoon configuration 43 | hsreload_keys = {{"cmd", "shift", "ctrl"}, "R"} 44 | 45 | -- Toggle help panel of this configuration. 46 | hshelp_keys = {{"alt", "shift"}, "/"} 47 | 48 | -- aria2 RPC host address 49 | hsaria2_host = "http://localhost:6800/jsonrpc" 50 | -- aria2 RPC host secret 51 | hsaria2_secret = "token" 52 | 53 | ---------------------------------------------------------------------------------------------------- 54 | -- Those keybindings below could be disabled by setting to {"", ""} or {{}, ""} 55 | 56 | -- Window hints keybinding: Focuse to any window you want 57 | hswhints_keys = {"alt", "tab"} 58 | 59 | -- appM environment keybinding: Application Launcher 60 | hsappM_keys = {"alt", "A"} 61 | 62 | -- clipshowM environment keybinding: System clipboard reader 63 | hsclipsM_keys = {"alt", "C"} 64 | 65 | -- Toggle the display of aria2 frontend 66 | hsaria2_keys = {"alt", "D"} 67 | 68 | -- Launch Hammerspoon Search 69 | hsearch_keys = {"alt", "G"} 70 | 71 | -- Read Hammerspoon and Spoons API manual in default browser 72 | hsman_keys = {"alt", "H"} 73 | 74 | -- countdownM environment keybinding: Visual countdown 75 | hscountdM_keys = {"alt", "I"} 76 | 77 | -- Lock computer's screen 78 | hslock_keys = {"alt", "L"} 79 | 80 | -- resizeM environment keybinding: Windows manipulation 81 | hsresizeM_keys = {"alt", "R"} 82 | 83 | -- cheatsheetM environment keybinding: Cheatsheet copycat 84 | hscheats_keys = {"alt", "S"} 85 | 86 | -- Show digital clock above all windows 87 | hsaclock_keys = {"alt", "T"} 88 | 89 | -- Type the URL and title of the frontmost web page open in Google Chrome or Safari. 90 | hstype_keys = {"alt", "V"} 91 | 92 | -- Toggle Hammerspoon console 93 | hsconsole_keys = {"alt", "Z"} 94 | -------------------------------------------------------------------------------- /hammerspoon/ime.lua: -------------------------------------------------------------------------------- 1 | log = hs.logger.new('ime', 'debug') 2 | 3 | -- you can find the source id by running `hs.keycodes.currentSourceID()` 4 | 5 | local function zh() 6 | hs.keycodes.currentSourceID("im.rime.inputmethod.Squirrel.Hans") 7 | end 8 | 9 | local function en() 10 | hs.keycodes.currentSourceID("com.apple.keylayout.ABC") 11 | end 12 | 13 | local function ko() 14 | hs.keycodes.currentSourceID("com.apple.inputmethod.Korean.HNCRomaja") 15 | end 16 | 17 | local function jp() 18 | hs.keycodes.currentSourceID("com.apple.inputmethod.Japanese.Japanese") 19 | end 20 | 21 | -- app to expected ime config 22 | local appName2Ime = { 23 | { 'Finder', 'en' }, 24 | { 'Bitwarden', 'en' }, 25 | { 'Code', 'en' }, 26 | { 'Dash', 'zh' }, 27 | { 'iTerm', 'zh' }, 28 | { 'GoldenDict', 'zh' }, 29 | { 'GoldenDict-ng', 'zh' }, 30 | { 'Google Chrome', 'zh' }, 31 | { 'IntelliJ IDEA', 'en' }, 32 | { 'KakaoTalk', 'ko' }, 33 | { 'kitty', 'en' }, 34 | { 'NeteaseMusic', 'zh' }, 35 | { 'MacVim', 'en' }, 36 | { 'Raycast', 'en' }, 37 | { 'System Preferences', 'en' }, 38 | { 'SmartGit', 'en' }, 39 | { 'MindNode', 'zh' }, 40 | { 'Obsidian', 'zh' }, 41 | { 'Postman', 'en' }, 42 | { 'PyCharm', 'en' }, 43 | { 'Vivaldi', 'zh' }, 44 | { 'wechatwebdevtools', 'zh' }, 45 | { 'Warp', 'en' }, 46 | { 'WeChat', 'zh' }, 47 | { 'Whistle', 'en' }, 48 | { 'Xcode', 'zh' }, 49 | { 'Ghostty', 'en'}, 50 | { 'AeroSpace', 'en'} 51 | } 52 | 53 | function updateFocusAppInputMethod() 54 | local focusAppName = hs.window.frontmostWindow():application():name() 55 | if focusAppName == nil then 56 | return 57 | end 58 | -- hs.alert.show(focusAppPath) 59 | for index, app in pairs(appName2Ime) do 60 | local appName = app[1] 61 | local expectedIme = app[2] 62 | 63 | if focusAppName == appName then 64 | if expectedIme == 'en' then 65 | en() 66 | elseif expectedIme == 'ko' then 67 | ko() 68 | elseif expectedIme == 'jp' then 69 | jp() 70 | else 71 | zh() 72 | end 73 | break 74 | end 75 | end 76 | end 77 | 78 | -- helper hotkey to figure out the app path and name of current focused window 79 | hs.hotkey.bind({ 'ctrl', 'cmd' }, ".", function() 80 | hs.alert.show("App path: " 81 | .. hs.window.focusedWindow():application():path() 82 | .. "\n" 83 | .. "App name: " 84 | .. hs.window.focusedWindow():application():name() 85 | .. "\n" 86 | .. "Bundle ID: " 87 | .. hs.window.focusedWindow():application():bundleID() 88 | .. "\n" 89 | .. "IM source id: " 90 | .. hs.keycodes.currentSourceID()) 91 | end) 92 | 93 | -- Handle cursor focus and application's screen manage. 94 | function applicationWatcher(appName, eventType, appObject) 95 | if (eventType == hs.application.watcher.activated or eventType == hs.application.watcher.launched) then 96 | updateFocusAppInputMethod() 97 | end 98 | end 99 | 100 | appWatcher = hs.application.watcher.new(applicationWatcher) 101 | appWatcher:start() 102 | 103 | -------------------------------------------------------------------------------- /hammerspoon/usb.lua: -------------------------------------------------------------------------------- 1 | 2 | log = hs.logger.new('autoscript', 'debug') 3 | 4 | function keyboardCallback(data) 5 | -- if data.eventType == "added" then 6 | -- log.i(data.productName .. data.vendorName .. data.vendorID .. data.productID .. "added") 7 | -- end 8 | -- if data.eventType == "removed" then 9 | -- log.i(data.productName .. data.vendorName .. data.vendorID .. data.productID .. "removed") 10 | -- end 11 | log.i(data) 12 | end 13 | 14 | local keyboardWatcher = hs.usb.watcher.new(keyboardCallback) 15 | keyboardWatcher:start() 16 | -------------------------------------------------------------------------------- /init/init_cinnamon_applets.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash - 2 | 3 | set -o nounset # Treat unset variables as an error 4 | 5 | DESKTOP_CAPTURE=v1.17 6 | 7 | # desktop capture 8 | sudo apt install -y byzanz 9 | 10 | TEMP_FORLDER=capture 11 | wget https://github.com/rjanja/desktop-capture/archive/${DESKTOP_CAPTURE}.tar.gz 12 | mkdir -p ${TEMP_FORLDER} 13 | tar xzvf ${DESKTOP_CAPTURE}.tar.gz -C ${TEMP_FORLDER} 14 | mv -v ${TEMP_FORLDER}/capture@rjanja ~/.local/share/cinnamon/applets/ 15 | rm -rf ${DESKTOP_CAPTURE}.tar.gz 16 | rm -rf ${TEMP_FORLDER} 17 | -------------------------------------------------------------------------------- /init/init_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash - 2 | set -o nounset # Treat unset variables as an error 3 | 4 | # install 5 | sudo apt-get install zsh 6 | sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)" 7 | chsh -s $(which zsh) 8 | 9 | # pyenv 10 | curl -L https://raw.githubusercontent.com/pyenv/pyenv-installer/master/bin/pyenv-installer | bash 11 | 12 | # pyenv required 13 | sudo apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev \ 14 | libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev libncursesw5-dev \ 15 | xz-utils tk-dev libffi-dev liblzma-dev python-openssl 16 | 17 | # zeal 18 | sudo add-apt-repository -y ppa:zeal-developers/ppa 19 | -------------------------------------------------------------------------------- /init/init_server.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash - 2 | 3 | set -o nounset # Treat unset variables as an error 4 | 5 | VIM_SERVER="https://raw.githubusercontent.com/wklken/vim-for-server/master/vimrc" 6 | # sh -c "$(curl -fsSL https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh)" 7 | echo "Are you sure to install vim for server" 8 | read -n 1 is_server 9 | if [ "$is_server" == "Y" ] || [ "$is_server" == "y" ]; then 10 | curl $VIM_SERVER > $HOME/.vimrc 11 | exit 12 | fi 13 | 14 | -------------------------------------------------------------------------------- /install: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # init dotbot and it's submodules 3 | set -e 4 | 5 | CONFIG="config/install.conf.yml" 6 | DOTBOT_DIR="dotbot" 7 | 8 | DOTBOT_BIN="bin/dotbot" 9 | BASEDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 10 | 11 | cd "${BASEDIR}" 12 | git -C "${DOTBOT_DIR}" submodule sync --quiet --recursive 13 | git submodule update --init --recursive "${DOTBOT_DIR}" 14 | 15 | "${BASEDIR}/${DOTBOT_DIR}/${DOTBOT_BIN}" -d "${BASEDIR}" \ 16 | --verbose \ 17 | --plugin-dir dotbot-brew \ 18 | -c "${CONFIG}" "${@}" 19 | -------------------------------------------------------------------------------- /kitty/base16-solarized-dark-256.conf: -------------------------------------------------------------------------------- 1 | # Base16 Solarized Dark - kitty color config 2 | # Scheme by Ethan Schoonover (modified by aramisgithub) 3 | background #002b36 4 | foreground #93a1a1 5 | selection_background #93a1a1 6 | selection_foreground #002b36 7 | url_color #839496 8 | cursor #93a1a1 9 | active_border_color #657b83 10 | inactive_border_color #073642 11 | active_tab_background #002b36 12 | active_tab_foreground #93a1a1 13 | inactive_tab_background #073642 14 | inactive_tab_foreground #839496 15 | tab_bar_background #073642 16 | 17 | # normal 18 | color0 #002b36 19 | color1 #dc322f 20 | color2 #859900 21 | color3 #b58900 22 | color4 #268bd2 23 | color5 #6c71c4 24 | color6 #2aa198 25 | color7 #93a1a1 26 | 27 | # bright 28 | color8 #657b83 29 | color9 #dc322f 30 | color10 #859900 31 | color11 #b58900 32 | color12 #268bd2 33 | color13 #6c71c4 34 | color14 #2aa198 35 | color15 #fdf6e3 36 | 37 | # extended base16 colors 38 | color16 #cb4b16 39 | color17 #d33682 40 | color18 #073642 41 | color19 #586e75 42 | color20 #839496 43 | color21 #eee8d5 44 | -------------------------------------------------------------------------------- /mac_bootstrap.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # https://github.com/crispgm/dotfiles/blob/master/bootstrap 3 | 4 | set -e 5 | 6 | echo "Install Homebrew" 7 | if test ! $(which brew); then 8 | echo "Installing homebrew..." 9 | /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 10 | fi 11 | 12 | echo "Setup hostname" 13 | sudo scutil --set HostName mac 14 | 15 | echo "Install with Brew Bundle" 16 | set +e 17 | brew cleanup 18 | brew uninstall openssl 19 | brew bundle 20 | set -e 21 | 22 | echo "Setup workspace" 23 | mkdir -p ~/Git/ 24 | 25 | echo "Setup Git" 26 | ln -s ./git/work.gitconfig ~/Git/.gitconfig 27 | if [ -f $HOME/.gitconfig ]; then 28 | cat $HOME/.gitconfig 29 | mv $HOME/.gitconfig $HOME/.gitconfig.bak 30 | fi 31 | ln -s $PWD/git/global.gitconfig $HOME/.gitconfig 32 | 33 | # echo "Setup Bash" 34 | # sudo cp ./motd /etc/motd 35 | # cp ./bash/.bashrc ~/.bashrc 36 | # cp ./bash/.bash_profile ~/.bash_profile 37 | 38 | echo "Setup Zsh" 39 | sudo sh -c 'echo /usr/local/bin/zsh >> /etc/shells' 40 | sudo chsh -s $(which zsh) 41 | 42 | echo "Setup applications" 43 | echo "- fzf" 44 | $(brew --prefix)/opt/fzf/install 45 | # echo "- neovim" 46 | # ./neovim/neovim 47 | -------------------------------------------------------------------------------- /mackup/mackup.cfg: -------------------------------------------------------------------------------- 1 | [storage] 2 | engine = file_system 3 | path = Sync 4 | directory = Mackup 5 | 6 | [applications_to_sync] 7 | macosx 8 | 9 | [applications_to_ignore] 10 | karabiner-elements 11 | 12 | -------------------------------------------------------------------------------- /pulsedmedia/README.md: -------------------------------------------------------------------------------- 1 | # App install on Pulsed Media Seedbox 2 | 3 | 4 | 5 | curl https://gist.githubusercontent.com/gsj1377/a7eb727e079a1cefc9baff4e130d8900/raw/app-installation.sh | bash && source ~/.bashrc 6 | -------------------------------------------------------------------------------- /python_scripts/clone-all-gitlab.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | 4 | # https://stackoverflow.com/a/57412415/1820217 5 | # pip install python-gitlab 6 | # python clone-all-gitlab.py GITLAB_HOST GROUP_ID PERSONAL_ACCESS_TOKEN 7 | import os 8 | import sys 9 | import gitlab 10 | import subprocess 11 | 12 | glab = gitlab.Gitlab(f'https://{sys.argv[1]}', f'{sys.argv[3]}') 13 | groups = glab.groups.list() 14 | groupname = sys.argv[2] 15 | for group in groups: 16 | if group.name == groupname: 17 | projects = group.projects.list(all=True) 18 | 19 | for repo in projects: 20 | command = f'git clone {repo.ssh_url_to_repo}' 21 | process = subprocess.Popen(command, stdout=subprocess.PIPE, shell=True) 22 | output, _ = process.communicate() 23 | process.wait() 24 | -------------------------------------------------------------------------------- /script/byzanz-record-gui.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # AUTHOR: (c) Rob W 2012, modified by MHC (http://askubuntu.com/users/81372/mhc) 4 | # NAME: GIFRecord 0.1 5 | # DESCRIPTION: A script to record GIF screencasts. 6 | # LICENSE: GNU GPL v3 (http://www.gnu.org/licenses/gpl.html) 7 | # DEPENDENCIES: byzanz,gdialog,notify-send (install via sudo add-apt-repository ppa:fossfreedom/byzanz; sudo apt-get update && sudo apt-get install byzanz gdialog notify-osd) 8 | 9 | # Time and date 10 | TIME=$(date +"%Y-%m-%d_%H%M%S") 11 | 12 | # Delay before starting 13 | DELAY=5 14 | 15 | # Standard screencast folder 16 | FOLDER="$HOME/Pictures" 17 | 18 | # Default recording duration 19 | DEFDUR=10 20 | 21 | # Sound notification to let one know when recording is about to start (and ends) 22 | beep() { 23 | paplay /usr/share/sounds/freedesktop/stereo/message-new-instant.oga & 24 | } 25 | 26 | # Custom recording duration as set by user 27 | USERDUR=$(gdialog --title "Duration?" --inputbox "Please enter the screencast duration in seconds" 200 100 2>&1) 28 | 29 | # Duration and output file 30 | if [ $USERDUR -gt 0 ]; then 31 | D=$USERDUR 32 | else 33 | D=$DEFDUR 34 | fi 35 | 36 | # Window geometry 37 | XWININFO=$(xwininfo) 38 | read X < <(awk -F: '/Absolute upper-left X/{print $2}' <<< "$XWININFO") 39 | read Y < <(awk -F: '/Absolute upper-left Y/{print $2}' <<< "$XWININFO") 40 | read W < <(awk -F: '/Width/{print $2}' <<< "$XWININFO") 41 | read H < <(awk -F: '/Height/{print $2}' <<< "$XWININFO") 42 | 43 | # Notify the user of recording time and delay 44 | notify-send "GIFRecorder" "Recording duration set to $D seconds. Recording will start in $DELAY seconds." 45 | 46 | #Actual recording 47 | sleep $DELAY 48 | beep 49 | byzanz-record -c --verbose --delay=0 --duration=$D --x=$X --y=$Y --width=$W --height=$H "$FOLDER/GIFrecord_$TIME.gif" 50 | beep 51 | 52 | # Notify the user of end of recording. 53 | notify-send "GIFRecorder" "Screencast saved to $FOLDER/GIFrecord_$TIME.gif" 54 | -------------------------------------------------------------------------------- /script/byzanz-record-region.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash - 2 | 3 | #!/bin/bash 4 | 5 | # Delay before starting 6 | DELAY=10 7 | 8 | # Sound notification to let one know when recording is about to start (and ends) 9 | beep() { 10 | paplay /usr/share/sounds/KDE-Im-Irc-Event.ogg & 11 | } 12 | 13 | # Duration and output file 14 | if [ $# -gt 0 ]; then 15 | D="--duration=$@" 16 | else 17 | echo Default recording duration 10s to /tmp/recorded.gif 18 | D="--duration=10 /tmp/recorded.gif" 19 | fi 20 | 21 | # xrectsel from https://github.com/lolilolicon/xrectsel 22 | ARGUMENTS=$(xrectsel "--x=%x --y=%y --width=%w --height=%h") || exit -1 23 | 24 | echo Delaying $DELAY seconds. After that, byzanz will start 25 | for (( i=$DELAY; i>0; --i )) ; do 26 | echo $i 27 | sleep 1 28 | done 29 | beep 30 | byzanz-record --verbose --delay=0 ${ARGUMENTS} $D 31 | beep 32 | -------------------------------------------------------------------------------- /script/byzanz-record-window.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Example: byzanz-record-window 30 -c output.gif 4 | 5 | # Delay before starting 6 | DELAY=10 7 | 8 | # Sound notification to let one know when recording is about to start (and ends) 9 | beep() { 10 | paplay /usr/share/sounds/KDE-Im-Irc-Event.ogg & 11 | } 12 | 13 | # Duration and output file 14 | if [ $# -gt 0 ]; then 15 | D="--duration=$@" 16 | else 17 | echo Default recording duration 10s to /tmp/recorded.gif 18 | D="--duration=10 /tmp/recorded.gif" 19 | fi 20 | XWININFO=$(xwininfo) 21 | read X < <(awk -F: '/Absolute upper-left X/{print $2}' <<< "$XWININFO") 22 | read Y < <(awk -F: '/Absolute upper-left Y/{print $2}' <<< "$XWININFO") 23 | read W < <(awk -F: '/Width/{print $2}' <<< "$XWININFO") 24 | read H < <(awk -F: '/Height/{print $2}' <<< "$XWININFO") 25 | 26 | echo Delaying $DELAY seconds. After that, byzanz will start 27 | for (( i=$DELAY; i>0; --i )) ; do 28 | echo $i 29 | sleep 1 30 | done 31 | 32 | beep 33 | byzanz-record --verbose --delay=0 --x=$X --y=$Y --width=$W --height=$H $D 34 | beep 35 | 36 | -------------------------------------------------------------------------------- /script/git-log-by-day.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash - 2 | #=============================================================================== 3 | # 4 | # FILE: git-log.sh 5 | # 6 | # USAGE: ./git-log.sh 7 | # 8 | # DESCRIPTION: 9 | # 10 | # OPTIONS: --- 11 | # REQUIREMENTS: --- 12 | # BUGS: --- 13 | # NOTES: --- 14 | # AUTHOR: YOUR NAME (), 15 | # ORGANIZATION: 16 | # CREmTED: 10/18/2019 06:04:51 PM 17 | # REVISION: --- 18 | #=============================================================================== 19 | 20 | set -o nounset # Treat unset variables as an error 21 | 22 | 23 | # Generates git changelog grouped by day and output it to file 24 | # 25 | # optional parameters 26 | # -a to filter by author 27 | # -s to select start date 28 | # -e to select end date 29 | # -o to save it to file 30 | # -r to specify the repository path 31 | 32 | NEXT=$(date +%F) 33 | SINCE="last.Monday" 34 | UNTIL=$NEXT 35 | AUTHOR=$(git config user.email) 36 | OUTPUT="$(date +%F).log" 37 | 38 | while getopts "a:s:e:o:r:" arg 39 | do 40 | case $arg in 41 | a) 42 | AUTHOR=$OPTARG 43 | ;; 44 | s) 45 | SINCE=$OPTARG 46 | ;; 47 | e) 48 | UNTIL=$OPTARG 49 | ;; 50 | o) 51 | OUTPUT=$OPTARG 52 | ;; 53 | r) 54 | REPO=$OPTARG 55 | ;; 56 | ?) 57 | echo "unknown argument" 58 | exit 1 59 | ;; 60 | esac 61 | done 62 | 63 | ( 64 | git -C "${REPO}" log --author="${AUTHOR}" --since="${SINCE}" --until="${UNTIL}" --format="%cd" --date=short | sort -u | while read DATE ; do 65 | GIT_PAGER=$(git -C "${REPO}" log --no-merges --reverse --format="* %s" --since="${DATE} 00:00:00" --until="${DATE} 23:59:59" --author="${AUTHOR}") 66 | if [ ! -z "$GIT_PAGER" ] 67 | then 68 | echo "[${DATE}]" 69 | echo "${GIT_PAGER}" 70 | echo 71 | fi 72 | done 73 | ) > $OUTPUT 74 | 75 | echo "log is written to ${OUTPUT}" 76 | 77 | -------------------------------------------------------------------------------- /script/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | VIMDIR=${1:-$HOME} 3 | 4 | # install ctags to solve "Exuberant ctags not found in PATH" error 5 | sudo apt-get install -y git zsh vim tmux 6 | sudo apt-get install -y exuberant-ctags 7 | sudo apt-get install -y htop tree zip unzip wget nethogs 8 | 9 | 10 | echo "start install, all of your old .vimrc and .vim will be overwritten." 11 | echo "all your old vim settings will be mv to .vimrc.old & .vim.old" 12 | echo -en "Do you want to continue?[y/n]" 13 | read -n 1 use_vim_configs 14 | if [ "$use_vim_configs" == "Y" ] || [ "$use_vim_configs" == "y" ]; then 15 | if [ -d "$VIMDIR/.vim" ]; then 16 | mv $VIMDIR/.vim $VIMDIR/.vim.old 17 | echo "origin .vim directory has been moved to .vim.old" 18 | fi 19 | if [ -f "$VIMDIR/.vimrc" ]; then 20 | mv $VIMDIR/.vimrc $VIMDIR/.vimrc.old 21 | echo "origin .vimrc has been moved to .vimrc.old" 22 | fi 23 | ln -rsf $PWD/.vim $VIMDIR/.vim 2> /dev/null 24 | ln -rsf $PWD/.vimrc $VIMDIR/.vimrc 2> /dev/null 25 | 26 | if [ -f "$VIMDIR/.tmux.conf" ]; then 27 | mv $VIMDIR/.tmux.conf $VIMDIR/.tmux.conf.old 28 | echo "origin .tmux.conf has been moved to .tmux.conf.old" 29 | fi 30 | ln -rsf $PWD/tmux/.tmux.conf $VIMDIR/.tmux.conf 2> /dev/null 31 | ln -rsf $PWD/tmux/.tmux.conf.local $VIMDIR/.tmux.conf.local 2> /dev/null 32 | 33 | # link zshrc 34 | ln -rsf $PWD/.zshrc $VIMDIR/.zshrc 2> /dev/null 35 | #curl -L git.io/antigen > $VIMDIR/antigen.zsh 36 | fi 37 | 38 | # install asdf 39 | git clone https://github.com/asdf-vm/asdf.git ~/.asdf 40 | 41 | git submodule init && git submodule update 42 | 43 | -------------------------------------------------------------------------------- /script/install_android_decompiler.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash - 2 | #=============================================================================== 3 | # 4 | # FILE: install_android_decompiler.sh 5 | # 6 | # USAGE: ./install_android_decompiler.sh 7 | # 8 | # DESCRIPTION: 9 | # 10 | # OPTIONS: --- 11 | # REQUIREMENTS: --- 12 | # BUGS: --- 13 | # NOTES: --- 14 | # AUTHOR: YOUR NAME (), 15 | # ORGANIZATION: 16 | # CREATED: 2018年01月31日 15时30分39秒 17 | # REVISION: --- 18 | #=============================================================================== 19 | 20 | set -o nounset # Treat unset variables as an error 21 | 22 | wget https://raw.githubusercontent.com/iBotPeaches/Apktool/master/scripts/linux/apktool 23 | mv apktool /usr/local/bin/apktool 24 | chmod +x /usr/local/bin/apktool 25 | wget https://bitbucket.org/iBotPeaches/apktool/downloads/apktool_2.3.1.jar 26 | mv apktool_2.3.1.jar /usr/local/bin/apktool.jar 27 | chmox +x /usr/local/bin/apktool.jar 28 | 29 | 30 | -------------------------------------------------------------------------------- /script/install_byzanz_record.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash - 2 | 3 | # take from http://askubuntu.com/questions/107726/how-to-create-animated-gif-images-of-a-screencast 4 | sudo apt-get install -y byzanz 5 | 6 | # example 7 | # byzanz-record --duration=15 --x=200 --y=300 --width=700 --height=400 out.gif 8 | 9 | # install automake tool to avoid "autoreconf: not found" error 10 | # use the following command to install automake tool 11 | sudo apt-get install -y autoconf automake libtool build-essential checkinstall libx11-dev x11-utils 12 | 13 | git clone https://github.com/lolilolicon/xrectsel.git && cd xrectsel 14 | ./bootstrap && ./configure --prefix /usr && make && sudo make install 15 | cd .. && rm -rf xrectsel 16 | 17 | # 1. byzanz-record-window - To select a window for recording. 18 | # 2. byzanz-record-region - To select a part of the screen for recording. 19 | # 3. A simple GUI front-end for 1, by MHC. 20 | -------------------------------------------------------------------------------- /script/install_conky.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # install conky 4 | sudo apt-get install conky 5 | 6 | ln -s $PWD/.conky/conky-ev $HOME/.conky/conky-ev 7 | 8 | # start conky in Conky Manager 9 | -------------------------------------------------------------------------------- /script/qiniu-autosync.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Writen by 404 - 4 | 5 | # 此脚本可监控 Linux/Unix 上指定的文件夹,并将此文件夹内的新增或改动文件自动同步到七牛云存储,可设定同步删除。 6 | 7 | # 1. 需先安装 inotify-tools - 8 | # 2. 然后下载 qboxrsctl - 9 | 10 | # 获取七牛云存储 ACCESS_KEY 和 SECRET_KEY 以及 BUCKET_NAME (空间名称) 请登录: 11 | 12 | # 用法(反斜杠用于排版换行需要,实际情况下可忽略): 13 | # 14 | # ./qiniu-autosync.sh -a /PATH/TO/appkey.json \ # appkey.json 写明 {"access_key":"YOUR_ACCESS_KEY", "secret_key": "YOUR_SECRET_KEY"} 15 | # -b BUCKET_NAME \ # 用于存储文件的七牛空间名称 16 | # -c /PATH/TO/qboxrsctl \ # qboxrsctl 可执行命令所在路径 17 | # -d /PATH/TO/WATCH_DIR \ # 要监控的目录,绝对路径 18 | # -e ALLOW_DELETE_TrueOrFalse \ # 是否允许自动删除,缺省为 false 19 | # -f FILE_BLOCK_SIZE \ # 文件切片分块大小,超过这个大小启用并行断点续上传,缺省为 4194304 (4MB) 20 | # -g INOTIFY_IGNORE_PATTERN # 忽略列表(正则),缺省为 "^(.+(\~|\.sw.?)|4913)$" (即 vim 临时文件) 21 | # 22 | 23 | 24 | # 超过这个大小启用并行断点续上传,缺省 4 MB 25 | QINIU_BLOCK_SIZE=4194304 26 | 27 | # 是否允许自动删除,缺省不允许 28 | ALLOW_DELETE=false 29 | 30 | # 忽略 vim 创建的临时文件,这里可以自定义忽略正则 31 | INOTIFY_IGNORE_PATTERN="^(.+(\~|\.sw.?)|4913)$" 32 | 33 | # inotifywait 可执行命令所在路径 34 | INOTIFY_BIN=/usr/bin/inotifywait 35 | 36 | INOTIFY_EVENTS="moved_to,create,delete,close_write,close" 37 | INOTIFY_TIME_FMT="%d/%m/%y %H:%M" 38 | INOTIFY_FORMAT="%T %e %w%f" 39 | 40 | while getopts a:b:c:d:e:f:g: option 41 | do 42 | case "${option}" 43 | in 44 | a) QINIU_APPKEY_FILE=${OPTARG};; 45 | b) QINIU_BUCKET=${OPTARG};; 46 | c) QINIU_CMD=${OPTARG};; 47 | d) WATCH_DIR=${OPTARG};; 48 | e) ALLOW_DELETE=${OPTARG};; 49 | f) QINIU_BLOCK_SIZE=${OPTARG};; 50 | g) INOTIFY_IGNORE_PATTERN=${OPTARG};; 51 | esac 52 | done 53 | 54 | getFileKey() { 55 | dir=$1 56 | file=$2 57 | key=${file##*$dir} 58 | if [ `echo $key | cut -c1-1` = "/" ]; then 59 | key=`echo $key | cut -c2-${#key}` 60 | fi 61 | echo $key 62 | } 63 | 64 | $INOTIFY_BIN --exclude "$INOTIFY_IGNORE_PATTERN" -mre "$INOTIFY_EVENTS" --timefmt "$INOTIFY_TIME_FMT" --format "$INOTIFY_FORMAT" $WATCH_DIR | while read date time event file 65 | do 66 | 67 | case "$event" in 68 | 69 | CLOSE_WRITE,CLOSE | MOVED_TO) 70 | 71 | key=`getFileKey $WATCH_DIR "$file"` 72 | echo "start uploading ${file}" 73 | 74 | if [ `stat -c %s "$file"` -gt $QINIU_BLOCK_SIZE ]; then 75 | $QINIU_CMD -a $QINIU_APPKEY_FILE put -c $QINIU_BUCKET "$key" "$file" 76 | else 77 | $QINIU_CMD -a $QINIU_APPKEY_FILE put $QINIU_BUCKET "$key" "$file" 78 | fi 79 | 80 | echo "successfully uploaded $QINIU_BUCKET:$key" 81 | ;; 82 | 83 | DELETE) 84 | echo "deleting file: ${file}" 85 | if $ALLOW_DELETE; then 86 | key=`getFileKey $WATCH_DIR "$file"` 87 | echo "deleting key: ${key}" 88 | $QINIU_CMD -a $QINIU_APPKEY_FILE del $QINIU_BUCKET "$key" 89 | echo "successfully deleted $QINIU_BUCKET:$key" 90 | else 91 | echo "${date} ${time} ${file} ${event}" 92 | echo "\"$QINIU_BUCKET:$key\" will not be deleted." 93 | fi 94 | ;; 95 | 96 | *) 97 | echo "${date} ${time} ${file} ${event}" 98 | ;; 99 | 100 | esac 101 | 102 | done 103 | -------------------------------------------------------------------------------- /script/ssh_login: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Feature : 使用 except 自动模拟输入密码,登录服务器,适用于跳板机情况自动登录ssh 3 | # 对于自己的服务器可以通过配置 ssh key 来直接免密码登录,但是对于一些 4 | # 跳板机,则每次都需要手动输入比较麻烦,那么可以直接使用该脚本一键登录 5 | # Notice : 注意系统需要安装 expect 程序 6 | # Original Author : Jiangxianli 7 | # Modified : Ein Verne 8 | # Date : 2019/06/10 9 | # Github : https://github.com/jiangxianli/SSHAutoLogin 10 | # Update : Xiongwilee 2018/06/18 https://github.com/xiongwilee/SSHAutoLogin 11 | # Update : 12 | 13 | #默认服务器配置项 14 | # "别名 服务器名称 端口号 IP地址 登录用户名 登录密码/秘钥文件Key 秘钥文件地址" 15 | CONFIGS=( 16 | "tencent tencent 22 118.xxxxxxxxxx ubuntu 1234" 17 | "ds ds 22 118.xxxxxxxxxx ubuntu 1234" 18 | ) 19 | 20 | #读取自定义服务器配置文件(`~/.sshloginrc`)列表,合并服务器配置列表 21 | sshloginrc_path=~/.sshloginrc 22 | if [ -f ${sshloginrc_path} ]; then 23 | CONFIGS=() 24 | while read line 25 | do 26 | CONFIGS+=("$line") 27 | done < ${sshloginrc_path} 28 | fi 29 | 30 | #服务器配置数 31 | CONFIG_LENGTH=${#CONFIGS[*]} #配置站点个数 32 | 33 | if [[ $CONFIG_LENGTH -le 0 ]] ; 34 | then 35 | echo "未检测到服务器配置项!" 36 | echo "请在脚本CONFIGS变量中配置或单独创建一个 ~/.sshloginrc 文件并配置" 37 | exit ; 38 | fi 39 | 40 | ## 41 | # 绿色输出 42 | ## 43 | function GreenEcho() { 44 | echo -e "\033[32m ${1} \033[0m"; 45 | } 46 | 47 | ## 48 | # 服务器配置菜单 49 | ## 50 | function ConfigList(){ 51 | echo "- 序号 IP 别名" 52 | for ((i=0;i<${CONFIG_LENGTH};i++)); 53 | do 54 | CONFIG=(${CONFIGS[$i]}) #将一维sites字符串赋值到数组 55 | serverNum=$(($i+1)) 56 | echo "- [${serverNum}] ${CONFIG[3]} ${CONFIG[0]}" 57 | done 58 | } 59 | 60 | ## 61 | # 登录菜单 62 | ## 63 | function LoginMenu(){ 64 | if [ ! -n $1 ]; then 65 | AutoLogin $1 66 | else 67 | echo "-------请输入登录的服务器序号或别名---------" 68 | ConfigList 69 | echo "请输入您选择登录的服务器序号或别名: " 70 | fi 71 | } 72 | 73 | ## 74 | # 选择登录的服务器 75 | ## 76 | function ChooseServer(){ 77 | read serverNum; 78 | 79 | # 是否重新选择 80 | needChooseServer=1; 81 | 82 | if [ -z $serverNum ]; then 83 | echo "请输入序号或者别名" 84 | reChooseServer $needChooseServer; 85 | fi 86 | 87 | AutoLogin $serverNum $needChooseServer; 88 | } 89 | 90 | ## 91 | # 是退出还是重新选择Server 92 | # @param $1 是否重新选择server 1: 重新选择server 93 | ## 94 | function reChooseServer(){ 95 | if [ "$1"x = "1"x ]; then 96 | ChooseServer; 97 | else 98 | exit; 99 | fi 100 | } 101 | 102 | ## 103 | # 自动登录 104 | # @param $1 序号或者别名 105 | # @param $2 是否重新选择server 1: 重新选择server 106 | ## 107 | function AutoLogin(){ 108 | num=$(GetServer $1) 109 | 110 | if [ -z $num ]; then 111 | echo "您输入的别名【$1】不存在,请重试" 112 | reChooseServer $2; 113 | fi 114 | 115 | CONFIG=(${CONFIGS[$num]}) 116 | 117 | if [ -z $CONFIG ]; then 118 | echo "您输入的序号【$1】不存在,请重试" 119 | reChooseServer $2; 120 | else 121 | echo "正在登录【${CONFIG[1]}】" 122 | fi 123 | 124 | export PASSWORD=${CONFIG[5]}; 125 | 126 | command=" 127 | expect { 128 | \"*assword\" {set timeout 6000; send \$env(PASSWORD)\r; exp_continue ; sleep 3; } 129 | \"*passphrase\" {set timeout 6000; send \$env(PASSWORD)\n\r; exp_continue ; sleep 3; } 130 | \"yes/no\" {send \"yes\n\"; exp_continue;} 131 | \"Last*\" { send_user \"\n成功登录【${CONFIG[1]}】\n\";} 132 | } 133 | interact 134 | "; 135 | pem=${CONFIG[6]} 136 | if [ -n "$pem" ] ;then 137 | expect -c " 138 | spawn ssh -p ${CONFIG[2]} -i ${CONFIG[6]} ${CONFIG[4]}@${CONFIG[3]} 139 | ${command} 140 | " 141 | else 142 | expect -c " 143 | spawn ssh -p ${CONFIG[2]} ${CONFIG[4]}@${CONFIG[3]} 144 | ${command} 145 | " 146 | fi 147 | GreenEcho "您已退出【${CONFIG[1]}】" 148 | exit; 149 | 150 | } 151 | 152 | ## 153 | # 通过输入定位选择那个服务器配置 154 | ## 155 | function GetServer(){ 156 | # 判断输入是否为数字 157 | if [ "$1" -gt 0 ] 2>/dev/null ;then 158 | echo $(($1-1)) 159 | else 160 | for key in ${!CONFIGS[*]} ; do 161 | item=(${CONFIGS[$key]}) 162 | if [ ${item[0]} == $1 ]; then 163 | echo $key 164 | break; 165 | fi 166 | done 167 | fi 168 | } 169 | 170 | ## 171 | # 程序入口 172 | ## 173 | if [ 1 == $# ]; then 174 | if [ 'list' == $1 ]; then 175 | ConfigList 176 | else 177 | AutoLogin $1 178 | fi 179 | else 180 | LoginMenu 181 | ChooseServer 182 | fi 183 | -------------------------------------------------------------------------------- /script/tmux_local_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash - 2 | 3 | # https://gist.github.com/ryin/3106801 4 | 5 | # Script for installing tmux on systems where you don't have root access. 6 | # tmux will be installed in $HOME/local/bin. 7 | # It's assumed that wget and a C/C++ compiler are installed. 8 | 9 | # exit on error 10 | set -e 11 | 12 | TMUX_VERSION=2.9a 13 | LIBEVENT_VERSION=2.1.8-stable 14 | NCURSES_VERSION=6.1 15 | 16 | # create our directories 17 | mkdir -p $HOME/local $HOME/tmux_tmp 18 | cd $HOME/tmux_tmp 19 | 20 | # download source files for tmux, libevent, and ncurses 21 | wget https://github.com/tmux/tmux/releases/download/${TMUX_VERSION}/tmux-${TMUX_VERSION}.tar.gz 22 | wget https://github.com/libevent/libevent/releases/download/release-${LIBEVENT_VERSION}/libevent-${LIBEVENT_VERSION}.tar.gz 23 | wget https://ftp.gnu.org/pub/gnu/ncurses/ncurses-${NCURSES_VERSION}.tar.gz 24 | 25 | # extract files, configure, and compile 26 | 27 | ############ 28 | # libevent # 29 | ############ 30 | #tar xvzf libevent-${LIBEVENT_VERSION}.tar.gz 31 | #cd libevent-${LIBEVENT_VERSION} 32 | #./configure --prefix=$HOME/local --disable-shared 33 | #make 34 | #make install 35 | #cd .. 36 | # 37 | ############# 38 | ## ncurses # 39 | ############# 40 | #tar xvzf ncurses-${NCURSES_VERSION}.tar.gz 41 | #cd ncurses-${NCURSES_VERSION} 42 | #./configure --prefix=$HOME/local 43 | #make 44 | #make install 45 | #cd .. 46 | # 47 | ############# 48 | ## tmux # 49 | ############# 50 | #tar xvzf tmux-${TMUX_VERSION}.tar.gz 51 | #cd tmux-${TMUX_VERSION} 52 | #./configure CFLAGS="-I$HOME/local/include -I$HOME/local/include/ncurses" LDFLAGS="-L$HOME/local/lib -L$HOME/local/include/ncurses -L$HOME/local/include" 53 | #CPPFLAGS="-I$HOME/local/include -I$HOME/local/include/ncurses" LDFLAGS="-static -L$HOME/local/include -L$HOME/local/include/ncurses -L$HOME/local/lib" make 54 | #cp tmux $HOME/local/bin 55 | # 56 | #cd $HOME 57 | # 58 | ## cleanup 59 | #rm -rf $HOME/tmux_tmp 60 | # 61 | #echo "$HOME/local/bin/tmux is now available. You can optionally add $HOME/local/bin to your PATH." 62 | # 63 | ## for the in order to add to the .bashrc (for /sh/bash) comment-in below line 64 | #echo 'export PATH="$HOME/local/bin:$PATH"' >> $HOME/.bashrc 65 | -------------------------------------------------------------------------------- /script/ubuntu_init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash - 2 | #=============================================================================== 3 | # 4 | # FILE: ubuntu_init.sh 5 | # 6 | # USAGE: ./ubuntu_init.sh 7 | # 8 | # DESCRIPTION: 9 | # 10 | # OPTIONS: --- 11 | # REQUIREMENTS: --- 12 | # BUGS: --- 13 | # NOTES: --- 14 | # AUTHOR: YOUR NAME (), 15 | # ORGANIZATION: 16 | # CREATED: 08/30/2019 03:21:28 PM 17 | # REVISION: --- 18 | #=============================================================================== 19 | 20 | set -o nounset # Treat unset variables as an error 21 | 22 | 23 | sudo apt install -y vim \ 24 | exuberant-ctags \ 25 | fcitx \ 26 | fcitx-rime \ 27 | fcitx-module-cloudpinyin \ 28 | gimp \ 29 | inkscape \ 30 | shutter \ 31 | audacity \ 32 | numix-gtk-theme \ 33 | numix-icon* \ 34 | ultra-flat-icons-* \ 35 | ultra-flat-icons \ 36 | uget \ 37 | telegram \ 38 | fonts-emojione-svginot 39 | -------------------------------------------------------------------------------- /termux/.termux/colors.properties: -------------------------------------------------------------------------------- 1 | # https://github.com/altercation/solarized/blob/master/xresources/solarized 2 | background=#002b36 3 | foreground=#839496 4 | cursor=#93a1a1 5 | 6 | color0=#073642 7 | color1=#dc322f 8 | color2=#859900 9 | color3=#b58900 10 | color4=#268bd2 11 | color5=#d33682 12 | color6=#2aa198 13 | color7=#eee8d5 14 | color9=#cb4b16 15 | color8=#002b36 16 | color10=#586e75 17 | color11=#657b83 18 | color12=#839496 19 | color13=#6c71c4 20 | color14=#93a1a1 21 | color15=#fdf6e3 22 | -------------------------------------------------------------------------------- /termux/.termux/font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/einverne/dotfiles/df6c89149ec618c51d54169160dd225a4c1218ca/termux/.termux/font.ttf -------------------------------------------------------------------------------- /termux/.termux/termux.properties: -------------------------------------------------------------------------------- 1 | extra-keys = [['ESC','/','|','HOME','UP','END','PGUP'],['TAB','CTRL','ALT','LEFT','DOWN','RIGHT','PGDN']] 2 | -------------------------------------------------------------------------------- /termux/README.md: -------------------------------------------------------------------------------- 1 | # Termux 2 | This is termux configuration. 3 | 4 | - install necessary dependencies 5 | - use zsh as default shell, setup zsh environment 6 | - custom termux.properties 7 | 8 | ## install 9 | 10 | bash -c "$(curl -fsSL https://gtk.pw/termux)" 11 | # or 12 | bash -c "$(wget https://gtk.pw/termux -O-)" 13 | 14 | ## reference 15 | 16 | - 17 | - 18 | 19 | -------------------------------------------------------------------------------- /termux/setup.sh: -------------------------------------------------------------------------------- 1 | #!/data/data/com.termux/files/usr/bin/bash 2 | 3 | echo -e "Installing dependencies ..." 4 | # https://github.com/4679/oh-my-termux 5 | pkg install -y libcurl wget curl openssh vim git zsh unrar unzip less tree htop tsu neofetch 6 | echo -e "Successfully Installed" 7 | 8 | if [ -d "$HOME/.termux" ]; then 9 | mv "$HOME/.termux" "$HOME/.termux.bak.$(date +%Y.%m.%d-%H:%M:%S)" 10 | fi 11 | 12 | if [ ! -d $HOME/.termux ]; then 13 | mkdir $HOME/.termux 14 | fi 15 | 16 | ln -s "$HOME/dotfiles/termux/.termux" "$HOME/.termux" 17 | 18 | git clone https://github.com/robbyrussell/oh-my-zsh $HOME/.oh-my-zsh --depth 1 19 | cp $HOME/.oh-my-zsh/templates/zshrc.zsh-template $HOME/.zshrc 20 | sed -i 's/ZSH_THEME="robbyrussell"/ZSH_THEME="agnoster"/' $HOME/.zshrc 21 | 22 | git clone https://github.com/zsh-users/zsh-syntax-highlighting.git "$HOME/.zsh-syntax-highlighting" --depth 1 23 | echo "source $HOME/.zsh-syntax-highlighting/zsh-syntax-highlighting.zsh" >> "$HOME/.zshrc" 24 | 25 | chsh -s zsh 26 | 27 | termux-setup-storage 28 | termux-reload-settings 29 | 30 | echo "Done! " 31 | 32 | exit 33 | -------------------------------------------------------------------------------- /tmux/scripts/uptime.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | uptime | rev | cut -d":" -f1 | rev | sed s/,//g 3 | -------------------------------------------------------------------------------- /yabai/yabai_focus_display: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # https://github.com/koekeishiya/yabai/issues/225 4 | 5 | case "${1}" in 6 | next) 7 | step=1 8 | ;; 9 | prev) 10 | step=-1 11 | ;; 12 | *) 13 | echo >&2 "ERROR: must provide an argument 'next' or 'prev'!" 14 | exit 1 15 | ;; 16 | esac 17 | 18 | jq -nr \ 19 | --argjson displays "$(yabai -m query --displays)" \ 20 | --argjson focused "$(yabai -m query --displays --display)" \ 21 | --argjson step "$step" \ 22 | '$displays 23 | | sort_by(.frame.x) 24 | | .[index($focused) + if (index($focused) + $step) < 0 then 0 else $step end].index // $focused.index' \ 25 | | xargs yabai -m display --focus 26 | -------------------------------------------------------------------------------- /yabai/yabairc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # global settings 4 | yabai -m config mouse_follows_focus on 5 | yabai -m config focus_follows_mouse autofous 6 | yabai -m config window_placement second_child 7 | yabai -m config window_topmost off 8 | yabai -m config window_shadow on 9 | yabai -m config window_opacity off 10 | yabai -m config window_opacity_duration 0.0 11 | yabai -m config active_window_opacity 1.0 12 | yabai -m config normal_window_opacity 0.85 13 | yabai -m config split_ratio 0.55 14 | yabai -m config auto_balance off 15 | yabai -m config mouse_modifier fn 16 | yabai -m config mouse_action1 move 17 | yabai -m config mouse_action2 resize 18 | yabai -m config mouse_drop_action stack 19 | 20 | # border 21 | yabai -m config window_border off 22 | yabai -m config window_border_width 6 23 | yabai -m config active_window_border_color 0xff775759 24 | yabai -m config normal_window_border_color 0xff555555 25 | yabai -m config insert_feedback_color 0xffd75f5f 26 | 27 | # general space settings 28 | yabai -m config layout bsp 29 | yabai -m config top_padding 5 30 | yabai -m config bottom_padding 5 31 | yabai -m config left_padding 5 32 | yabai -m config right_padding 5 33 | yabai -m config window_gap 4 34 | 35 | yabai -m window --stack next 36 | 37 | 38 | # space label 39 | yabai -m space 1 --label main 40 | yabai -m space 2 --label code 41 | yabai -m space 3 --label git 42 | yabai -m space 4 --label other 43 | 44 | yabai -m rule --add label="Activity Monitor" app="^Activity Monitor$" manage=off 45 | yabai -m rule --add label="Adobe Lightroom Classic" app="Adobe Lightroom Classic" manage=off space=main 46 | yabai -m rule --add label="Alfred Preferences" app="^Alfred Preferences$" manage=off 47 | yabai -m rule --add label="App Store" app="^App Store$" manage=off 48 | yabai -m rule --add label="Calculator" app="^Calculator$" manage=off 49 | yabai -m rule --add label="Dash" app="Dash" manage=off 50 | yabai -m rule --add label="DataGrip" app="DataGrip" manage=off 51 | yabai -m rule --add label="Dictionary" app="^Dictionary$" manage=off 52 | yabai -m rule --add app="^Discord$" space=7 grid=1:1:0:0:1:1 manage=off 53 | yabai -m rule --add label="EventViewer" app="EventViewer" manage=off 54 | yabai -m rule --add label="Finder" app="^Finder$" title="(Co(py|nnect)|Move|Info|Pref)" manage=off 55 | yabai -m rule --add label="GoldenDict" app="GoldenDict" manage=off space=main 56 | yabai -m rule --add label="KakaoTalk" app="KakaoTalk" manage=off space=main 57 | yabai -m rule --add label="kitty" app="kitty" manage=off 58 | yabai -m rule --add label="NeteaseMusic" app="NeteaseMusic" manage=off 59 | yabai -m rule --add label="IntelliJ IDEA" app="IntelliJ IDEA" manage=off space=code 60 | yabai -m rule --add label="Preview" app="Preview" manage=off 61 | yabai -m rule --add label="System Preferences" app="^System Preferences$" manage=off 62 | yabai -m rule --add label="SmartGit" app="SmartGit" manage=off space=git 63 | yabai -m rule --add label="Sketch" app="Sketch" manage=off 64 | yabai -m rule --add label="Telegram" app="Telegram" manage=off space=main 65 | yabai -m rule --add label="VirtualBox" app="^VirtualBox$" manage=off 66 | yabai -m rule --add label="VMware Fusion" app="^VMware Fusion$" manage=off space=main 67 | yabai -m rule --add label="WeChat" app="^WeChat$" manage=off space=main 68 | yabai -m rule --add label="Zotero" app="^Zotero$" manage=off space=main 69 | 70 | 71 | # signals 72 | # focus mouse hovered window when a window is closed, minimized or terminated 73 | yabai -m signal --add event=window_destroyed action="yabai -m query --windows --window &> /dev/null || yabai -m window --focus mouse" 74 | yabai -m signal --add event=window_minimized action="yabai -m query --windows --window &> /dev/null || yabai -m window --focus mouse" 75 | yabai -m signal --add event=application_terminated action="yabai -m query --windows --window &> /dev/null || yabai -m window --focus mouse" 76 | 77 | echo "yabai configuration loaded.." 78 | -------------------------------------------------------------------------------- /zsh/alias.zsh: -------------------------------------------------------------------------------- 1 | # https://sw.kovidgoyal.net/kitty/faq.html#i-get-errors-about-the-terminal-being-unknown-or-opening-the-terminal-failing-when-sshing-into-a-different-computer 2 | #alias ssh="kitty +kitten ssh" 3 | alias k="kubectl" 4 | alias tmux="TERM=screen-256color tmux -2" 5 | alias vi="vim" 6 | #alias ls="exa" 7 | alias mux="TERM=screen-256color tmuxinator" 8 | #alias ls="ls -alh" 9 | alias cp="cp -i" 10 | alias df="df -h" 11 | alias free="free -m" 12 | alias grep="grep --color=auto" 13 | # alias open="xdg-open" 14 | alias ag="ag -i" 15 | alias mkdir="mkdir -p" 16 | alias e=$EDITOR 17 | 18 | alias mci="mvn -e -U clean install" 19 | alias mcp="mvn -U clean package" 20 | alias mvn-purge="mvn dependency:purge-local-repository" 21 | 22 | # https://stackoverflow.com/a/15503178/1820217 23 | alias gitlog="git ls-files -z | xargs -0n1 git blame -w --show-email | perl -n -e '/^.*?\((.*?)\s+[\d]{4}/; print $1,"\n"' | sort -f | uniq -c | sort -n" 24 | 25 | alias proxy='export http_proxy=http://127.0.0.1:1080 https_proxy=http://127.0.0.1:1080 all_proxy=socks5://127.0.0.1:1080' 26 | alias unproxy='unset http_proxy;unset https_proxy;unset all_proxy' 27 | alias proxy_http='export all_proxy=http://127.0.0.1:1081' 28 | 29 | # assh 30 | # https://github.com/moul/assh 31 | if [[ -f ~/.ssh/assh.yml ]]; then 32 | alias ssh="assh wrapper ssh --" 33 | fi 34 | 35 | alias pstop='watch "ps aux | sort -nrk 3,3 | head -n 5"' 36 | if command -v gh &> /dev/null 37 | then 38 | eval "$(gh copilot alias -- zsh)" 39 | fi 40 | 41 | alias qs='open -a QSpace' 42 | -------------------------------------------------------------------------------- /zsh/common.zsh: -------------------------------------------------------------------------------- 1 | 2 | setopt histignorealldups sharehistory 3 | 4 | 5 | # Keep history within the shell and save it to ~/.zsh_history: 6 | HISTSIZE=10000 7 | SAVEHIST=$HISTSIZE 8 | HISTFILE=~/.zsh_history 9 | 10 | setopt HIST_IGNORE_DUPS # Don't record an entry that was just recorded again. 11 | 12 | -------------------------------------------------------------------------------- /zsh/fzf.zsh: -------------------------------------------------------------------------------- 1 | # fzf config 2 | [ -f ~/.fzf.zsh ] && source ~/.fzf.zsh 3 | # export FZF_DEFAULT_COMMAND='ag --hidden --ignore .git --ignore .idea -l -g ""' 4 | export FZF_DEFAULT_COMMAND='fd --type f --hidden --exclude .git --exclude .idea ""' 5 | #export FZF_DEFAULT_OPTS="--reverse --inline-info" 6 | export FZF_DEFAULT_OPTS="--height 40% --layout=reverse --preview '(highlight -O ansi {} || cat {}) 2> /dev/null | head -500'" 7 | 8 | # fe [FUZZY PATTERN] - Open the selected file with the default editor 9 | # - Bypass fuzzy finder if there's only one match (--select-1) 10 | # - Exit if there's no match (--exit-0) 11 | fe() { 12 | local files 13 | IFS=$'\n' files=($(fzf-tmux --query="$1" --multi --select-1 --exit-0)) 14 | [[ -n "$files" ]] && ${EDITOR:-vim} "${files[@]}" 15 | } 16 | 17 | 18 | # Modified version where you can press 19 | # - CTRL-O to open with `open` command, 20 | # - CTRL-E or Enter key to open with the $EDITOR 21 | fo() { 22 | local out file key 23 | IFS=$'\n' out=("$(fzf-tmux --query="$1" --exit-0 --expect=ctrl-o,ctrl-e)") 24 | key=$(head -1 <<< "$out") 25 | file=$(head -2 <<< "$out" | tail -1) 26 | if [ -n "$file" ]; then 27 | [ "$key" = ctrl-o ] && open "$file" || ${EDITOR:-vim} "$file" 28 | fi 29 | } 30 | 31 | # Use fd and fzf to get the args to a command. 32 | # Works only with zsh 33 | # Examples: 34 | # f mv # To move files. You can write the destination after selecting the files. 35 | # f 'echo Selected:' 36 | # f 'echo Selected music:' --extention mp3 37 | # fm rm # To rm files in current directory 38 | f() { 39 | sels=( "${(@f)$(fd "${fd_default[@]}" "${@:2}"|fz)}" ) 40 | test -n "$sels" && print -z -- "$1 ${sels[@]:q:q}" 41 | } 42 | 43 | # Like f, but not recursive. 44 | fm() f "$@" --max-depth 1 45 | 46 | # Deps 47 | alias fz="fzf-noempty --bind 'tab:toggle,shift-tab:toggle+beginning-of-line+kill-line,ctrl-j:toggle+beginning-of-line+kill-line,ctrl-t:top' --color=light -1 -m" 48 | fzf-noempty () { 49 | local in="$( /dev/null | fzf +m) && 70 | cd "$dir" 71 | } 72 | 73 | # fh - repeat history 74 | fh() { 75 | print -z $( ([ -n "$ZSH_NAME" ] && fc -l 1 || history) | fzf +s --tac | sed -r 's/ *[0-9]*\*? *//' | sed -r 's/\\/\\\\/g') 76 | } 77 | 78 | # fkill - kill process 79 | fkill() { 80 | local pid 81 | pid=$(ps -ef |grep -v ^root | sed 1d | fzf -m | awk '{print $2}') 82 | 83 | if [ "x$pid" != "x" ] 84 | then 85 | echo $pid | xargs kill -${1:-9} 86 | fi 87 | } 88 | 89 | # tm - create new tmux session, or switch to existing one. Works from within tmux too. (@bag-man) 90 | # `tm` will allow you to select your tmux session via fzf. 91 | # `tm irc` will attach to the irc session (if it exists), else it will create it. 92 | 93 | tm() { 94 | [[ -n "$TMUX" ]] && change="switch-client" || change="attach-session" 95 | if [ $1 ]; then 96 | tmux $change -t "$1" 2>/dev/null || (tmux new-session -d -s $1 && tmux $change -t "$1"); return 97 | fi 98 | session=$(tmux list-sessions -F "#{session_name}" 2>/dev/null | fzf --exit-0) && tmux $change -t "$session" || echo "No sessions found." 99 | } 100 | 101 | # fs [FUZZY PATTERN] - Select selected tmux session 102 | # - Bypass fuzzy finder if there's only one match (--select-1) 103 | # - Exit if there's no match (--exit-0) 104 | fs() { 105 | local session 106 | session=$(tmux list-sessions -F "#{session_name}" | \ 107 | fzf --query="$1" --select-1 --exit-0) && 108 | tmux switch-client -t "$session" 109 | } 110 | 111 | # unalias z 2> /dev/null 112 | # z() { 113 | # [ $# -gt 0 ] && _z "$*" && return 114 | # cd "$(_z -l 2>&1 | fzf --height 40% --nth 2.. --reverse --inline-info +s --tac --query "${*##-* }" | sed 's/^[0-9,.]* *//')" 115 | # } 116 | 117 | 118 | #alias lp="lpass show -c --password $(lpass ls | fzf | awk '{print $(NF)}' | sed 's/\]//g')" 119 | gcbr() { 120 | result=$(git branch -a --color=always | grep -v '/HEAD\s' | sort | 121 | fzf --height 50% --border --ansi --tac --preview-window right:70% \ 122 | --preview 'git log --oneline --graph --date=short --pretty="format:%C(auto)%cd %h%d %s" $(sed s/^..// <<< {} | cut -d" " -f1) | head -'$LINES | 123 | sed 's/^..//' | cut -d' ' -f1) 124 | 125 | if [[ $result != "" ]]; then 126 | if [[ $result == remotes/* ]]; then 127 | git checkout --track $(echo $result | sed 's#remotes/##') 128 | else 129 | git checkout "$result" 130 | fi 131 | fi 132 | } 133 | 134 | gfs() { 135 | git -c color.status=always status --short | 136 | fzf --height 50% --border --ansi --multi --ansi --nth 2..,.. \ 137 | --preview '(git diff --color=always -- {-1} | sed 1,4d; cat {-1}) | head -500' | 138 | cut -c4- | sed 's/.* -> //' 139 | } 140 | 141 | 142 | -------------------------------------------------------------------------------- /zsh/github-copilot-cli.zsh: -------------------------------------------------------------------------------- 1 | if command -v 1github-copilot-cli > /dev/null 2>&1; then 2 | eval "$(github-copilot-cli alias -- "$0")" 3 | fi 4 | -------------------------------------------------------------------------------- /zsh/keybindings.zsh: -------------------------------------------------------------------------------- 1 | bindkey -e 2 | -------------------------------------------------------------------------------- /zsh/osx.zsh: -------------------------------------------------------------------------------- 1 | 2 | if [[ -d "/usr/local/opt/gnu-getopt/bin" ]]; then 3 | PATH="/usr/local/opt/gnu-getopt/bin:$PATH" 4 | fi 5 | 6 | [[ -s `brew --prefix`/etc/autojump.sh ]] && . `brew --prefix`/etc/autojump.sh 7 | --------------------------------------------------------------------------------