发布时间

从 River 切换到 Niri:无限画布式窗口管理


引言

在把 River 作为日常主力窗口管理器高效使用了几个月之后,我们还是碰到了瓶颈。虽然已经有双显示器,再加上 River 灵活的标签系统(每个显示器 9 个 tag),我们依然经常觉得工作空间不够用。多个开发项目、浏览器调研窗口、沟通应用以及监控工具同时开着时,即便理论上有 18 种 tag 组合,依旧不够分。

但真正让人崩溃的,其实是另一个问题:窗口尺寸变化会破坏应用布局。当你在一个精心排好的编码工作区里新开一个终端时,已有窗口会被重新缩放,导致 IDE 侧边栏折叠、浏览器开发者工具变得难用,或者监控面板直接切到移动端布局。这种反复打断布局的行为,会实实在在地拖慢工作效率。

于是我们发现了 Niri - 一个采用 scrollable-tiling 设计的 Wayland 合成器,它用“窗口永不缩放的无限横向画布”重新定义了窗口管理方式。

River 的问题

River 本身是一个很优秀的窗口管理器,但对我们的工作流来说,有两个限制最终变成了不能忽视的问题:

双显示器下的标签耗尽

River 提供 9 个标签,听起来已经不少了,但当你需要同时管理以下内容时,很快就会感觉捉襟见肘:

  • 多个开发项目(3-4 个标签给不同代码库)
  • 调研与文档(2-3 个标签给浏览器与参考资料)
  • 通信工具(1-2 个标签给邮件、聊天、消息)
  • 监控与系统工具(1-2 个标签)
  • 媒体和其他零散应用(1-2 个标签)

即使有两台显示器(理论上等于 18 种标签组合),我们依然经常陷入下面两种情况之一:

  • 某些标签塞进太多窗口,导致导航困难
  • 为了腾出 tag 空间,不得不关掉还在用的应用
  • 不断消耗脑力去决定新窗口到底该分配到哪个标签

窗口缩放会破坏布局

传统平铺式窗口管理器(包括 River)在打开新窗口时,会自动调整已有窗口尺寸,以维持平铺布局。这会带来很现实的问题:

开发工作流被打断:调试时新开一个终端,IDE 就会被挤压到文件树折叠,代码编辑区也变得太窄。原本精心安排的分屏布局会自行重排。

浏览器布局崩掉:现代 Web 应用大量依赖响应式设计。当浏览器窗口因为平铺调整而被缩小时,复杂 Web 应用(管理后台、数据面板、开发工具)往往会切换到平板甚至手机布局,把关键功能藏进汉堡菜单里,整个界面结构也会重新组织。

监控面板出问题:带有多块面板的系统监控工具在尺寸变化后,会重新排列甚至隐藏部分信息,逼得你不断滚动或展开之前本来就能直接看到的区域。

这些并不是“小烦恼” - 它们每天会发生几十次,频繁打断专注状态,并迫使你手动重新整理窗口。

Niri 登场:可滚动平铺

Niri 是一个 scrollable-tiling 的 Wayland 合成器,它用一个简单却强大的概念,同时解决了上面两个问题:窗口会被排列在一条无限延展的横向列带上

无限画布的概念

Niri 不会通过缩小窗口来把所有内容硬塞进固定屏幕空间,而是把窗口按列排列,并无限向右延展。你可以把它理解为一块很长的横向画布:

  • 每打开一个新窗口,就在右侧新增一列
  • 当你打开新应用时,已有窗口永远不会缩放
  • 你通过左右滚动来浏览整条窗口带
  • 每个显示器都有自己独立的无限横向带

这种方式同时解决了标签耗尽和布局被破坏的问题。你可以无限制地继续打开窗口,而不用担心:

  • 空间不够用(画布本身是无限的)
  • 现有布局被破坏(窗口不会缩放)
  • 还要做复杂 tag 管理决策(工作区是动态的)

