unit-3-hyprland-nier-rice

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Unit-3 Hyprland NieR:Automata Rice

Unit-3 尼尔机械纪元主题Hyprland桌面配置(Rice)

Skill by ara.so — Daily 2026 Skills collection.
Unit-3 is a fully themed Arch Linux desktop rice combining Hyprland (Wayland compositor), Quickshell (QML-based widget system), and Waybar into a cohesive NieR:Automata aesthetic. It includes custom QML widgets for an app menu, lockscreen, wallpaper picker, notifications, and media player.

ara.so开发的技能——2026每日技能合集。
Unit-3是一套完整主题化的Arch Linux桌面配置,将Hyprland(Wayland合成器)、Quickshell(基于QML的组件系统)和Waybar整合为统一的尼尔机械纪元视觉风格。它包含自定义QML组件,应用菜单、锁屏、壁纸选择器、通知栏和媒体播放器一应俱全。

Installation

安装

Quick Install (Recommended)

快速安装(推荐)

bash
bash <(curl -fsSL https://raw.githubusercontent.com/samyns/Unit-3/main/install.sh)
bash
bash <(curl -fsSL https://raw.githubusercontent.com/samyns/Unit-3/main/install.sh)

Manual Clone

手动克隆

bash
git clone https://github.com/samyns/Unit-3.git
cd Unit-3
bash install.sh
The installer sets up:
  • Hyprland config in
    ~/.config/hypr/
  • Quickshell widgets in
    ~/.config/quickshell/
  • Waybar config in
    ~/.config/waybar/
  • Kitty terminal config

bash
git clone https://github.com/samyns/Unit-3.git
cd Unit-3
bash install.sh
安装程序会完成以下配置:
  • Hyprland配置文件存放于
    ~/.config/hypr/
  • Quickshell组件存放于
    ~/.config/quickshell/
  • Waybar配置文件存放于
    ~/.config/waybar/
  • Kitty终端配置

Directory Structure

目录结构

Unit-3/
├── install.sh
├── .config/
│   ├── hypr/
│   │   ├── hyprland.conf       # Main Hyprland config
│   │   └── user.conf           # User overrides (never overwritten)
│   ├── quickshell/
│   │   └── *.qml               # Quickshell QML widget files
│   └── waybar/
│       ├── config              # Waybar modules config
│       └── style.css           # Waybar NieR-themed styles

Unit-3/
├── install.sh
├── .config/
│   ├── hypr/
│   │   ├── hyprland.conf       # Hyprland主配置文件
│   │   └── user.conf           # 用户自定义配置(永不覆盖)
│   ├── quickshell/
│   │   └── *.qml               # Quickshell QML组件文件
│   └── waybar/
│       ├── config              # Waybar模块配置
│       └── style.css           # 尼尔主题Waybar样式

User Configuration (Never Overwritten)

用户自定义配置(永不覆盖)

All personal overrides go in
~/.config/hypr/user.conf
. This file is safe from updates.
conf
undefined
所有个人自定义配置需写入
~/.config/hypr/user.conf
,该文件不会被更新覆盖。
conf
undefined

~/.config/hypr/user.conf

~/.config/hypr/user.conf

Monitor setup

显示器设置

monitor = DP-1, 2560x1440@144, 0x0, 1 monitor = HDMI-A-1, 1920x1080@60, 2560x0, 1
monitor = DP-1, 2560x1440@144, 0x0, 1 monitor = HDMI-A-1, 1920x1080@60, 2560x0, 1

Input layout

输入布局

input { kb_layout = us kb_variant = follow_mouse = 1 sensitivity = 0 }
input { kb_layout = us kb_variant = follow_mouse = 1 sensitivity = 0 }

Custom keybinds

自定义快捷键

bind = SUPER, B, exec, firefox bind = SUPER, E, exec, nautilus bind = SUPER, M, exec, spotify
bind = SUPER, B, exec, firefox bind = SUPER, E, exec, nautilus bind = SUPER, M, exec, spotify

Environment variables

环境变量

env = XCURSOR_SIZE, 24 env = GTK_THEME, NieR

---
env = XCURSOR_SIZE, 24 env = GTK_THEME, NieR

---

Keybinds Reference

快捷键参考

KeyAction
SUPER
(tap)
Open app menu
SUPER + L
Lockscreen
SUPER + T
Terminal (kitty)
SUPER + Return
Toggle Quickshell player
SUPER + P
Wallpaper picker
SUPER + Q
Close window
SUPER + F
Fullscreen toggle
ALT + Tab
Cycle windows
ALT + 1/2/3...
Switch workspace
Print
Screenshot
ALT + SHIFT + S
Region screenshot

按键操作
SUPER
(轻触)
打开应用菜单
SUPER + L
锁屏
SUPER + T
打开终端(kitty)
SUPER + Return
切换Quickshell媒体播放器显示状态
SUPER + P
打开壁纸选择器
SUPER + Q
关闭窗口
SUPER + F
切换全屏状态
ALT + Tab
循环切换窗口
ALT + 1/2/3...
切换工作区
Print
截图
ALT + SHIFT + S
区域截图

QML Widget Development

QML组件开发

Quickshell widgets are written in QML. Unit-3 widgets follow a NieR aesthetic with dark backgrounds and angular UI elements.
Quickshell组件基于QML编写。Unit-3的组件遵循尼尔机械纪元风格,采用深色背景和棱角分明的UI元素。

Basic Quickshell Widget Structure

基础Quickshell组件结构

qml
// ~/.config/quickshell/MyWidget.qml
import Quickshell
import Quickshell.Io
import QtQuick
import QtQuick.Controls

ShellRoot {
    // Panels are screen-anchored overlays
    PanelWindow {
        id: myPanel
        anchors {
            top: true
            left: true
            right: true
        }
        height: 40
        color: "transparent"

        Rectangle {
            anchors.fill: parent
            color: "#1a1a1a"
            border.color: "#c8a951"  // NieR gold accent
            border.width: 1

            Text {
                anchors.centerIn: parent
                text: "Unit-3"
                color: "#e8d5a3"
                font.family: "monospace"
                font.pixelSize: 14
            }
        }
    }
}
qml
// ~/.config/quickshell/MyWidget.qml
import Quickshell
import Quickshell.Io
import QtQuick
import QtQuick.Controls

ShellRoot {
    // Panel是锚定屏幕的覆盖层
    PanelWindow {
        id: myPanel
        anchors {
            top: true
            left: true
            right: true
        }
        height: 40
        color: "transparent"

        Rectangle {
            anchors.fill: parent
            color: "#1a1a1a"
            border.color: "#c8a951"  // 尼尔金色强调色
            border.width: 1

            Text {
                anchors.centerIn: parent
                text: "Unit-3"
                color: "#e8d5a3"
                font.family: "monospace"
                font.pixelSize: 14
            }
        }
    }
}

Notification Widget Pattern

通知组件示例

qml
// Notification popup following NieR style
import Quickshell
import Quickshell.Services.Notifications
import QtQuick

ShellRoot {
    NotificationServer {
        id: notifServer
    }

    Variants {
        model: notifServer.trackedNotifications

        PanelWindow {
            required property var modelData
            property var notification: modelData

            anchors {
                top: true
                right: true
            }
            margins.top: 10
            margins.right: 10

            width: 320
            height: 80
            color: "transparent"

            Rectangle {
                anchors.fill: parent
                color: "#101418"
                border.color: "#c8a951"
                border.width: 1
                radius: 2

                Column {
                    anchors {
                        left: parent.left
                        verticalCenter: parent.verticalCenter
                        leftMargin: 16
                    }
                    spacing: 4

                    Text {
                        text: notification.summary
                        color: "#c8a951"
                        font.pixelSize: 13
                        font.bold: true
                    }
                    Text {
                        text: notification.body
                        color: "#9a9a8a"
                        font.pixelSize: 11
                    }
                }

                MouseArea {
                    anchors.fill: parent
                    onClicked: notification.dismiss()
                }
            }

            // Auto-dismiss timer
            Timer {
                interval: 5000
                running: true
                onTriggered: notification.dismiss()
            }
        }
    }
}
qml
// 尼尔风格通知弹窗
import Quickshell
import Quickshell.Services.Notifications
import QtQuick

ShellRoot {
    NotificationServer {
        id: notifServer
    }

    Variants {
        model: notifServer.trackedNotifications

        PanelWindow {
            required property var modelData
            property var notification: modelData

            anchors {
                top: true
                right: true
            }
            margins.top: 10
            margins.right: 10

            width: 320
            height: 80
            color: "transparent"

            Rectangle {
                anchors.fill: parent
                color: "#101418"
                border.color: "#c8a951"
                border.width: 1
                radius: 2

                Column {
                    anchors {
                        left: parent.left
                        verticalCenter: parent.verticalCenter
                        leftMargin: 16
                    }
                    spacing: 4

                    Text {
                        text: notification.summary
                        color: "#c8a951"
                        font.pixelSize: 13
                        font.bold: true
                    }
                    Text {
                        text: notification.body
                        color: "#9a9a8a"
                        font.pixelSize: 11
                    }
                }

                MouseArea {
                    anchors.fill: parent
                    onClicked: notification.dismiss()
                }
            }

            // 自动关闭计时器
            Timer {
                interval: 5000
                running: true
                onTriggered: notification.dismiss()
            }
        }
    }
}

Media Player Widget Pattern

媒体播放器组件示例

qml
// ~/.config/quickshell/Player.qml
import Quickshell
import Quickshell.Services.Mpris
import QtQuick
import QtQuick.Controls

ShellRoot {
    FloatingWindow {
        id: playerWindow
        visible: false  // toggled by SUPER + Return

        width: 340
        height: 100
        color: "transparent"

        Rectangle {
            anchors.fill: parent
            color: "#0d1117"
            border.color: "#c8a951"
            border.width: 1

            Row {
                anchors {
                    left: parent.left
                    verticalCenter: parent.verticalCenter
                    leftMargin: 12
                }
                spacing: 12

                // Album art
                Rectangle {
                    width: 64
                    height: 64
                    color: "#1a1a1a"
                    border.color: "#c8a951"
                    border.width: 1

                    Image {
                        anchors.fill: parent
                        source: MprisController.currentPlayer?.trackArtUrl ?? ""
                        fillMode: Image.PreserveAspectCrop
                    }
                }

                Column {
                    anchors.verticalCenter: parent.verticalCenter
                    spacing: 4

                    Text {
                        text: MprisController.currentPlayer?.trackTitle ?? "No media"
                        color: "#e8d5a3"
                        font.pixelSize: 13
                        font.bold: true
                        elide: Text.ElideRight
                        width: 220
                    }
                    Text {
                        text: MprisController.currentPlayer?.trackArtist ?? ""
                        color: "#9a9a8a"
                        font.pixelSize: 11
                    }

                    // Playback controls
                    Row {
                        spacing: 8
                        Repeater {
                            model: ["⏮", "⏯", "⏭"]
                            Text {
                                text: modelData
                                color: "#c8a951"
                                font.pixelSize: 16
                                MouseArea {
                                    anchors.fill: parent
                                    onClicked: {
                                        if (index === 0) MprisController.currentPlayer?.previous()
                                        else if (index === 1) MprisController.currentPlayer?.playPause()
                                        else MprisController.currentPlayer?.next()
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
qml
// ~/.config/quickshell/Player.qml
import Quickshell
import Quickshell.Services.Mpris
import QtQuick
import QtQuick.Controls

ShellRoot {
    FloatingWindow {
        id: playerWindow
        visible: false  // 通过SUPER + Return切换显示

        width: 340
        height: 100
        color: "transparent"

        Rectangle {
            anchors.fill: parent
            color: "#0d1117"
            border.color: "#c8a951"
            border.width: 1

            Row {
                anchors {
                    left: parent.left
                    verticalCenter: parent.verticalCenter
                    leftMargin: 12
                }
                spacing: 12

                // 专辑封面
                Rectangle {
                    width: 64
                    height: 64
                    color: "#1a1a1a"
                    border.color: "#c8a951"
                    border.width: 1

                    Image {
                        anchors.fill: parent
                        source: MprisController.currentPlayer?.trackArtUrl ?? ""
                        fillMode: Image.PreserveAspectCrop
                    }
                }

                Column {
                    anchors.verticalCenter: parent.verticalCenter
                    spacing: 4

                    Text {
                        text: MprisController.currentPlayer?.trackTitle ?? "无媒体播放"
                        color: "#e8d5a3"
                        font.pixelSize: 13
                        font.bold: true
                        elide: Text.ElideRight
                        width: 220
                    }
                    Text {
                        text: MprisController.currentPlayer?.trackArtist ?? ""
                        color: "#9a9a8a"
                        font.pixelSize: 11
                    }

                    // 播放控制
                    Row {
                        spacing: 8
                        Repeater {
                            model: ["⏮", "⏯", "⏭"]
                            Text {
                                text: modelData
                                color: "#c8a951"
                                font.pixelSize: 16
                                MouseArea {
                                    anchors.fill: parent
                                    onClicked: {
                                        if (index === 0) MprisController.currentPlayer?.previous()
                                        else if (index === 1) MprisController.currentPlayer?.playPause()
                                        else MprisController.currentPlayer?.next()
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

Wallpaper Picker Widget

壁纸选择器组件

qml
// ~/.config/quickshell/WallpaperPicker.qml
import Quickshell
import Quickshell.Io
import QtQuick
import QtQuick.Layouts

ShellRoot {
    property string wallpaperDir: "/home/" + Qt.application.name + "/Pictures/Wallpapers"

    FloatingWindow {
        id: pickerWindow
        width: 800
        height: 500

        Rectangle {
            anchors.fill: parent
            color: "#0d1117"
            border.color: "#c8a951"
            border.width: 1

            GridView {
                anchors {
                    fill: parent
                    margins: 16
                }
                cellWidth: 180
                cellHeight: 120

                model: FileView {
                    path: wallpaperDir
                    nameFilters: ["*.jpg", "*.png", "*.webp"]
                }

                delegate: Rectangle {
                    width: 172
                    height: 112
                    color: "#1a1a1a"
                    border.color: mouseArea.containsMouse ? "#c8a951" : "transparent"
                    border.width: 2

                    Image {
                        anchors.fill: parent
                        anchors.margins: 2
                        source: "file://" + model.filePath
                        fillMode: Image.PreserveAspectCrop
                    }

                    MouseArea {
                        id: mouseArea
                        anchors.fill: parent
                        hoverEnabled: true
                        onClicked: {
                            // Set wallpaper via swww
                            Process.exec(["swww", "img", model.filePath,
                                          "--transition-type", "wipe",
                                          "--transition-angle", "30"])
                            pickerWindow.visible = false
                        }
                    }
                }
            }
        }
    }
}

qml
// ~/.config/quickshell/WallpaperPicker.qml
import Quickshell
import Quickshell.Io
import QtQuick
import QtQuick.Layouts

ShellRoot {
    property string wallpaperDir: "/home/" + Qt.application.name + "/Pictures/Wallpapers"

    FloatingWindow {
        id: pickerWindow
        width: 800
        height: 500

        Rectangle {
            anchors.fill: parent
            color: "#0d1117"
            border.color: "#c8a951"
            border.width: 1

            GridView {
                anchors {
                    fill: parent
                    margins: 16
                }
                cellWidth: 180
                cellHeight: 120

                model: FileView {
                    path: wallpaperDir
                    nameFilters: ["*.jpg", "*.png", "*.webp"]
                }

                delegate: Rectangle {
                    width: 172
                    height: 112
                    color: "#1a1a1a"
                    border.color: mouseArea.containsMouse ? "#c8a951" : "transparent"
                    border.width: 2

                    Image {
                        anchors.fill: parent
                        anchors.margins: 2
                        source: "file://" + model.filePath
                        fillMode: Image.PreserveAspectCrop
                    }

                    MouseArea {
                        id: mouseArea
                        anchors.fill: parent
                        hoverEnabled: true
                        onClicked: {
                            // 通过swww设置壁纸
                            Process.exec(["swww", "img", model.filePath,
                                          "--transition-type", "wipe",
                                          "--transition-angle", "30"])
                            pickerWindow.visible = false
                        }
                    }
                }
            }
        }
    }
}

Waybar Configuration Pattern

Waybar配置示例

jsonc
// ~/.config/waybar/config (NieR modules example)
{
    "layer": "top",
    "position": "top",
    "height": 32,
    "modules-left": ["hyprland/workspaces", "hyprland/window"],
    "modules-center": ["clock"],
    "modules-right": ["pulseaudio", "network", "battery", "tray"],

    "hyprland/workspaces": {
        "format": "{id}",
        "on-click": "activate"
    },

    "clock": {
        "format": "{:%H:%M}",
        "format-alt": "{:%Y-%m-%d %H:%M:%S}",
        "tooltip-format": "<big>{:%Y %B}</big>\n<tt>{calendar}</tt>"
    },

    "network": {
        "format-wifi": "  {essid}",
        "format-ethernet": "  {ipaddr}",
        "format-disconnected": "  disconnected",
        "tooltip-format": "{ifname}: {ipaddr}"
    },

    "pulseaudio": {
        "format": " {volume}%",
        "format-muted": " muted",
        "on-click": "pavucontrol"
    }
}
css
/* ~/.config/waybar/style.css — NieR color palette */
* {
    font-family: "monospace";
    font-size: 13px;
}

window#waybar {
    background: rgba(13, 17, 23, 0.92);
    border-bottom: 1px solid #c8a951;
    color: #e8d5a3;
}

#workspaces button {
    color: #9a9a8a;
    border-radius: 0;
    padding: 0 8px;
    border-bottom: 2px solid transparent;
}

#workspaces button.active {
    color: #c8a951;
    border-bottom: 2px solid #c8a951;
}

#clock {
    color: #c8a951;
    font-weight: bold;
    letter-spacing: 2px;
}

#network, #pulseaudio, #battery {
    color: #e8d5a3;
    padding: 0 12px;
}

jsonc
// ~/.config/waybar/config(尼尔主题模块示例)
{
    "layer": "top",
    "position": "top",
    "height": 32,
    "modules-left": ["hyprland/workspaces", "hyprland/window"],
    "modules-center": ["clock"],
    "modules-right": ["pulseaudio", "network", "battery", "tray"],

    "hyprland/workspaces": {
        "format": "{id}",
        "on-click": "activate"
    },

    "clock": {
        "format": "{:%H:%M}",
        "format-alt": "{:%Y-%m-%d %H:%M:%S}",
        "tooltip-format": "<big>{:%Y %B}</big>\n<tt>{calendar}</tt>"
    },

    "network": {
        "format-wifi": "  {essid}",
        "format-ethernet": "  {ipaddr}",
        "format-disconnected": "  已断开",
        "tooltip-format": "{ifname}: {ipaddr}"
    },

    "pulseaudio": {
        "format": " {volume}%",
        "format-muted": "  已静音",
        "on-click": "pavucontrol"
    }
}
css
/* ~/.config/waybar/style.css — 尼尔配色方案 */
* {
    font-family: "monospace";
    font-size: 13px;
}

window#waybar {
    background: rgba(13, 17, 23, 0.92);
    border-bottom: 1px solid #c8a951;
    color: #e8d5a3;
}

#workspaces button {
    color: #9a9a8a;
    border-radius: 0;
    padding: 0 8px;
    border-bottom: 2px solid transparent;
}

#workspaces button.active {
    color: #c8a951;
    border-bottom: 2px solid #c8a951;
}

#clock {
    color: #c8a951;
    font-weight: bold;
    letter-spacing: 2px;
}

#network, #pulseaudio, #battery {
    color: #e8d5a3;
    padding: 0 12px;
}

Hyprland Config Patterns

Hyprland配置示例

conf
undefined
conf
undefined

~/.config/hypr/hyprland.conf (snippet)

~/.config/hypr/hyprland.conf(代码片段)

NieR-style animations

尼尔风格动画

animations { enabled = true bezier = nier, 0.05, 0.9, 0.1, 1.0 animation = windows, 1, 4, nier, slide animation = fade, 1, 4, nier animation = workspaces, 1, 5, nier, slidevert }
animations { enabled = true bezier = nier, 0.05, 0.9, 0.1, 1.0 animation = windows, 1, 4, nier, slide animation = fade, 1, 4, nier animation = workspaces, 1, 5, nier, slidevert }

Minimal decorations for NieR aesthetic

尼尔风格极简窗口装饰

decoration { rounding = 0 blur { enabled = true size = 4 passes = 2 noise = 0.02 contrast = 1.0 brightness = 0.9 } drop_shadow = true shadow_color = rgba(200, 169, 81, 0.4) shadow_range = 8 }
decoration { rounding = 0 blur { enabled = true size = 4 passes = 2 noise = 0.02 contrast = 1.0 brightness = 0.9 } drop_shadow = true shadow_color = rgba(200, 169, 81, 0.4) shadow_range = 8 }

Window gaps

窗口间距

general { gaps_in = 4 gaps_out = 8 border_size = 1 col.active_border = rgba(c8a951ff) col.inactive_border = rgba(2a2a2aff) }

---
general { gaps_in = 4 gaps_out = 8 border_size = 1 col.active_border = rgba(c8a951ff) col.inactive_border = rgba(2a2a2aff) }

---

Troubleshooting

故障排查

Quickshell widgets not appearing

Quickshell组件不显示

bash
undefined
bash
undefined

Check if quickshell is running

检查Quickshell是否在运行

pgrep -a quickshell
pgrep -a quickshell

Restart quickshell

重启Quickshell

pkill quickshell && quickshell &
pkill quickshell && quickshell &

Check logs for QML errors

检查QML错误日志

quickshell 2>&1 | grep -i error
undefined
quickshell 2>&1 | grep -i error
undefined

Waybar not loading

Waybar无法加载

bash
undefined
bash
undefined

Validate config syntax

验证配置语法

waybar --log-level debug
waybar --log-level debug

Reload waybar

重启Waybar

pkill waybar && waybar &
pkill waybar && waybar &

Check config location

检查配置文件位置

ls ~/.config/waybar/
undefined
ls ~/.config/waybar/
undefined

Hyprland keybinds not working

Hyprland快捷键不生效

bash
undefined
bash
undefined

Check active config

检查当前生效配置

hyprctl activewindow hyprctl getoption general:border_size
hyprctl activewindow hyprctl getoption general:border_size

Reload config live

实时重载配置

hyprctl reload
hyprctl reload

Verify user.conf is sourced

确认user.conf已被加载

grep "user.conf" ~/.config/hypr/hyprland.conf
undefined
grep "user.conf" ~/.config/hypr/hyprland.conf
undefined

Wallpaper not setting (swww)

壁纸无法设置(swww)

bash
undefined
bash
undefined

Initialize swww daemon first

先初始化sww守护进程

swww-daemon &
swww-daemon &

Then set wallpaper

再设置壁纸

swww img ~/Pictures/wall.jpg --transition-type wipe
undefined
swww img ~/Pictures/wall.jpg --transition-type wipe
undefined

Wrong monitor layout

显示器布局错误

conf
undefined
conf
undefined

~/.config/hypr/user.conf

~/.config/hypr/user.conf

List available monitors first:

先列出可用显示器:

hyprctl monitors

hyprctl monitors

monitor = DP-1, 2560x1440@144, 0x0, 1 monitor = HDMI-A-1, 1920x1080@60, 2560x0, 1

---
monitor = DP-1, 2560x1440@144, 0x0, 1 monitor = HDMI-A-1, 1920x1080@60, 2560x0, 1

---

NieR Color Palette Reference

尼尔机械纪元配色参考

NameHexUsage
Gold accent
#c8a951
Borders, active elements
Light text
#e8d5a3
Primary text
Dim text
#9a9a8a
Secondary text
Background
#0d1117
Main background
Surface
#101418
Widget backgrounds
Dark surface
#1a1a1a
Inset elements

名称十六进制码用途
金色强调色
#c8a951
边框、活跃元素
浅色文本
#e8d5a3
主要文本
暗色文本
#9a9a8a
次要文本
主背景色
#0d1117
整体背景
组件背景色
#101418
组件背景
深色组件色
#1a1a1a
内嵌元素

Updating the Rice

更新桌面配置

bash
cd ~/Unit-3   # or wherever you cloned it
git pull
bash install.sh   # re-runs installer; user.conf is preserved
bash
cd ~/Unit-3   # 或你克隆到的目录
git pull
bash install.sh   # 重新运行安装程序;user.conf会被保留