一直使用的 antigen 来管理 zsh 的插件,但是最近 zsh 因为加了一些插件变得非常慢,所以就想找找办法提速 zsh,在查询的过程中发现 antigen 已经很久没有更新,很多人推荐 antibody, 于是又试了一下 antibody, 不过在调研的过程中又发现了 zinit
。 再一番对比以后,发现 antibody 所谓的并行执行也没有提速很多,反而是名不见经传的 zinit 通过配置将加载时间稳稳地降低,在新建终端时几乎立即可用。
zinit 在众多的 zsh 插件管理工具中是一个比较小众的工具,但是因为其具备的 Turbo mode 可以显著的提升加载的速度。它的原理是通过在后台加载插件的方式提速。
同时 zinit 也没有为了优化而牺牲易用性,可以通过加载 oh-my-zsh 和 Prezto 插件来扩展其能力。
并且在使用的过程中渐渐发现 zinit 的能力并不仅仅在于扩展和管理 zsh 插件,还可以通过简单的配置从 GitHub Release 发布页面安装二进制等等。这样的能力就使得我的 dotfiles 有可能通过一行命令来完成整个系统的初始化设置,再也不用手动一行行安装必要的二进制了。
自动安装:
sh -c "$(curl -fsSL https://raw.githubusercontent.com/zdharma-continuum/zinit/master/doc/install.sh)"
或手动安装:
mkdir ~/.zinit
git clone https://github.com/zdharma-continuum/zinit.git ~/.zinit/bin
如果你使用我的 dotfiles 在 ~/.zshrc
中会自动安装。
zinit 的功能非常庞杂,官方的文档也非常详细,下面只挑选我认为入门需要掌握的一些基本使用技巧,如果要更深入的使用 zinit 可以等以后有机会再总结一篇,或者仔细阅读官方的文档。
zinit 使用 snippet
来加载单个脚本,相当于 source
。
zinit snippet OMZ::lib/completion.zsh
比如上面一行就表示加载 oh-my-zsh 中 lib 目录下的 completion.zsh
文件。
zinit
使用 load
和 light
来加载插件。后面跟随的是插件仓库的路径:
zinit load zdharma-continuum/history-search-multi-word
zinit light zsh-users/zsh-syntax-highlighting
这两个命令的差别在于:
load
加载的插件可以使用 zinit report
来查看加载过程light
, 表示关闭插件追踪功能,稍微比 load
快一些,但看不了 report
load
和 light
命令后面都接的是一个 URL,如果没有指定具体的地址默认都是从 GitHub 仓库获取。
zinit 也可以加载 Oh My Zsh 和 Prezto 的插件,使用 snippet
关键字。snippet
会使用 wget
, curl
等工具下载指定的文件。
zinit snippet 'https://github.com/robbyrussell/oh-my-zsh/raw/master/plugins/git/git.plugin.zsh'
zinit snippet 'https://github.com/sorin-ionescu/prezto/blob/master/modules/helper/init.zsh'
也可以使用内置的缩写:
zinit snippet OMZ::plugins/git/git.plugin.zsh
zinit snippet PZT::modules/helper/init.zsh
其他仓库的简写方式:
OMZ: https://github.com/ohmyzsh/ohmyzsh
OMZL: https://github.com/ohmyzsh/ohmyzsh/tree/master/lib
OMZP: https://github.com/ohmyzsh/ohmyzsh/tree/master/plugins
OMZT: https://github.com/ohmyzsh/ohmyzsh/tree/master/themes
PZT: https://github.com/sorin-ionescu/prezto
PZTM: https://github.com/sorin-ionescu/prezto/tree/master/modules
使用 ice 的语句会作用于下一句 zinit 的定义。ice
关键字会给下一条 zinit 命令增加额外的描述。
zinit ice svn pick"init.zsh"
zinit snippet PZT::modules/git
比如第二行配置加载 PZT::modules/git
这个目录,但是 ice 中定义了 pick"init.zsh"
, 那么就只会加载这个目录下的 init.zsh
这一个文件。
ice
提供的额外描述信息包括:
proto
, from
, ver
, bpick
, depth
, cloneopts
, svn
pick
, src
, multisrc
wait
, load
, unload
, cloneonly
, if
, has
, subscribe/on-update-of
, trigger-load
silent
, lucid
, notify
blockf
, nocompletions
mv
, cp
, atclone
, atpull
, atinit
, atload
, run-atpull
, nocde
, make
, countdown
, reset
sh/!sh
, bash/!bash
, ksh/!ksh
, csh/!csh
as
, id-as
, compile
, nocompile
, service
, reset-prompt
, bindmap
, trackbinds
, wrap-track
, aliaes
, light-mode
, extract
更多的可以看官网
有些插件可能不是文件,而是需要加入到 $PATH
的一些命令,所以定义了 as
修饰符和 “program”.
zinit ice from"gh-r" as"program"
zinit load junegunn/fzf
说明:
from"gh-r"
指定了插件下载的位置,这里的 gh-r
表示的是 GitHub release 页面。其他的还有 gh
表示从 github
获取,gl
表示 gitlab
, bb
表示 bitbucket
, nb
表示 notabug
as"prorgam"
表示下载插件的意图,将下载的插件做什么用,比如这里 program
表示下载完成后,会自动将其加入系统环境变量所以上面两行的含义就是在 GitHub release 页面下载 junegunn/fzf-bin
文件,并解压添加到系统环境变量。
复制命令:
zinit ice as"program" cp"httpstat.sh -> httpstat" pick"httpstat"
zinit light b4b4r07/httpstat
上面的指令会下载插件 b4b4r07/httpstat
,cp
指令则将 httpstat.sh
拷贝到 httpstat
,再由于 pick
把插件目录加入到 $PATH
中,并添加执行权限。因为指定了 as"program"
所以后面的 pick
会将其作为可执行文件。
拷贝文件是一种安全的不影响更新的操作,原始的仓库没有修改,Git
不会有任何冲突。但是如果定义了合理的 atpull
,也可以使用 mv
zinit ice as"program" mv"httpstat.sh -> httpstat" \
pick"httpstat" atpull'!git reset --hard'
zinit light b4b4r07/httpstat
atpull
会在更新插件的时候执行,如果 atpull
以感叹号开始,表示会在 git pull
之前执行。
atpull
, mv
, cp
只会在有新的提交后执行。
如果用户使用 zinit update b4b4r07/httpstat
来更新插件,并且有新的提交被拉下来,那么
git reset --hard
执行,恢复原来的 httpstat.sh
git pull
执行,fast-forward 拉取最新的提交mv
执行wait
用于插件延迟加载。
用法一,wait'<number>'
, 表示在终端启动 number 毫秒之后,加载插件
zinit ice wait'1'
zinit light wfxr/forgit
用法二,有条件加载 wait'[[ ... ]]'
/wait'(( ... ))'
,当满足条件时,加载插件。
zinit ice wait'[[ $PWD = */github || $PWD = */github/* ]]'
zinit load unixorn/git-extra-commands '
用法三,wait'!...'
, !
表示在加载完成后重置控制台,用于主题加载
使用 as
关键字和 completion
可以将 snippet
的内容加入到 completion
。
zinit ice as"completion"
zinit snippet https://github.com/docker/cli/blob/master/contrib/completion/zsh/_docker
zinit 允许单独的禁用和启用每一个插件的自动补全。
zinit ice blockf
zinit light zsh-users/zsh-completions
可以通过 zi clist
查看插件提供的自动补全。
可以单独的启用和禁用补全:
$ zi cdisable cmake
Disabled cmake completion belonging to zsh-users/zsh-completions
$ zi cenable cmake
Enabled cmake completion belonging to zsh-users/zsh-completions
Zinit 可以使用 wait
ice-mod 来延迟加载插件。Zsh 5.3 以后可以使用。
zinit 升级
zinit self-update
升级其他插件
zinit update
清理没有加载的插件
zinit delete --clean
今天工作的电脑因为长时间没有关机,重启了一下之后发现竟然无法启动,显示 no bootable device。大概率是因为更新的时候把 Ubuntu 的 GRUB 给更新坏了。
首先需要通过可以启动的 U 盘系统开机,然后使用 grub-install
来安装 GRUB:
fdisk -l
sudo blkid
sudo mount /dev/sda1 /mnt
sudo grub-install --boot-directory=/mnt/boot /dev/sda
先使用 USB Ubuntu 系统,从 USB 启动系统,然后在上面安装工具 boot-repair 工具:
sudo apt-add-repository ppa:yannubuntu/boot-repair
sudo apt update
sudo apt-get install boot-repair -y
然后启动 boot-repair 自动找到对应的硬盘进行修复。
推荐使用这种方式进行修复。
在 Windows 上有一个类似的工具叫做:EasyBCD
修复完成之后再复盘之前的错误,大概率是因为我的系统是 Clonezilla 从之前的硬盘中拷贝过来的,所以我的引导分区在另外一块硬盘之上,但是更新系统的时候将这块引导分区给更新坏了。
# backup
dd if=/dev/sda2 of=$HOME/sda2.dd
#wipe it
dd if=/dev/zero of=/dev/sda2
大部分的资料来自于 GitHub 页面。
安装 npm,并执行:
npm install @alicloud/fun -g
两种方式对 fun 进行配置,
在项目 template.yml
文件所在目录,新建 .env
文件,并配置:
ACCOUNT_ID=xxxxxxxx
REGION=cn-shanghai
ACCESS_KEY_ID=xxxxxxxxxxxx
ACCESS_KEY_SECRET=xxxxxxxxxx
FC_ENDPOINT=https://{accountid}.{region}.fc.aliyuncs.com
TIMEOUT=10
RETRIES=3
执行 fun config
进行 Account ID、AccessKey ID、AccessKey Secret、Default Region Name 配置,完成配置操作后,Funcraft会将配置保存到您目录下的.fcli/config.yaml文件中。
在之前的文章中提到过 函数计算 但一直没有正式的用起来,现在正好通过在阿里云函数计算中连接访问 PostgreSQL 来系统性的学习一下阿里云的函数计算。 首先要了解的几个概念:
template.yml
配置文件可以对函数计算的服务, 方法, 网关 进行管理。更多内容可以参考官方提供的文档fun 命令的安装可以参考官方的文档.
配置 fun:
fun config
这里需要填写账号相关的信息。执行后会将账号相关的信息保存到:
`~/.fcli/config.yaml`
初始化项目模板:
fun init -n demo
fun 命令的执行依赖于 template.yml
配置文件。
本地调试:
fun local invoke
部署函数:
fun deploy
上传应用方式:
项目依赖 template.yml
配置,配置函数计算的服务名,函数名,触发方式等等。
创建 Funfile 文件,安装依赖:
RUNTIME python3
RUN fun-install pip install psycopg2
然后执行 fun install
yabai 是一个 Mac OS 上的平铺窗口管理工具。Linux 上很早就有一系列的平铺窗口管理工具,比如 i3, awesome 等等。yabai 将这个功能带到了 Mac 上。所谓的平铺式窗口管理,是相较于普通的浮动窗口管理,在通常使用的情况下,系统上的应用如果打开了很多,就不可避免的相互叠加,需要频繁的使用 ⌘+Tab 来切换窗口。而平铺式窗口管理,则将所有的窗口平铺在桌面上,窗口之间不会相互重叠。
在不了解平铺式窗口管理之前,我个人非常厌烦的一个事情就是不停地在不同的窗口之前切换,并且切换的效率非常低,虽然在 Mac 上有 Contexts 这样的软件来间接的提高窗口切换的速度,但是 Contexts 也需要一个模糊的查找来定位到需要切换的窗口。后来又发现了 Karabiner, 发现可以通过定义组合快捷键来快速切换到对应的应用,比如我定义了 oc 切换到 Chrome,ob 切换到 Obsidian, ok 切换到 kitty,这使得我在任何一个应用中都可以按下 o, 然后迅速的按下 c/b/k 等等来跳转到对应的窗口,即使这个应用窗口在后台,或者这个应用都没有开启,也会打开这个应用后将光标定位到该应用窗口。但这种情况下窗口大小的管理问题便随之而来,虽然我也用 Hammerspoon 定义了快捷键可以全屏,左右上下半屏,但窗口管理的其他一些问题还是没有得到进一步的改善,比如将窗口移动到第二个桌面,将窗口移到左边的显示器等等问题。
而平铺式的窗口管理,通过强可定制的快捷键将这些操作都固化成一定的动作,在熟悉这一些动作之后可以明显的提高效率,从窗口管理的麻烦中解脱出来。平铺式的窗口管理通常有如下特点:
如果要让 yabai 正常工作,需要 关闭系统完整性保护 System Integrity Protection,然后通过官网的教程 直接进行安装。
brew tap koekeishiya/formulae
brew install yabai
sudo yabai --install-sa
skhd 用于给 yabai 提供快捷键支持。
brew install koekeishiya/formulae/skhd
如果有其他的键盘映射工具,比如 Hammerspoon, Karabiner 也是可以的。
如果要调试 skhd 可以在安装时:
brew install skhd --with-logging
然后 skhd 就会将错误日志打印到 /usr/local/var/log/skhd/skhd.err.log
下。
yabai 的 wiki 已经解释了大部分的配置选项。
yabai 会自动加载 ~/.yabairc
配置文件。
配置的格式:
yabai -m <category> <command>
在配置调试阶段,不可避免的会需要多次加载 yabairc 配置,如果每次都要重启 brew services restart yabai
太慢了,yabai 提供了重新加载的方法,执行 :
launchctl kickstart -k "gui/${UID}/homebrew.mxcl.yabai"
也可以绑定快捷键
# e.g. bind to key in skhd:
# ctrl + alt + cmd - r : launchctl kickstart -k "gui/${UID}/homebrew.mxcl.yabai"
因为我自己的常用的快捷键是 hyper+r 重新加载 hammerspoon 的配置,所以改了一下 hyper+r 不仅重新加载 hammerspoon 配置,也重新加载 yabai 配置。
以 koekeishiya 的配置为例:
# change layout of desktop
ctrl + alt - a : yabai -m space --layout bsp
ctrl + alt - d : yabai -m space --layout float
ctrl + alt - s : yabai -m space --layout $(yabai -m query --spaces --space | jq -r 'if .type == "bsp" then "float" else "bsp" end')
说明:
ctrl + alt - a
就是平铺窗口模式ctrl + alt - d
就是浮动窗口模式ctrl + alt - s
切换模式使用者可以定义任何自己习惯的快捷键来替换这一套配置。
在显示器之间切换,显示器的编号可以在系统偏好中查看。
# Focus display focused before the current one (so you can alternate)
yabai -m display --focus recent
# Focus previous display by arrangement index
yabai -m display --focus prev
# Focus next display by arrangement index
yabai -m display --focus next
# Focus display with arrangement index 2
yabai -m display --focus 2
Space 指的是系统的不同虚拟桌面,yabai 可以快速的对不同的桌面进行管理。
将焦点放到不同的 Space 上
alt - 1 : yabai -m space --focus 1
alt - 2 : yabai -m space --focus 2
alt - 3 : yabai -m space --focus 3
alt - 4 : yabai -m space --focus 4
yabai 默认会使用二分的方式来划分窗口,一个窗口时会全屏,两个窗口时会左右对半,三个时会上下切分左边半个,依次类推。
下面的配置是将光标定位到同一个 Space 的不同窗口上。这个操作比较常见,使用 option 作为 modifier。切换活跃窗口的操作还是非常频繁的。
# focus window
alt - j : yabai -m window --focus south
alt - h : yabai -m window --focus west
alt - k : yabai -m window --focus north
alt - l : yabai -m window --focus east
这些定义分别对应着使得,j(下方), k(上方), h(左侧), l(右侧) 的窗口成为活跃窗口。
交换窗口的位置
alt + shift - j : yabai -m window --swap south
alt + shift - h : yabai -m window --swap west
alt + shift - k : yabai -m window --swap north
alt + shift - l : yabai -m window --swap east
同样可以定义如何移动窗口
shift + cmd - h : yabai -m window --warp west
shift + cmd - j : yabai -m window --warp south
shift + cmd - k : yabai -m window --warp north
shift + cmd - l : yabai -m window --warp east
调整窗口大小的操作我用的很少,有需要可以使用。
# move window
shift + ctrl - a : yabai -m window --move rel:-20:0
shift + ctrl - s : yabai -m window --move rel:0:20
shift + ctrl - w : yabai -m window --move rel:0:-20
shift + ctrl - d : yabai -m window --move rel:20:0
# increase window size
shift + alt - a : yabai -m window --resize left:-20:0
shift + alt - s : yabai -m window --resize bottom:0:20
shift + alt - w : yabai -m window --resize top:0:-20
shift + alt - d : yabai -m window --resize right:20:0
# decrease window size
shift + cmd - a : yabai -m window --resize left:20:0
shift + cmd - s : yabai -m window --resize bottom:0:-20
shift + cmd - w : yabai -m window --resize top:0:20
shift + cmd - d : yabai -m window --resize right:-20:0
开关窗口的浮动模式
alt - t : yabai -m window --toggle float && yabai -m window --grid 4:4:1:1:2:2
开关 picture-in-picture 模式
alt - p : yabai -m window --toggle border && \
yabai -m window --toggle pip
我个人将桌面上除了窗口以外的所有状态栏等等都隐藏了,如果要添加自定义的内容可以选择:
stackline 是 yabai 的一个增强,在窗口的 stack 模式上增加了一层可视化的显示,一个 stack 会在左上角的地方显示当前这个 stack 中的应用列表。
⌘+Shift+3
截取整个屏幕⌘+Shift+4
截取部分屏幕,按下快捷键之后选择⌘+Shift+5
打开截图应用,在应用中可以选择截图、录制视频等⌘+Shift+6
截取 Touchbar 的屏幕⌘+Shift+Ctrl+4
截取屏幕并保存到粘贴板⌘+Shift+4+space
截取某个窗口或菜单shottr 是一款 macOS 上的截图工具,支持部分,全屏,窗口,滚动截图。shottr 还支持文字打码。
我沿用之前的习惯:
[[Snipaste]] 是一款跨平台的截图工具,使用 QT 构建。
F3 直接贴图
iShot 是一款 macOS 上的截图工具,支持长截图,OCR,标注等等功能。
Jietu 是腾讯推出的截图工具。
[[CleanShot X for Mac]] CleanShot X 是一款收费的截图工具。
一次性付费 29$ 可以到的软件及 1GB 的 Cloud 空间。也可以选择月付费 10$,或者年付费,获得无限空间,自定义域名等服务。
Xnip 是一个 macOS 上的截图工具。
Covid-19 以来很少有让我继续追下去的韩综,除了带我入坑的周末综艺,一个 《[[Begin Again Korea]]》剩下的就只有《You Quiz On the Block》了。可以看得出来韩国的综艺人,电视人总还是带着一些社会关怀再做内容,之前在《Begin Again》里面就提过,在这个特殊的时代,他们把歌声第一时间先给一线的医护人员,公共服务人员,在这个疫情的特殊时期《Begin Again》就是一个治愈的存在,而相同的《You Quiz》第三期开篇就是对社会一线人员的致敬,公交司机,私营店老板,快递员,防疫人员,社会正是在这些人继续工作下去的时候才能维持正常的运行。
而在第三季的《You Quiz》中因为受到疫情影响,节目形式进行的改变,从原来街头的随机采访变成了主题式的室内采访,我甚至觉得这个模式似乎更有看点,主题也分外的明显,显而易见的比之前的模式要更加有中心主旨。虽然以前也会用一两个相同的问题来串起一个 Episode,但相较于现在而形式,也显得比较散。
再回到 E70,这一期的主题是世代差异。节目组分别邀请了 Z 世代,Y 世代,X 世代,386 世代,产业化世代 5 代人来讲述不同世代的故事。这一期的节目就像是一部倒放的韩国近代史。一直从 2000 年讲述到了上世纪 50,60 年代,当然叙事的方式并不是大而全的通用叙事,而是更加具体的从个人视角发出的主观的历史叙事。我作为千禧一代 (Y 世代)出生的人,最感同身受的却是 X 世代,或许另一个角度也能看出两个国家发展阶段的不同。
另外一个能察觉到节目组用心之处的便是节目的音乐,虽然韩综的配乐一直是长项,但这样的一个主题下,便更能体现其年代感,每一个世代所接触到的音乐,所关心的音乐人都不一样。生活在 2000 年以后 Z 世代是随手可得的音乐,Y 世代是放在自己的网络小屋的背景音乐,是自己的在庆典上偶像的歌曲,在到 X 世代是弘大,是夜店的歌曲,386 世代而言,音乐要珍贵许多,要守在广播前等待自己喜欢的歌手出现,再用录音机把歌声录下(杨熙恩,宋昌植)(采访的对象提到的杨熙恩 양희은 也是我非常喜欢的一位歌手,在 [[Fantastic Duo]] 短暂出现过),到更早一代,只能在异国他乡独自一人的晚上收听唯一的一档韩语广播。每一个世代都有自己喜欢的音乐,而有些音乐不像那个时代被一代人熟知,也依然可以穿过时间,直达现在每一个观众的内心,尤其是看那位远赴德国务工的老爷爷哼唱出《阳光明媚的日子 (해뜰날 by 송대관)》:
꿈을안고왔단다 抱着梦想来了 내가왔단다 我来了 슬픔도괴로움도 悲伤与伤心 모두모두비켜라 统统都让开 안 뒤는 일이없단다 有志者 노력하면은 事竟成 쨍하고해뜰날 阳光明媚的日子 고구간단다 回到故国 쨍하고해뜰날 阳光明媚的日子 고구간단다 回到故国
这几句歌词出来,直抵人心。音乐无论在哪个年代,虽然会以不一样的形式出现,虽然会被不一样的歌手所演绎,但总能给人以慰藉,这或许也是我独爱《Begin Again》的理由吧。
抛开音乐回到这几代人对历史时刻的最深记忆,而这些历史事件也逐渐的塑造了如今的韩国。从远到近说起。
大多数的历史性事件已经有大量的电影,进行了更加详细的记录和阐述。而现在的采访才更加有价值,事件的亲历者,亲自讲述那段苦难,虽然现在能笑着说出来,但背后却不知道隐藏了多少泪水。赴德国的老爷爷讲述第一次面试举麻袋失败后在家练习,亲历 1987 年 6 月运动后讲述为后辈捡起掉落的鞋子,却不料是最后一别。不禁让我感慨现实有的时候远比电影要残酷。老爷爷最后回答人生是什么的时候说,「人生是艺术」,不为过,也确确实实可以形容了。
唠叨会让人不爽,忠告会让人更不爽。
X 世代
经历事件:
386 世代是在 60 年代出生,在 80 年代上大学,在 90 年代是 30 多岁的世代。当时的电脑架构是 386,还是 486 的,还是奔腾的,这个时候登场的 386 世代。这是一个象征着当时主导抵抗军事政权的学生运动和工人运动等社会运动的世代。
[[柳烈的音乐专辑]] 中
电影 [[1987 黎明到来的一天』] 中有大量描述。
1945 年光复之后,韩国战争期间出生的世代。1960~1970 年代,游走世界,掀起产业发展,创造出「汉江奇迹」,大韩民国经济成长的主力,称之为「产业化世代」。 电影 [[国际市场]] 有讲述。
https://en.wikipedia.org/wiki/Miracle_on_the_Han_River ↩
之前在学习使用 dotbot 管理 dotfiles 的时候参考了不少 GitHub 上的 dotfiles 项目,发现大家都不约而同的用到了一个叫做 kitty 的终端,我个人在 Linux 上用 Guake ,在切换到 Mac 之后选择了大部分人推荐的 iTerm2,虽然用着也没有遇到问题,但一旦和 kitty 比较起来速度便成为了一个问题。
官网地址:https://github.com/kovidgoyal/kitty
之前使用的 [[Guake]], iTerm 自身也并没有什么问题,但近两年来越来越喜欢纯文本的配置,这样就可以放到 dotfiles 中进行追踪,并且跨平台也只需要同步一下配置即可,Guake 和 iTerm 在各自的平都是非常不错的选择,但都不是跨平台的选择。所以综上这些原因,让我有了尝试一下 kitty 的动力。
kitty 的一些特性:
kitty 默认的配置文件在 ~/.config/kitty/kitty.conf
,可以将这个文件拷贝到 dotfiles 仓库中管理,然后用软链接链过去。kitty 默认不支持热加载配置文件 1,所以每一次修改配置都需要退出重进。具体的配置可以参考我的 GitHub
调试 kitty 的配置可以使用 kitty --debug-config
,执行这行命令会将 kitty 当前的配置,以及加载的配置都打印出来。kitty 配置的各个选项在 kitty 的文档中已经非常详细的记录了。
include other.conf
自从发现了 Fira Code 字体已经用这一款字体很多年了。但是在 kitty 上使用时,Fira Code 字体总是在一行的偏上部分,这已经很多人反馈过 2.
目前的解决办法只能是:
adjust_line_height -3
adjust_column_width -1
借助 [[Hammerspoon]] 可以实现类似 Guake 类似的下拉效果。
hs.hotkey.bind({}, "F12", function()
local app = hs.application.get("kitty")
if app then
if not app:mainWindow() then
app:selectMenuItem({"kitty", "New OS window"})
elseif app:isFrontmost() then
app:hide()
else
app:activate()
end
else
hs.application.launchOrFocus("kitty")
app = hs.application.get("kitty")
end
app:mainWindow():moveToUnit'[100, 80, 0, 0]'
app:mainWindow().setShadows(false)
end)
我之前在使用 Guake 的时候就使用 Alt+n/p 来切换 Tab,正好键位在 Mac 上是 Cmd+n/p 用下面的代码重新 remap 一下:
map cmd+n next_tab
map cmd+p previous_tab
Karabiner 是 MacOS 上一款强大的自定义键盘的软件,可以非常自由的定义任何键位。Karabiner 是一个开源软件,用于在 macOS 系统上自定义键盘映射。它允许用户修改键盘输入以实现更高效的使用体验。
之前看文章是将 Caps Lock 作为一个 Hyper key,但看过 @nikitavoloboev 的文章之后,发现利用 Karabiner 和 Goku 定义的 DSL 配置语言可以更加充分的发挥 Karabiner 的功能。
Karabiner Elements 使用 JSON 作为配置规则的格式,但是使用 JSON 作为 Karabiner 配置格式的问题在于 JSON 格式非常庞大冗余,在生成之后几乎很难徒手去做修改,对于复杂的配置可能长达几万行。
所以诞生了 Goku,可以利用非常简洁的配置语法来配置 Karabiner 的 JSON 配置文件。 Goku 是一个基于 Karabiner 的配置工具,它使用了 EDN 格式来定义键盘映射和自定义操作。
在进入配置之前需要先了解一些前提知识。
Sticky Keys 叫做粘滞键,是方便无法同时按下 Ctrl C 这样组合按键的用户,启用粘滞键后按下任何 modifier 按键后,这个 modifier 按键会持续激活直到按下一个非 modifier 按键。
常见的 modifier 按键有 Ctrl, Command, Shift, Alt, Option,Fn, Caps Lock 等等。
A keyboard feature that enables you to press a modifier key (CTRL, ALT, or SHIFT), or the Windows logo key, and have it remain active until a non-modifier key is pressed. This is useful for people who have difficulty pressing two keys simultaneously.
其他可作为 modifier key 的按键有:
\
,
.
space
Tab
EDN 全称是 「extensible data notation」,是一种用于表示数据的轻量级文本格式。它的语法类似于 Clojure 语言的数据结构表示法,可以用于序列化和传输数据。
下面是一个最基本的 EDN:
{:main [
{:des "hello world"
; comments use semicolons
:rules [
[:a :b] ; map a key to b key
]}
]}
不用担心这里看不明白,后面会继续展开。上面的配置及时目前看不明白,可以大致了解到,是一条自定义的规则,描述和具体的 rule ,将按键 a 映射到 按键 b。
[[Karabiner Elements]] 是一个 MacOS 上的键盘自定义工具。
准备工作:
brew install yqrashawn/goku/goku
goku
服务,让修改立即生效 brew services start goku
~/.config/
目录下创建 karabiner.edn
文件,可以参考我的Goku 会通过 EDN 文件生成 karabiner.json
,编写好 edn 文件后可以执行 goku 来生成 JSON 配置。
brew install yqrashawn/goku/goku
安装之后配置文件默认位置在 ~/.config/karabiner.edn
但是可以使用环境变量 GOKU_EDN_CONFIG_FILE
自定义配置路径。
服务其中之后,日志在 ~/Library/Logs/goku.log
,任何错误都可以查看。
完成安装之后可以运行 goku --version
来查看版本。可以使用 gokuw
来持续监控配置文件生成 karabiner.json
重启服务
brew services restart yqrashawn/goku/goku
查看服务配置
less ~/Library/LaunchAgents/homebrew.mxcl.goku.plist
整个 EDN 配置大体可以分成几个部分:
接下来就一步步看一下最简单的配置。
从最主要的 main 部分配置来看。
{:main[ {:des "..." :rules [[<from> <to> <condition>]]}
{:des "..." :rules [[<from> <to>]
[<from> <to>]]} ]}
:des
部分用来注释规则:rules
中是真正的规则from
, to
, condition
,其中 condition
部分是可选的。更加详细的配置可以参考我的 dotfiles。
可以使用 bundle ID 来定义应用程序,如何查找这个 Bundle ID,可以利用 Karabiner 自带一个 EventViewer 工具,很方便地查看应用的 Bundle ID,或者右键『应用.app』-> 显示包内容 Contents/Info.plist
-> BundleIdentifier
也可以查看到。
:applications {:chrome ["^com\\.google\\.Chrome$"]}
在 Karabiner EventViewer 中
定义设备,同样设备的 ID 也可以在 EventViewer 中查看:
:devices {:quickfire [{:vendor_id 1234 :product_id 17}]}
通常当你有外置的键盘等等设备时会使用到这个。比如当你有不同布局的外置键盘的时候,可以配合 [[Hammerspoon]] 来自动切换 Karabiner 的配置。
定义输入法,也可以通过应用的 ID 来配置:
:input-sources {:squirrel {:input_mode_id "com.googlecode.rimeime.inputmethod.Squirrel"
:input_source_id "com.googlecode.rimeime.inputmethod.Squirrel.Rime"
:language "zh-Hans"}
:us {:input_mode_id ""
:input_source_id "com.apple.keylayout.US"
:language "en"}}
变量条件定义:
[:escape [:escape ["in-alfred" 0]] ["in-alfred" 1]]
;; |<from>||_________<to>__________| |<conditions> |
这一条规则表示的含义是,当变量 in-alfred
等于 1 时,tap Escape 按键映射到 Escape 并将 in-alfred
变量设置为 0 。
在使用条件的时候可以组合使用,或者使用非语句。
比如,先定义了应用,然后将预先定义的应用到规则中。
{:applications {:chrome ["^com\\.google\\.Chrome$"]
:safari ["^com\\.apple\\.Safari$"]}
:main [{:des "a to 1 only in chrome" :rules [[:a :1 :chrome]]}
{:des "a to 1 only in chrome, safari" :rules [[:a :1 [:chrome :safari]]]}
{:des "a to 1 only outside chrome, safari" :rules [[:a :1 [:!chrome :!safari]]]}]}
上面的这三条规则就是表示
或者组合使用:
:main [{:des "a to 1 multiple conditions"
:rules [[:a :1 [:chromes :quickfire :us]]]}]}
这条规则就表示在 Chrome 中,使用外置的 quickfire 键盘,并且输入法是 us 时,将 a 键映射到 1。
有了这样的组合,就可以非常轻松地实现一个 Windows 布局键盘修改成 macOS 使用的键盘,比如将 Ctrl 映射成 Command。
简单规则,一个键映射到另一个按键,一个键映射到多个按键
{:main [{:des "a to 1" :rules [[:a :1]]}
{:des "b to 2" :rules [[:b :2]]}
{:des "c to insert 123" :rules [[:c [:1 :2 :3]]]}]}
多个按键映射到其他按键,比如同时按下 j,l
映射到 F20
:rules [[:j :l] :f20]
配置也可以将按键映射到 Shell 脚本
:rules [{:des "hyper 1 to clean tmp"
:rules [[:!!1 ["rm -rf /tmp/*"]]]]}
对于常见的修饰键 modifier,Goku 中用简化的配置来表示
;; ! | means mandatory - modifier(s) alone when pressend change behavior
;; # | means optional - modifiers are optional (but at least one necessary)
;; :!Ca is keycode :a and prefix a with !C
;; C | left_command
;; T | left_control
;; O | left_option
;; S | left_shift
;; F | fn
;; Q | right_command
;; W | right_control
;; E | right_option
;; R | right_shift
;; ## | optional any
;; !! | command + control + optional + shift (hyper)
说明:
mofifiers
时,键映射只有在没有 modifier 时才生效,比如定义了一条规则,将 Caps Lock 映射成 Escape,那么只有在单独按下 Caps Lock 的时候才会映射成 Escape,如果组合按键比如 Left Shift+Caps Lock(当然应该没人那么做) 的时候,表现还是 Left Shift+Caps Lockmandatory
时,只有 modifier 按下时,映射才会触发{:templates {:launch "osascript -e 'tell application \"Alfred 3\" to run trigger \"launch %s\" in workflow \"yqrashawn.workflow.launcher\" with argument \"\"'"}
:main [{:des "launcher mode"
:rules [[:k [:launch "Emacs"] :launch-mode]
[:l [:launch "Chrome"] :launch-mode]
[:m [:launch "Mail"] :launch-mode]
[:v [:launch "WeChat"] :launch-mode]]}]}
定义 simlayer
:simlayers {:vi-mode {:key :d}}
上面的规则定义了如果按下了 d
按键,则设置变量 vi-mode
为 1,表示进入 simlayer vi-mode,to_if_alone d
, to_after_key_up
然后设置变量 vi-mode
到 0 。
当我使用外置键盘的时候交换 Option 和 Command 的按键。
首先定义一些设备:
:devices {
:apple [{:vendor_id 1452 :product_id 832}]
:quickfire [{:vendor_id 9494 :product_id 17}]
}
特定键盘更改键位,改完之后就可以和内置键盘的键位一样了,不用去熟悉两套键盘了。
可以使用两种不同的方式进行配置:
这里的 quickfire
是我的外接键盘。这些信息可以在 Karabiner 提供的 EventViewer 中查看。
然后在 main 部分定义:
{:des "swap cmd <-> option when using specific devices"
:rules [
[:##left_command :left_option [:quickfire]]
[:##left_option :left_command [:quickfire]]
] }
或者
{:des "CM Storm keyboard setup"
:rules [:quickfire
[:##left_command :left_option]
[:##left_option :left_command]
]}
tap caps lock once -> Escape key
hold caps lock -> hold Ctrl+Shift+Option+Command at same time
在英文的世界中,有一种 Remap,是将
left_shift once -> type (
right_shift once -> type )
但是在中文的世界里面,我的 Shift 是作为中英文切换按键,非常重要的一个按键。
如果经常在几个常见的应用之间切换,即使用了不错的比如 Context 这样的窗口管理工具,那也会在 Command + Tab 按键中非常频繁的按键。假如有方式可以通过按下键盘上的快捷键就可以直接切换到不同的窗口,是不是可以省去不少的烦恼。
下面是一个模式,通过按住 O,然后快速按下第二个按键就可以实现在常用的应用之间切换。
hold o + tap i -> launch "iTerm"
hold o + tap c -> launch "Chrome"
hold o + tap b -> launch "Obsidian"
tap o -> type "o"
记住一定要按住 o 不要松开然后再按 c 就可以快速切换到 Chrome。 一旦熟悉了自己的配置,就会发现再也不需要使用 Cmd + Tab 来切换了。
{:des "caps lock -> escape(alone) and caps lock -> hyper"
:rules [
[:##caps_lock :!CTOleft_shift nil {:alone :escape}]
]}
{:des "Ctrl np -> down up"
:rules [
[:!Tn :down_arrow [:ctrlnp]]
[:!Tp :up_arrow [:ctrlnp]]
]}
{:des "Disable Cmd+H Hide"
:rules [
[:!Ch nil nil [:kybs]]
]}
在默认情况下 macOS 按下 Cmd + q 就会退出当前应用,没有任何提示,使用这个配置之后就必须同时按下 Cmd + q 一秒钟时间才会触发退出。
{:des "Cmd + Q held 1 second to quit"
:rules [
[:!Cq nil nil {:held :!Cq }]]}
;; ! | means mandatory - modifier(s) alone when pressend change behavior
;; # | means optional - modifiers are optional (but atleast one necessary)
;; :!Ca is keycode :a and prefix a with !C
;; C | left_command
;; T | left_control
;; O | left_option
;; S | left_shift
;; F | fn
;; Q | right_command
;; W | right_control
;; E | right_option
;; R | right_shift
;; ## | optional any
;; !! | command + control + optional + shift (hyper)
;; to understand better how modifiers work in karabiner
;; karabiner definition of mandatory and optional
;; https://karabiner-elements.pqrs.org/docs/json/complex-modifications-manipulator-definition/from/modifiers/
;; need to prefix C T O S F with ! or #
;;
;; code for all this:
;; https://github.com/yqrashawn/GokuRakuJoudo/blob/b9b334a187379f9bc8182ad59e2cca2a1789e9c0/src/karabiner_configurator/keys.clj#L68
其他配置案例:
一直都使用手动的方式来管理 dotfiles
,之前一方面是学习,一方面是熟悉整个配置,但随着配置文件的不断增多,管理便成为了一个问题。今天本来是在看 Karabiner 的配置,然后找到了一些参考,发现 narze 使用 dotbot
来管理其配置。便顺手也把我的 dotfiles 改了一下。
dotbot
的原理非常简单,就是将配置文件软链接到特定的位置,使得 Vim,Tmux, zsh 之类可以直接使用。然后 dotbot
使用 YAML 定义的一套配置格式将整个过程简化。使得最后可以直接运行 git clone git@github.com:einverne/dotfiles.git && cd dotfiles && make bootstrap
一键完成初始化。
最基本的使用方法,参考说明:
cd ~/.dotfiles # replace with the path to your dotfiles
git init # initialize repository if needed
git submodule add https://github.com/anishathalye/dotbot
git config -f .gitmodules submodule.dotbot.ignore dirty # ignore dirty commits in the submodule
cp dotbot/tools/git-submodule/install .
touch install.conf.yaml
将其加入为作为 submodule 然后添加一个配置 install.conf.yaml
即可。
更新子 module:
git submodule update --remote
dotbot 的配置文件是 yaml
格式,非常易读。这是官网给的一个例子:
- defaults:
link:
relink: true
- clean: ['~']
- link:
~/.tmux.conf: tmux.conf
~/.vim: vim
~/.vimrc: vimrc
- create:
- ~/downloads
- ~/.vim/undo-history
- shell:
- [git submodule update --init --recursive, Installing submodules]
目前 Dotbot
定义了一系常用的动作,比如 link, create, shell, clean 等等。
defaults 定义了默认的行为。
link 命令定义了文件或文件夹如何 symbolically linked。
create 命令定义了回去创建这些目录。
Shell 命令则会指定需要运行的命令。
clean 命令会去检查目录下的连接,如果发现连接已经无效则会移除这些连接。