- 一个窗口可以横跨接缝,一半在左屏一半在右屏,而且不会被画两次。
- 把它从一块屏拖到另一块屏,是一整段连续的位移,松手就停在那儿。
- 平移、缩放、落到某个窗口上,都会同时带动所有屏幕 —— 因为那就是同一张画布。
和「每屏一个独立相机」的做法比,这个方案的代价是要处理一堆边界情况:窗口归属哪块屏(答案是「显示它最多的那块」)、每屏各自全屏、X11 应用以为自己在哪里……项目专门写了一份 docs/window-rules.md,把「每一条 Hyprland 按键在画布上意味着什么」逐条写死,而且每条规则后面都标注了由哪个测试覆盖。
真正的硬骨头:让「平铺的按键」在画布上说得通
这是整个项目最容易被低估的部分。Hyprland 有一大套围绕平铺和工作区的按键:方向键聚焦邻居、SUPER + 1..0 切工作区、SUPER + J/P/L 调平铺布局、SUPER + HOME 移动工作区……到了画布上,这些键要么得重新赋予含义,要么得明确地「什么都不做」,而且必须在每一种窗口状态下都成立:窗口在一块屏上、横跨接缝、在屏幕外、被 fill 过、已经退到缩略视图。
项目给每种情况定的判据很硬:「没有别的窗口移动 / 窗口保持浮动且被绘制 / 两块屏处在同一个视图层级 / 退开再回来什么都不变」。例如 SUPER + G 建组的键,在画布上被显式定义为「什么都不做」,理由写在文档里 —— 在画布上堆叠的组,会让 Hyprland 在退出时崩溃。这种「把不支持的东西写清楚并让它安全地无效」的做法,比悄悄给个半吊子行为要靠谱得多。
全屏、填充、钉住:三个各自独立的语义
- 填充(
SUPER + T)把窗口撑满它所在的那块屏,算上 gaps 和边框;再按一次回到原来的位置和大小。中途移动过就以新位置为准,中途缩放过就重新撑满。 - 全屏(
SUPER + F)只让那一块屏的画布让开,其他屏幕该什么样还什么样。每块屏可以各自有一个全屏窗口。Wine / Proton 游戏那种「无边框窗口 + 覆盖整屏」会被识别成全屏,退回窗口模式时恢复到游戏自己要的尺寸;游戏在「战斗闪一下」时不会退出全屏;游戏藏起光标做鼠标视角时指针老老实实待在那块屏上。 - 钉住(
SUPER + O)让窗口钉在屏幕上不动,画布平移时它悬在画布之上;再按一次放回画布当前位置。
另外还有一档实验性的「地点」(canvas.places = true):工作区键被映射成画布上的一个个位置,SUPER + 1..0 飞过去,Shift + SUPER + N 把当前窗口搬过去,SUPER + TAB 去隔壁那个(哪怕它是空的)。去的时候 minimap 上会显示你正飞向哪里。默认是关的,因为这套语义还在打磨。
图标解析:不打包任何图片,直接问你的桌面
画布上要给每个窗口画图标,项目没有自带任何图标资源。它做的是按启动器的规矩去解析:拿窗口的 app id 去匹配 .desktop 文件,依次尝试 StartupWMClass、文件名、Name;拿到 Icon= 之后,在「当前图标主题 → 它 Inherits 的主题(递归,深度上限 6)→ hicolor → /usr/share/pixmaps」这条链上按尺寸子目录顺序找 SVG 或 PNG。
里面有几个只有真实使用才会踩到的补丁:
- 反向域名 id 的兜底:
org.gnome.Nautilus这种 app id,桌面文件常常只叫nautilus,所以要拿最后一个点后面的部分再查一次。 - Omarchy 的网页应用:它们是
chromium --app=URL跑起来的,窗口类名长成chrome-<host>__-Default。项目在扫 desktop 文件时顺手把Exec里的 URL host 记下来,再拿 host(必要时逐段砍掉后面的路径)去反查图标。 - 缓存按
path@size建键,超过 256 个就整体清空;更重要的是,它提供了一个resolve()—— 在动画开始之前先把文件系统扫描做掉,而不是在动画中间去扫盘。
调参:所见即所得,而且不会覆盖你的配置
在退开的视图里按 CTRL + ,,搜索框会原地变成设置列表。打字筛(blur、grid dot、accent),上下选,左右调,改一下就能立刻看到;Shift 微调、CTRL 粗调、Delete 撤销这一项。
可调的项接近 80 个:镜片强度 / 边缘模糊 / 暗角 / 色散 / 过渡时机、网格的间距线宽点径样式、minimap 的位置透明度、HUD 的字体和字号和圆角、视差壁纸的速度和深度、智能整理时的尺寸相似度阈值、窗口自动放置的碰撞间隙……
这里有一处工程上很正确的处理:调参结果写到单独的文件 ~/.config/hypr/spatialoverview-tuning.lua,它在 spatialoverview.lua 之后加载。所以升级不会覆盖你的设置,而调过的值会「赢」到你手动删掉那一行为止。同理,scripts/install.sh 也只往 hyprland.lua 里加两行带标记的语句,加之前先备份。
关于「改了名字但内部没改」这件小事
项目原来叫 Spatial Overview,改名成 Phantomat 之后,设置文件名、hl.plugin.spatialoverview.* 动作、hyprctl spatialoverview 命令全部保留了旧名,就是为了让改名之前的配置继续能用。这种取舍看起来不优雅,但比强行统一再让所有人重写配置要友好。
安装
需要 Hyprland 0.56 或更新(并且是它带 Lua 配置的那套)、GCC 15 或更新,以及 Hyprland 的开发文件。Arch / Omarchy 上:
sudo pacman -S --needed base-devel git hyprland hyprgraphics pango lua
git clone https://github.com/kaolti/phantomat.git
cd phantomat
scripts/install.sh
安装脚本会:编译插件 → 放到 ~/.local/share/spatial-overview/ → 备份并往 hyprland.lua 插两行 → 立刻加载。然后按 SUPER + CTRL + G 就能看到效果。不想用了跑 scripts/uninstall.sh,桌面恢复原状、设置文件保留(要连设置一起删就加 --purge)。
必须说清楚的代价
这是一款深度 hook 进 Hyprland 内部的插件,README 自己写得很坦白:Hyprland 一升级,插件就需要重新编译,否则会直接提示「这个 build 是给另一个 Hyprland 版本用的」,跑一遍安装脚本即可。它目前只在一套配置上被长期日常使用(Omarchy + Hyprland 0.56.2 + NVIDIA + 双屏),其他组合的测试覆盖要弱得多。
另外它是在 yayuuu/hyprland-scroll-overview 的基础上长出来的 fork,上游代码和历史都保留在仓库里 —— 包括那个 480KB 的 scrollOverview.cpp。两个项目之间没有隶属或背书关系。
为什么值得一看
它最值得看的地方不是「无限画布」这个点子 —— 这个点子本身不新 —— 而是把点子落地成了什么:用自己写的 GLSL 兑现镜片效果、把多屏的边界情况一条条写成规则再一条条挂上测试、给系统主题做 icon 回退、把调参结果隔离到单独文件、在不支持的行为上选择「安全地什么都不做」并说明理由。这些东西都不会出现在截图里,但决定了它能不能真的每天用。
顺带一提,9 月 26 日刚发布的 phantomat 同期还有一位邻居:同一个 Omarchy 生态里的 omarchy-meeting-recorder,把麦克风和系统声音录成两条独立的轨再转写。看得出来这个围绕 Omarchy 的小工具生态正在快速长出来。
封面图按项目自己的视觉语言生成:桶形镜片变形与暗角直接复刻自 shaders/barrel.frag 的公式。项目本身不打包任何图标资源,所以没有可用的官方 logo。
先把「无限画布」这件事说清楚
Hyprland 上管窗口,传统思路是「工作区 + 平铺」:窗口被切成互不重叠的格子,工作区像抽屉一样一个叠一个。Phantomat 走的是完全相反的一条路 —— 它把工作区取消了,让所有窗口都活在同一张无限的二维平面上。
这个平面的名字有点中二:出自斯坦尼斯瓦夫·莱姆《技术大全》(Summa Technologiae)里的 “phantomat” —— 一台能在人周围造出整个世界的机器。项目用这个名字,大概是想说「整张画布就是你看到的世界」。
项目地址:github.com/kaolti/phantomat,BSD-3-Clause 协议,2026 年 9 月 24 日建仓,三天冲到一千多星。作者是在 Omarchy(DHH 那个「有主见的 Arch 发行版」)上日常用出来的。
它到底做了什么不同的事
平铺窗口管理器把屏幕当成一张纸,窗口是纸上的方块,位置没有意义。Phantomat 反过来:窗口的位置本身是有意义的。你把一个窗口放在另一个窗口右边,它就一直待在右边;你把它们拉开一屏远,它们就真的差一屏。
要看到全局,按 SUPER + CTRL + G。这一步不是普通的「概览」:镜头会穿过一圈弯曲的镜片退开,整个桌面像被吸进一个桶形镜头里,带着边缘发散、暗角和色散。再按一次回到 100%。
这个镜头不是贴图,是项目自己写的 GLSL。关键的一段在 shaders/barrel.frag 里:
// Radial lens mapping. Signed strength lets the user move continuously
// between barrel and pincushion distortion.
vec2 warped = centered * (1.0 + distort * radius2) / max(contrast, 0.001);
vec2 uv = warped * 0.5 + 0.5;
// 0 at the center of the screen, 1 in its corners.
float radius = sqrt(radius2) * 0.70710678;
float ramp = smoothstep(min(edgeBlurStart, 0.99), 1.0, radius);
vec2 outward = radius > 0.0001 ? centered / sqrt(radius2) : vec2(0.0);
// The fringe has its own, wider falloff: it grows from near the
// center, quadratically, as in a real lens.
float fringeRamp = smoothstep(0.1, 1.0, radius);
vec2 fringe = outward * chromatic * fringeRamp * fringeRamp * 0.014;
distort 是有符号的,所以同一个参数能从桶形畸变连续滑到枕形畸变。edgeBlur 那段用的是「黄金角圆盘采样」,越靠圆心越密,模仿真实镜头的散景。而且 HUD 和 minimap 是在这个 shader 的最后一步合成的,意味着搜索框的文字永远不会被镜片拧弯 —— 这个细节在很多「花哨效果」的实现里都会翻车。
键鼠操作:键盘优先,但鼠标也没被落下
退开之后可以直接打字搜索。这不是简单的过滤:
- 每个词都必须出现,且字母必须连着。打
fas能找到fastfetch;因为每一个字母都必须按顺序相邻,搜索几乎不会给出含糊的结果。 - 大小写和重音符号全部忽略。
- 镜头和焦点会跟着「最匹配的那个窗口」走 —— 你一边打字,画面一边自己往目标滑。
- 命中后用
Enter直接在 100% 落上去,Shift + Enter则是把它搬到你现在的位置再落上去。
搜索框旁边的操作是全套的:方向键选最近的窗口、CTRL + 数字 直达第 N 个结果、CTRL + 0 把所有匹配的窗口框进视野、CTRL + A 按应用把窗口整理成阵、CTRL + Z / CTRL + Shift + Z 撤销重做移动和整理。鼠标这边:点窗口飞过去、拖窗口就是移动、拖空白就是平移画布、滚轮缩放、点 minimap 跳转。
值得一提的是 ALT + TAB:在画布上它被重写成了「最近使用顺序」的切换器,轻点一下换到上一个窗口,按住 ALT 会直接把列表摊在屏幕上,松开就跳过去。
两块屏幕是同一张桌子
这是我觉得设计得最讲究的一部分。在 canvas.linked_screens(默认开)下,多个显示器展示的是同一张画布上相邻的部分,按显示器的实际摆放位置排列。于是:
- 一个窗口可以横跨接缝,一半在左屏一半在右屏,而且不会被画两次。
- 把它从一块屏拖到另一块屏,是一整段连续的位移,松手就停在那儿。
- 平移、缩放、落到某个窗口上,都会同时带动所有屏幕 —— 因为那就是同一张画布。
和「每屏一个独立相机」的做法比,这个方案的代价是要处理一堆边界情况:窗口归属哪块屏(答案是「显示它最多的那块」)、每屏各自全屏、X11 应用以为自己在哪里……项目专门写了一份 docs/window-rules.md,把「每一条 Hyprland 按键在画布上意味着什么」逐条写死,而且每条规则后面都标注了由哪个测试覆盖。
真正的硬骨头:让「平铺的按键」在画布上说得通
这是整个项目最容易被低估的部分。Hyprland 有一大套围绕平铺和工作区的按键:方向键聚焦邻居、SUPER + 1..0 切工作区、SUPER + J/P/L 调平铺布局、SUPER + HOME 移动工作区……到了画布上,这些键要么得重新赋予含义,要么得明确地「什么都不做」,而且必须在每一种窗口状态下都成立:窗口在一块屏上、横跨接缝、在屏幕外、被 fill 过、已经退到缩略视图。
项目给每种情况定的判据很硬:「没有别的窗口移动 / 窗口保持浮动且被绘制 / 两块屏处在同一个视图层级 / 退开再回来什么都不变」。例如 SUPER + G 建组的键,在画布上被显式定义为「什么都不做」,理由写在文档里 —— 在画布上堆叠的组,会让 Hyprland 在退出时崩溃。这种「把不支持的东西写清楚并让它安全地无效」的做法,比悄悄给个半吊子行为要靠谱得多。
全屏、填充、钉住:三个各自独立的语义
- 填充(
SUPER + T)把窗口撑满它所在的那块屏,算上 gaps 和边框;再按一次回到原来的位置和大小。中途移动过就以新位置为准,中途缩放过就重新撑满。 - 全屏(
SUPER + F)只让那一块屏的画布让开,其他屏幕该什么样还什么样。每块屏可以各自有一个全屏窗口。Wine / Proton 游戏那种「无边框窗口 + 覆盖整屏」会被识别成全屏,退回窗口模式时恢复到游戏自己要的尺寸;游戏在「战斗闪一下」时不会退出全屏;游戏藏起光标做鼠标视角时指针老老实实待在那块屏上。 - 钉住(
SUPER + O)让窗口钉在屏幕上不动,画布平移时它悬在画布之上;再按一次放回画布当前位置。
另外还有一档实验性的「地点」(canvas.places = true):工作区键被映射成画布上的一个个位置,SUPER + 1..0 飞过去,Shift + SUPER + N 把当前窗口搬过去,SUPER + TAB 去隔壁那个(哪怕它是空的)。去的时候 minimap 上会显示你正飞向哪里。默认是关的,因为这套语义还在打磨。
图标解析:不打包任何图片,直接问你的桌面
画布上要给每个窗口画图标,项目没有自带任何图标资源。它做的是按启动器的规矩去解析:拿窗口的 app id 去匹配 .desktop 文件,依次尝试 StartupWMClass、文件名、Name;拿到 Icon= 之后,在「当前图标主题 → 它 Inherits 的主题(递归,深度上限 6)→ hicolor → /usr/share/pixmaps」这条链上按尺寸子目录顺序找 SVG 或 PNG。
里面有几个只有真实使用才会踩到的补丁:
- 反向域名 id 的兜底:
org.gnome.Nautilus这种 app id,桌面文件常常只叫nautilus,所以要拿最后一个点后面的部分再查一次。 - Omarchy 的网页应用:它们是
chromium --app=URL跑起来的,窗口类名长成chrome-<host>__-Default。项目在扫 desktop 文件时顺手把Exec里的 URL host 记下来,再拿 host(必要时逐段砍掉后面的路径)去反查图标。 - 缓存按
path@size建键,超过 256 个就整体清空;更重要的是,它提供了一个resolve()—— 在动画开始之前先把文件系统扫描做掉,而不是在动画中间去扫盘。
调参:所见即所得,而且不会覆盖你的配置
在退开的视图里按 CTRL + ,,搜索框会原地变成设置列表。打字筛(blur、grid dot、accent),上下选,左右调,改一下就能立刻看到;Shift 微调、CTRL 粗调、Delete 撤销这一项。
可调的项接近 80 个:镜片强度 / 边缘模糊 / 暗角 / 色散 / 过渡时机、网格的间距线宽点径样式、minimap 的位置透明度、HUD 的字体和字号和圆角、视差壁纸的速度和深度、智能整理时的尺寸相似度阈值、窗口自动放置的碰撞间隙……
这里有一处工程上很正确的处理:调参结果写到单独的文件 ~/.config/hypr/spatialoverview-tuning.lua,它在 spatialoverview.lua 之后加载。所以升级不会覆盖你的设置,而调过的值会「赢」到你手动删掉那一行为止。同理,scripts/install.sh 也只往 hyprland.lua 里加两行带标记的语句,加之前先备份。
关于「改了名字但内部没改」这件小事
项目原来叫 Spatial Overview,改名成 Phantomat 之后,设置文件名、hl.plugin.spatialoverview.* 动作、hyprctl spatialoverview 命令全部保留了旧名,就是为了让改名之前的配置继续能用。这种取舍看起来不优雅,但比强行统一再让所有人重写配置要友好。
安装
需要 Hyprland 0.56 或更新(并且是它带 Lua 配置的那套)、GCC 15 或更新,以及 Hyprland 的开发文件。Arch / Omarchy 上:
sudo pacman -S --needed base-devel git hyprland hyprgraphics pango lua
git clone https://github.com/kaolti/phantomat.git
cd phantomat
scripts/install.sh
安装脚本会:编译插件 → 放到 ~/.local/share/spatial-overview/ → 备份并往 hyprland.lua 插两行 → 立刻加载。然后按 SUPER + CTRL + G 就能看到效果。不想用了跑 scripts/uninstall.sh,桌面恢复原状、设置文件保留(要连设置一起删就加 --purge)。
必须说清楚的代价
这是一款深度 hook 进 Hyprland 内部的插件,README 自己写得很坦白:Hyprland 一升级,插件就需要重新编译,否则会直接提示「这个 build 是给另一个 Hyprland 版本用的」,跑一遍安装脚本即可。它目前只在一套配置上被长期日常使用(Omarchy + Hyprland 0.56.2 + NVIDIA + 双屏),其他组合的测试覆盖要弱得多。
另外它是在 yayuuu/hyprland-scroll-overview 的基础上长出来的 fork,上游代码和历史都保留在仓库里 —— 包括那个 480KB 的 scrollOverview.cpp。两个项目之间没有隶属或背书关系。
为什么值得一看
它最值得看的地方不是「无限画布」这个点子 —— 这个点子本身不新 —— 而是把点子落地成了什么:用自己写的 GLSL 兑现镜片效果、把多屏的边界情况一条条写成规则再一条条挂上测试、给系统主题做 icon 回退、把调参结果隔离到单独文件、在不支持的行为上选择「安全地什么都不做」并说明理由。这些东西都不会出现在截图里,但决定了它能不能真的每天用。
顺带一提,9 月 26 日刚发布的 phantomat 同期还有一位邻居:同一个 Omarchy 生态里的 omarchy-meeting-recorder,把麦克风和系统声音录成两条独立的轨再转写。看得出来这个围绕 Omarchy 的小工具生态正在快速长出来。
封面图按项目自己的视觉语言生成:桶形镜片变形与暗角直接复刻自 shaders/barrel.frag 的公式。项目本身不打包任何图标资源,所以没有可用的官方 logo。
















cqlbgzs@163.com 1年前0
d好879445037@qq.com 2年前0
购买了 无法下载Alexcc 3年前0
强大Alexcc 3年前0
看不了教程Alexcc 3年前0
雷刺下载Alexcc 3年前0
下载Alexcc 3年前0
下载dsa456159 3年前0
下载