动态工作区 vs 固定标签

Niri 使用的是动态垂直工作区,这一点和 GNOME 有些类似:

  • 工作区按垂直方向排列(使用 Super+Up/Down 切换)
  • 每个显示器拥有独立工作区
  • 底部永远会保留一个空工作区
  • 当你把窗口向下移动时,会自动创建新工作区
  • 空工作区会自动消失

这与 River 的 tags 有本质区别:

  • River 的 tags:固定的 9 个标签,需要你给窗口分配标签
  • Niri 的 workspaces:会根据需要动态增减的空间

这里最关键的思路变化是:在 Niri 里,你不需要主动“管理工作区”,你只需要直接使用它们。需要分离上下文时,把窗口移到下面的新工作区,剩下的交给系统处理。

关键特性

除了正面解决我们在 River 上遇到的痛点,Niri 还为现代工作流提供了不少很有吸引力的功能:

内置截图界面:不需要额外折腾外部截图工具,Niri 自带原生截图 UI。

概览模式:可以缩小查看所有工作区和窗口(有点像 GNOME 的 Activities 概览),让导航和窗口管理更直观。

手势支持:触控板和鼠标手势都开箱即用:

  • 水平滑动可以滚动浏览窗口
  • 垂直滑动可以切换工作区
  • 鼠标手势可用于快速导航

窗口标签页:可以把相关窗口放进同一列中的标签页里,适合组织多个终端或浏览器窗口,而不用额外开新列。

显示器与窗口录屏 / 投屏:通过 xdg-desktop-portal-gnome 提供内建支持,还可以在采集中屏蔽敏感窗口。

配置热重载:修改配置后无需重启合成器,重新加载即可立即看到变化。

渐变边框:支持使用 Oklab 和 Oklch 色彩空间来配置渐变边框,视觉效果更现代。

自定义动画:你可以配置窗口动画,甚至使用自定义 shader 来实现特定视觉效果。

浮动窗口:从 niri 25.01 开始,已经提供完整的浮动窗口支持,适合那些确实需要浮动行为的应用。

配置

Niri 使用 KDL 格式配置文件,路径是 ~/.config/niri/config.kdl。我们的完整配置已经放在 arch-config 仓库 中:

// Essential input settings
input {
    keyboard {
        repeat-delay 250
        repeat-rate 50
    }

    touchpad {
        tap
        natural-scroll
    }

    mouse {
        accel-speed -0.6
        accel-profile "flat"
    }
}

// Layout: gaps, column widths, and focus ring
layout {
    gaps 0
    center-focused-column "always"

    preset-column-widths {
        proportion 0.33333
        proportion 0.5
        proportion 0.66667
    }

    default-column-width { proportion 0.5; }

    // Disable focus ring and borders for cleaner look
    focus-ring { off }
    border { off }
}

// Multi-monitor setup
output "DP-1" {
    mode "3840x2160@60"
    scale 2.4
    position x=0 y=0
}

output "HDMI-A-1" {
    mode "2560x1440@60"
    scale 1.6
    transform "270"  // Portrait mode
    position x=1600 y=0
}

// Startup applications
spawn-sh-at-startup "pkill fcitx5 || true; sleep 0.5; fcitx5 -d"
spawn-sh-at-startup "pkill waybar || true; sleep 0.5; waybar &"

// Turn off animations for snappier feel
animations { off }

核心快捷键

