IPFS 的全称是 「InterPlanetary File System」,直译过来叫做「星际文件系统」,这是一个点对点的媒体传输协议,作者创建这个项目的目的是为了建立一个持久的,分布式的文件系统。1
A peer-to-peer hypermedia protocol designed to make the web faster, safer, and more open.
IPFS 白皮书由Juan Benet 发表于 2014 年。
IPFS 允许用户不仅可以接受文件,还可以托管文件内容,类似 BitTorrent 协议的方式,网络节点中的每一个节点都可以既是客户端也是服务端。
和中心化的系统不一样的地方在于,IPFS 构建了一个去中心化的系统,任何用户都可以存储所有数据中的一部分,IPFS 网络创建了一个可以快速恢复的文件存储和分享系统。
任何用户都可以通过内容地址来分享文件,网络中的任何对等节点都可以通过分布式散列表 (Distributed Hash Table DHT) 来查找和请求文件内容。
IPFS 项目源码:https://github.com/ipfs
在白皮书中,作者概括了 IPFS 的设计:
首先,让我们来看一下目前的互联网,现在互联网上的大部分内容都依赖于一些大型或小型的服务器托管商。如果你要架设一个网站,你需要花钱购买一个服务器,或者找能够托管内容的提供商,然后将产生的内容放置到服务中,这样当你提供内容时,别人都可以访问这一个中心化的节点来获取。而对于 IPFS ,任何人都可以注册一个节点,开始托管自己的内容,不管是在 Raspberry Pi 上,还是跑在世界上最大的服务器集群中,你自己的节点都可以成为一个非常高效的节点。因为即使你这个节点宕机了,只要在网络上还有地方存储着这部分内容,其他人都可以获取到。
IPFS 网络和普通网络的第二点区别在于,IPFS 的数据是内容寻址 (content-addressed),而不是地址寻址 (location-addressed). 这是一个微妙的区别,但是结果却是巨大的。
目前如果你打开浏览器,输入 example.com
,你是告诉浏览器「帮我获取存放在 example.com 的 IP 地址的数据」,这可能存放在 93.184.216.34 这台服务器中,然后你就请求这个 IP 地址的内容,然后服务器会将相关的内容返回到浏览器。(当然现代网络依赖的 DNS 系统,以及浏览器内部的实现细节这里就略过)。所以基本的逻辑是,你告诉网络你要查找地址,然后互联网会将找到的内容返回。
但是 IPFS 扭转了这一逻辑。
在 IPFS 网络中,每一个存放在系统的单一区块数据都会生成一个由自身内容产生的密码散列 (Hash),也就是说,每一个块都会有一个唯一的由字符串和数字组成的串。当你想要在 IPFS 网络中获取数据时,你会请求这一个 HASH,所以并不是请求网络说「告诉我存放在 93.184.216.34 这个地址的内容」,而是说「请将 Hash 值为 QmXnnyufdzAWL5CqZ2RnSNgPbvCc1ALT73s6epPrRnZ1Xy 的内容告诉我」,而 QmXnnyufdzAWL5CqZ2RnSNgPbvCc1ALT73s6epPrRnZ1Xy
正好是一个包含了 “I’m trying out IPFS” 的 .txt
文件的 Hash。
那这样做有什么好处呢?
首先,这使得网络更有弹性,Hash 值是 QmXnnyufdzAWL5CqZ2RnSNgPbvCc1ALT73s6epPrRnZ1Xy
的内容可能被存放在成千上万的节点中,即使有一个节点 Crash 或者下线了,也不影响其他缓存过这个 Hash 的其他节点。
第二,这个方式提高了安全级别。比如说你想要某一个 Hash 的文件,所以你向网络请求,给我 Hash 值是 QmXnnyufdzAWL5CqZ2RnSNgPbvCc1ALT73s6epPrRnZ1Xy
的内容,然后网络响应请求,然后发送数据。当你接受了所有的数据,你可以重新计算 Hash,如果数据在传输的过程中被更改了,那么你重新计算的 Hash 就和请求的 Hash 不一致。你可以想象 Hash 就像是文件的唯一指纹。如果你接收到了一个和希望的不一致的内容,他们将拥有不同的指纹。这意味着这个方式实现的网络会知道这个内容是否被篡改了。
和传统的互联网相比,IPFS 不仅解决了内容从互联网消失的问题,并且在抵抗审查,抵抗大规模监控等等方面都要比传统的互联网要有优势。
既然上面提到了 content-addressed 系统的独特性,这就值得再来聊一聊 IPFS 地址是如何产生的。
每一个 IPFS 地址都是一个 multihash,这意味着每一个地址既包含了 Hash 算法也包含了 Hash 值。
IPFS multihashes 有三个不同的部分:
默认情况下,IPFS 使用 SHA-256 Hash 算法,会产生一个 32-byte 的 Hash。然后使用 Base58 来表示,这也就是为什么每一个 IPFS 地址都以 Qm...
开头。
虽然 SHA-256 算法是当今的标准,但是这个 multihash 格式允许 IPFS 协议自由的更改 Hash 算法。这就使得 如果在未来发现了 SHA-256 算法的缺陷,IPFS 网络可以迁移到另外的算法。如果有人使用其他的 Hash 算法,那么最后的地址可能就不是以 Qm
开头了。
经过上面这么多解释可以知道,IPFS 本质上是一个分布式的文件共享系统,所以互联网能用来做什么,IPFS 也能做到。并且 IPFS 可以做的更好。
适合下面的场景:
IPFS Gateway 网关提供了互联网用户访问托管在 IPFS 网络上内容的一种能力。ipfs.io
网关是由社区运营由 Protocol Labs 资助以帮助开发者的工具。
其他一些公共的网关可以在这个列表里面找到
如果想要了解更多 IPFS Gateway 相关的内容,可以到 https://docs.ipfs.io/concepts/ipfs-gateway/ 了解。
上传到 IPFS 网络的第一张图。
https://ipfs.io/ipfs/QmcTzSJspTbafYWR1B8RqncNcvsaxnKQJmbtTU6GUkLJ8j
在 目录 中。
IPFS 使用基于文件的寻址,这就使得分享文件的时候会有一大串的 Hash,并且一旦更新文件后,就会产生一个新的 Hash 值。
IPNS 全称是 The InterPlanetary Name System,IPNS 就是用了创建可以用于更新的地址。
IPNS 中的名字是一个公开密钥的 Hash,它会和一条记录相对应,这条记录被对应的私钥签名。新的记录可以被多次发布。
ipns
的地址会有一个前缀:
/ipns/yourname
拥有这样一个机制后就可以通过自己设定绑定到 IPFS Hash 的记录,然后通过该记录来访问。
使用如下命令会返回节点 ID
ipfs name publish QmQsLcmxzAh7Y6Ho1Nt8bispVmeHqjzdGBjG5m8KoGYjGi
使用 ipfs id
查看节点 ID。复制这一步的节点 ID,验证
ipfs name resolve your_ID
同样的 ID 可以使用 IPNS 来访问。
注意这里 URL 的 ipns
区别。
如果想要发布一个友好的地址,也可以使用 DNSLink
IPFS 允许用户直接使用现有的域名,这样就可以用一个简单的域名来访问。
只需要在 DNS 解析里面增加一条 TXT 记录,指向:
dnslink=/ipns/12D3KooWMrZpzzoSA2uxZiQ8NSizEK9A8SduhxcAc4yUB8imxXqU
IPFS 节点默认情况下会将文件认为是缓存 (cache),这意味着 IPFS 不会一直保存着文件。Pinning
一个文件就是告诉 IPFS 节点将这个文件视为重要的文件,不要抛弃这个文件。
将本地文件添加到 IPFS
ipfs add filename
同理增加文件夹
ipfs add -r directory
获取一个远程的文件,并指定一个名字,但是不 pin 它:
ipfs get hash -o outputname
pin 一个远程的文件
ipfs pin add hash
显示本地 pin 过的文件
ipfs pin ls
从本地 unpin 一个文件
ipfs pin rm hash
移除本地 unpinned 的文件:
ipfs repo gc
使用密钥工具,创建密钥:
<https://github.com/Kubuxu/go-ipfs-swarm-key-gen>
然后将密钥放到 IPFS 默认配置下 ~/.ipfs/
然后启动 ipfs init
,默认情况下连接的是公网节点,如果要连接私有网络,删除所有的启动节点,然后手动添加自己的节点:
ipfs bootstrap rm --all
ipfs bootstrap add node
查看节点:
ipfs swarm peers
最后借助 fleek 可以快速的将 GitHub 中托管的静态网站镜像一份到 IPFS 网络。
~/.gitconfig
配置用来存储用户相关的配置,当 git 在提交或其他操作时,如果找不到项目目录下的 .git/config
文件时会回退到使用该全局配置文件。
大部分的配置可以通过 git config
来配置,比如常见的设置用户名和密码。
git config user.name "Ein Verne"
git config user.email "some@one.com"
通常情况下只需要维护一份全局的 ~/.gitconfig
然后在各自的项目中维护自己的 gitconfig
即可,但是我最近遇到一个问题便是,我迁移了几十个项目到另外一台机器中,这些项目我需要一个 ~/.gitconfig-work
的配置,用来区别和其他 git config
配置中使用的用户名和邮箱。
比如经常见到的 work 中有一个工作邮箱,自己在使用 GitHub 时有一个自己的邮箱,另外在其他开源项目中有一个独特的用户名和邮箱。这个时候就需要使用到 git 配置中的 includeIf
配置。
一份正常的 ~/.gitconfig
配置可能是这样的:
[user]
email = someone@gmail.com
name = Ein Verne
signingkey = 92
[push]
default = matching
[alias]
unstage = reset HEAD --
a = add
b = branch
[commit]
gpgsign = true
[gpg]
program = gpg
[includeIf "gitdir:~/play/"]
path = .gitconfig-play
[includeIf "gitdir:~/projects/"]
path = .gitconfig-wk
中间略有省略,不过大致的格式是这样。注意到最后的 includeIf
配置。
上面两行表示的意思就是对于 ~/play/
下面的项目,使用 ~/.gitconfig-play
配置。
看一下 ~/.gitconfig-play
的配置。
[user]
email = some@play.com
name = Alex
然后对于 ~/projects/
下面的项目,就使用 ~/.gitconfig-wk
配置。
一直使用的 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 是一款收费的截图工具。
一次性付费 29$ 可以到的软件及 1GB 的 Cloud 空间。也可以选择月付费 10$,或者年付费,获得无限空间,自定义域名等服务。
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