binds {
    // Navigation: Vim-style + arrow keys
    Mod+H     { focus-column-left; }
    Mod+J     { focus-window-down; }
    Mod+K     { focus-window-up; }
    Mod+L     { focus-column-right; }

    // Move windows/columns
    Mod+Ctrl+H { move-column-left; }
    Mod+Ctrl+J { move-window-down; }
    Mod+Ctrl+K { move-window-up; }
    Mod+Ctrl+L { move-column-right; }

    // Workspace navigation (vertical)
    Mod+U { focus-workspace-down; }
    Mod+I { focus-workspace-up; }
    Mod+Ctrl+U { move-column-to-workspace-down; }
    Mod+Ctrl+I { move-column-to-workspace-up; }

    // Direct workspace access
    Mod+1 { focus-workspace 1; }
    Mod+2 { focus-workspace 2; }
    // ... up to Mod+9

    // Applications
    Mod+T     { spawn "alacritty"; }
    Mod+Space { spawn "fuzzel"; }

    // Window management
    Mod+Q       { close-window; }
    Mod+F       { maximize-column; }
    Mod+Shift+F { fullscreen-window; }
    Mod+V       { toggle-window-floating; }
    Mod+W       { toggle-column-tabbed-display; }

    // Column width adjustments
    Mod+R     { switch-preset-column-width; }
    Mod+Minus { set-column-width "-10%"; }
    Mod+Equal { set-column-width "+10%"; }

    // Overview and screenshots
    Mod+O { toggle-overview; }
    Mod+S { screenshot; }

    // System
    Mod+Shift+E { quit; }
}

入门建议

接受“横向思维”

从传统平铺式 WM 转到 Niri,最大的心智变化就是:你需要开始横向思考,而不是执着于离散工作区:

  • 不要再努力减少打开窗口的数量 - 画布本来就是无限的
  • 把横向滚动当作主要导航方式
  • 把工作区理解为不同工作的类别,而不是有限的容器

用工作区分离上下文

和 River 的 tags 不同,在 Niri 里你应该把工作区用来区分不同上下文:

  • 工作区 1:当前开发(横向滚动多个项目窗口)
  • 工作区 2:沟通(浏览器、聊天、邮件并排)
  • 工作区 3:监控(系统工具、日志、面板)
  • 工作区 4:媒体与调研

用窗口标签组织相关内容

使用标签页,把相关窗口放进同一列中:

  • 多个终端:使用 Mod+W 切换标签式显示
  • 按研究主题分组浏览器窗口
  • 把多个代码编辑器叠在一起管理

和 River 的关键区别

不再需要管理标签:你不需要再给窗口分配 tags,也不需要纠结某个窗口应该属于哪个工作区。直接把它移到你需要的位置即可。

无限空间:不用再担心空间耗尽。只要你的工作流需要,就可以继续开窗口。

稳定布局:新开应用时,你已经排好的窗口会保持原尺寸不变。

更少的前期配置:Niri 的默认设置很合理,而且动态工作区系统相比 River 的标签系统,通常需要更少前期配置工作。

结语

Niri 为窗口管理提供了一种很新的思路,而且它解决的是非常真实的生产力问题。对于那些已经撞上传统平铺式窗口管理器限制的用户来说 - 无论是 River 上的 tag 耗尽、自动缩放打乱布局,还是单纯想要更流动的工作方式 - Niri 的无限画布设计都提供了一个优雅的解决方案。

哪些人适合考虑 Niri:

  • 被工作区限制困扰的 River/i3/Sway 用户
  • 拥有复杂多窗口工作流的开发者
  • 习惯同时保持很多应用打开的用户
  • 曾经被窗口缩放打乱布局折磨过的人
  • 想体验设计用心的现代 Wayland 合成器的用户

Niri 的哲学:

Niri 不会逼你把窗口塞进预定义的盒子里。相反,它提供的是一块可以适应你工作流的无限画布。窗口保持自己的布局,工作区会按需生长和收缩,导航也从“在标签之间离散跳转”变成一种流动的滚动体验。

从 River 转向 Niri,确实需要先适应横向思维,但一旦你真正接受了无限画布这个概念,就很难再回到那个总担心 tag 用完、或者布局被缩放破坏的世界。

我们的完整 Niri 配置已经放在 dotfiles 仓库 中。欢迎加入 Matrix 上的 Niri 社区 获取支持与灵感,也可以查看官方文档获取更完整的安装与配置说明。

祝你滚动愉快!