Hyprview is a Hyprland plugin that adds a niri-like scrolling workspace overview. It shows live workspace cards for the current monitor, lets you select a workspace or window, drag windows between workspaces, and pan wide scrolling-layout workspaces inside the overview.
| feature | behavior |
|---|---|
| Niri-like overview | Shows workspaces as a vertical scrolling overview instead of a grid. |
| Per-monitor overview | Opens for the focused monitor and only includes workspaces from that monitor. |
| Live workspace cards | Projects real Hyprland windows and layer-shell surfaces into workspace-card geometry. |
| Live geometry records | Tracks per-window overview boxes, renderability, and visibility for rendering, hit testing, and keyboard selection. |
| Window selection | Clicking a window closes the overview, switches to its workspace if needed, focuses the window, and warps the cursor to it. |
| Workspace selection | Clicking a workspace closes the overview and switches to that workspace. |
| Window dragging | Left-drag a non-pinned window inside the overview. Floating windows land at the dropped card position; tiled windows can target the scrolling tape and commit through the target workspace layout. |
| Workspace insertion markers | Drop a dragged window on a numbered insertion marker to create or select that workspace ID and move the window there. |
| Horizontal content pan | Right-drag a workspace using Hyprland's scrolling tiled layout to pan tiled window entries horizontally. |
| Wheel navigation | Wheel input moves between overview workspaces by default. It can be configured to zoom instead. |
| Keyboard selection | Optional Lua-controlled selection dispatchers move the selected window entry left/right/up/down and activate it. |
| Normal Hyprland keybinds | Workspace switching, focus movement, and moving windows between workspaces keep working while the overview is open. Hyprview refreshes and recenters from the resulting Hyprland events. |
| Active/selected distinction | Hyprview selection uses the main overview indicator. The real Hyprland active window gets a configurable secondary marker so focus changes do not hide what the overview will activate. |
| Trackpad gesture | Lua config can register a trackpad gesture that opens the overview when closed and drives overview scale while swiping. |
| Workspace badges | Optional workspace annotations can show ID, name, ID/name combinations, or window count. |
| Hover/focus styling | Hovered windows can use a translucent overview box or Hyprland's active border gradient. |
| Drag feedback | Valid and invalid drag targets use separate overlay colors and preview opacity. |
| Backdrop modes | Renders live background layers and can blur them through Hyprland's monitor preblur resource when background_blur = true. |
| Lua API | Provides Lua functions for configure, overview control, close, moving the hovered window, and gesture registration. |
| Dispatcher | Registers hyprview:overview internally. Lua users normally call hl.plugin.hyprview.overview(...), which wraps the same action path. |
Hyprland plugins are ABI-sensitive. Hyprview checks the running Hyprland API hash at plugin load time and refuses to initialize if the plugin was built against different Hyprland headers.
Hyprview also requires Hyprland's Lua config. If Hyprland is not using Lua config, the plugin refuses to initialize with a notification.
Pinned Hyprland/plugin commit pairs are tracked in hyprpm.toml.
On plugin unload, Hyprview removes overview pass elements, marks gesture registration as unloading, and reloads Hyprland config so registered gestures are cleared.
Requirements are provided through pkg-config:
| dependency |
|---|
hyprland |
pixman-1 |
libdrm |
pangocairo |
libinput |
libudev |
wayland-server |
xkbcommon |
Build from the repository root:
make allThe build writes hyprview.so in the repository root.
Useful make targets:
| target | behavior |
|---|---|
make all |
Build hyprview.so. |
make format-fix |
Run clang-format -i over src/**/*.cpp and src/**/*.hpp. |
make clean |
Remove hyprview.so. |
CMake and Meson build files are also present, but make all is the primary local build path for this repo.
Hyprview is configured and controlled from Hyprland's Lua config through hl.plugin.hyprview.* functions.
Example:
hl.plugin.hyprview.configure {
gesture_distance = 200,
on_close = function()
hl.dispatch("submap", "reset")
end,
keyboard = {
enabled = true,
grab = false,
remember_selection = true,
wrap = true,
activation_closes_overview = true,
focus_follows_selection = false,
},
mouse = {
select_follows_hover = true,
edge_navigation = true,
edge_navigation_snap = true,
edge_navigation_speed = 1.0,
hitbox_expansion = 16,
nearest_hitbox = true,
snap_pan = true,
snap_pan_zone = 96,
},
scrolling = {
scroll_moves_up_down = true,
default_zoom = 0.5,
window_gap = 0,
workspace_gap = 0,
background_blur = false,
show_workspace_layers = true,
backdrop_col = "rgba(000000ff)",
workspace_shadow_col = "rgba(00000000)",
workspace_shadow_size = 0,
focus_indicator = "overview_box",
active_border_size = 4,
hover_col = "rgba(a7c7ff33)",
drop_target_col = "rgba(a7c7ff22)",
drag_alpha = 1.0,
invalid_drag_alpha = 0.55,
edge_scroll_zone = 64,
edge_scroll_speed = 1.0,
hover_activate_ms = 600,
workspace_annotation = "id",
workspace_annotation_position = "top_left",
workspace_annotation_color = "rgba(ffffffff)",
workspace_annotation_bg_col = "rgba(00000099)",
workspace_annotation_font_size = 14,
active_indicator = "corner",
active_indicator_col = "rgba(ffffffff)",
insertion_marker_labels = true,
insertion_max_markers = 8,
append_marker_count = 1,
insertion_marker_col = "rgba(a7c7ff44)",
invalid_insertion_marker_col = "rgba(ff5c5c55)",
},
}
hl.plugin.hyprview.gesture {
fingers = 3,
direction = "down",
gesture = "overview",
}
hl.bind("SUPER+G", function()
hl.plugin.hyprview.overview("toggle")
end)Keyboard selection can be bound through overview-local key overrides. These
binds only run while Hyprview is open and keyboard.grab = true. Matching
overview binds are cancelled before normal Hyprland keybind handling; unmatched
keys continue through Hyprland normally.
hl.plugin.hyprview.configure {
keyboard = {
enabled = true,
grab = true,
},
}
hl.plugin.hyprview.bind("SUPER", "LEFT", hl.plugin.hyprview.selection_left)
hl.plugin.hyprview.bind("SUPER", "RIGHT", hl.plugin.hyprview.selection_right)
hl.plugin.hyprview.bind("SUPER", "UP", hl.plugin.hyprview.selection_up)
hl.plugin.hyprview.bind("SUPER", "DOWN", hl.plugin.hyprview.selection_down)
hl.plugin.hyprview.bind("", "RETURN", hl.plugin.hyprview.selection_activate)
hl.plugin.hyprview.bind("", "ESCAPE", function()
hl.plugin.hyprview.close(false)
end)Top-level options:
| property | type | default | description |
|---|---|---|---|
gesture_distance |
integer | 200 |
Gesture travel distance used to interpolate the overview scale animation. Values below 1 are treated as 1. |
on_close |
function, false, or nil |
nil |
Optional callback called once whenever the overview begins closing. Use it to reset Lua-owned submaps. false clears an existing callback. |
Keyboard options:
| property | type | default | description |
|---|---|---|---|
enabled |
boolean | true |
Enables the overview-native selection dispatchers. If disabled, selection_left, selection_right, selection_up, selection_down, and selection_activate are no-ops. |
grab |
boolean | false |
Enables overview-local key overrides registered through hl.plugin.hyprview.bind. Only matching overview binds are cancelled while the overview is open; unmatched keys keep normal Hyprland behavior. |
remember_selection |
boolean | true |
Remembers the selected window per workspace and restores it when the viewport returns to that workspace. |
wrap |
boolean | true |
Allows left/right selection to wrap within a workspace and up/down selection to wrap between the first and last overview workspace. |
activation_closes_overview |
boolean | true |
selection_activate closes the overview after focusing the selected window. If disabled, it focuses the selected window and keeps the overview open. |
focus_follows_selection |
boolean | false |
Overview keyboard selection also focuses the selected Hyprland window. If disabled, selection is only an overview activation target until selection_activate. |
Mouse options:
| property | type | default | description |
|---|---|---|---|
select_follows_hover |
boolean | true |
Pointer hover updates the selected window entry. Disable this if keyboard selection should stay fixed until keyboard navigation changes it. |
edge_navigation |
boolean | true |
Pointer motion near the top/bottom overview edge moves the workspace viewport. Disable this to rely on wheel input or right-click drag panning instead. |
edge_navigation_snap |
boolean | true |
Move one workspace when the pointer enters the top/bottom edge zone, then wait until the pointer leaves and re-enters before moving again. Disable to restore continuous edge motion. |
edge_navigation_speed |
float | 1.0 |
Pointer edge-navigation speed multiplier used when edge_navigation_snap = false. Higher values move through overview workspaces faster. |
hitbox_expansion |
integer | 16 |
Extra logical pixels around each live window entry for pointer hit testing. Set to 0 to restore exact entry-only hits. |
nearest_hitbox |
boolean | true |
If expanded hitboxes overlap, choose the nearest real window entry. If disabled, the first expanded hit in reverse render order wins. |
snap_pan |
boolean | true |
Enables pointer-driven horizontal snap panning for scrolling-layout workspace cards. Right-click drag panning still works when this is disabled. |
snap_pan_zone |
integer | 96 |
Left/right screen edge size, in logical pixels, used for horizontal snap panning. |
Scrolling options:
| property | type | default | description |
|---|---|---|---|
scroll_moves_up_down |
boolean | true |
If enabled, vertical wheel/scroll input moves between overview workspaces. If disabled, wheel/scroll input changes zoom. |
default_zoom |
float | 0.5 |
Default overview zoom, clamped to 0.1..0.9 when opening or completing a gesture. |
window_gap |
integer | 0 |
Visual gap between live window entries. Values below 0 render as 0. |
workspace_gap |
integer | 0 |
Visual gap between workspace cards in the overview stack. Values below 0 render as 0. |
background_blur |
boolean | false |
Blur the live backdrop after layer-shell background surfaces are rendered. |
show_workspace_layers |
boolean | true |
Render background and bottom layer-shell surfaces inside each workspace card. |
backdrop_col |
color | rgba(000000ff) |
Base color drawn before live backdrop layers and behind overview cards. |
workspace_shadow_col |
color | rgba(00000000) |
Optional shadow color behind workspace cards. Alpha 0 disables visible shadows. |
workspace_shadow_size |
integer | 0 |
Shadow expansion around workspace cards. Values below 0 render as 0. |
focus_indicator |
string | overview_box |
overview_box draws the hover color over the selected live window entry. active_border draws Hyprland's active border gradient. Other values fall back to overview_box. |
active_border_size |
integer | 4 |
Border thickness used by active_border, clamped to 1..64. |
hover_col |
color | rgba(a7c7ff33) |
Hover overlay color for overview_box and valid drag previews. |
drop_target_col |
color | rgba(a7c7ff22) |
Workspace-body drop highlight color while dragging a window. |
drag_alpha |
float | 1.0 |
Dragged-window preview opacity for valid drops, clamped to 0..1. |
invalid_drag_alpha |
float | 0.55 |
Dragged-window preview opacity for invalid drops, clamped to 0..1. |
edge_scroll_zone |
integer | 64 |
Top/bottom edge size in pixels used for autoscroll while dragging a window. Values below 0 render as 0. |
edge_scroll_speed |
float | 1.0 |
Edge autoscroll speed multiplier. 1.0 is roughly one workspace per second at full edge pressure. Values below 0 render as 0. |
hover_activate_ms |
integer | 600 |
Delay before a hovered workspace body is centered while dragging a window. Values below 0 render as 0. |
workspace_annotation |
string | id |
Workspace badge text mode: none, id, name, id_name, name_id, or id_windows. Unknown values fall back to id. |
workspace_annotation_position |
string | top_left |
Badge position: top_left, top_right, bottom_left, or bottom_right. Unknown values behave like top_left. |
workspace_annotation_color |
color | rgba(ffffffff) |
Workspace badge and insertion label text color. |
workspace_annotation_bg_col |
color | rgba(00000099) |
Workspace badge background color. |
workspace_annotation_font_size |
integer | 14 |
Workspace badge and insertion label font size. Values below 1 render as 1. |
active_indicator |
string | corner |
Active Hyprland window marker mode: none, dot, corner, underline, or border. Unknown values behave like corner. |
active_indicator_col |
color | rgba(ffffffff) |
Color used for the active Hyprland window marker. Alpha 0 hides the marker. |
insertion_marker_labels |
boolean | true |
Show target workspace IDs inside insertion markers. |
insertion_max_markers |
integer | 8 |
Maximum insertion markers shown for one numeric workspace gap. Values below 0 render as 0. |
append_marker_count |
integer | 1 |
Number of append workspace markers after the last numeric workspace, clamped to 0..5. |
insertion_marker_col |
color | rgba(a7c7ff44) |
Insertion marker color. |
invalid_insertion_marker_col |
color | rgba(ff5c5c55) |
Invalid drag/drop feedback color. |
Color options accept Hyprland-style color strings or integer values.
Configuration is applied atomically: Hyprview parses into a copy of the current config and only replaces the active config after all fields validate. Integer fields require Lua integers. Float fields accept Lua numbers. Boolean fields accept booleans and integer values, where non-zero integers are treated as true. String fields require strings. Invalid fields report a Lua config error and leave the previous active config in place.
Calling hl.plugin.hyprview.configure while the overview is open updates the config and damages the overview monitor so the new settings can render. Config resets to defaults before Hyprland config reloads, so Lua config should call hl.plugin.hyprview.configure on each reload.
Hyprview builds the overview from non-special workspaces on the focused monitor. Workspaces are sorted by workspace ID. A workspace appears when it has at least one displayable window. The active workspace also appears even when it is empty.
Empty workspaces, including persistent empty workspaces, are hidden from the overview but can still appear as insertion targets by workspace ID.
Pinned windows are shown only on the active workspace for the current monitor and cannot be dragged across workspaces.
Floating windows are rendered after tiled windows and are raised when selected or dragged. Horizontal right-drag panning affects tiled windows only; floating windows keep their normal position.
When windows open, close, move between workspaces, or change active focus while the overview is open, Hyprview queues a refresh and recenters the overview. Keybind-driven focus changes also briefly warp the cursor to the newly focused live window entry.
Insertion markers use real Hyprland workspace IDs. If workspaces 1 and 3 exist, dragging to the marker between them targets workspace 2. If workspaces 1 and 6 exist, markers 2, 3, 4, and 5 are shown, capped by insertion_max_markers.
No marker is shown before the first numeric workspace. Append markers are shown after the last numeric workspace according to append_marker_count. Append markers can target new workspace IDs after the last numeric workspace.
Insertion targets respect monitor-bound workspace rules. Existing target workspaces must belong to the overview monitor and have no displayable windows. New insertion workspaces are created on the overview monitor.
| input | behavior |
|---|---|
| Left click | Select the hovered window or workspace and close the overview. |
| Left drag a window | Drag that window inside the overview. Pinned windows cannot be dragged across workspaces. |
| Release dragged window over a workspace | Floating windows move to the dropped card position. Tiled windows move or reorder through the target workspace layout. The target workspace is activated without stealing focus from the dragged window, the dragged window is focused, and the overview refreshes. |
| Release dragged window over a numbered insertion marker | Create or select that workspace ID, move the window there, focus it, and refresh the overview. |
| Right drag | Pan tiled entries inside the hovered scrolling-layout workspace horizontally. |
Mouse wheel with scroll_moves_up_down = true |
Move the overview up/down by workspace. Wheel steps move one workspace; smooth vertical scrolling accumulates until the threshold is reached. Horizontal scroll axes are ignored. |
Mouse wheel with scroll_moves_up_down = false |
Zoom the overview in/out. Zoom is clamped to 0.05..0.95 while wheel-zooming. |
| Touch press | Select the hovered workspace/window and close the overview. |
hl.plugin.hyprview.selection_left |
Move the selected live window entry left within the current workspace. |
hl.plugin.hyprview.selection_right |
Move the selected live window entry right within the current workspace. |
hl.plugin.hyprview.selection_up |
Move to the previous overview workspace and restore its remembered selection. |
hl.plugin.hyprview.selection_down |
Move to the next overview workspace and restore its remembered selection. |
hl.plugin.hyprview.selection_activate |
Focus the selected window. By default this also closes the overview. |
| Normal Hyprland keybinds | Continue to focus or move windows. The overview queues refreshes and recenters from Hyprland workspace/window events. |
While dragging a window, hovering a workspace body for hover_activate_ms centers that workspace. Moving the pointer into the top or bottom edge_scroll_zone autoscrolls through workspaces at edge_scroll_speed.
Hyprland focus changes always update Hyprview selection while the overview is open, so normal focus keybinds and overview selection agree on the current target. focus_follows_selection controls the opposite direction: when enabled, overview keyboard selection also moves real Hyprland focus immediately. The active Hyprland window is shown with active_indicator; if active and selected are the same window, the selected indicator remains primary and the active marker is drawn on top.
Outside a drag, pointer workspace navigation is snap-based by default: entering the top or bottom edge zone moves one workspace and waits for the pointer to leave the zone before moving again. Scrolling-layout workspace cards also support horizontal snap panning from the left or right screen edge. A horizontal snap centers the next tiled live window entry in that direction and waits until the pointer leaves the edge before snapping again. Right-click drag remains the manual continuous pan path.
hl.plugin.hyprview.gesture registers or removes a trackpad gesture through Hyprland's trackpad gesture manager.
| field | behavior |
|---|---|
fingers |
Required integer from 2 through 9. |
direction |
Required direction string parsed by Hyprland. Invalid directions are rejected. |
gesture |
overview registers the gesture. unset removes the matching gesture. Defaults to overview. |
mod |
Optional modifier string converted through Hyprland's keybind manager. |
scale |
Optional gesture scale, clamped to 0.1..10.0. |
disable_inhibit |
Optional boolean passed to Hyprland's gesture registration. |
Starting the gesture opens the overview if it is closed. Starting the gesture while the overview is already open selects the hovered item and uses close-style scaling while the gesture is active. Gesture updates interpolate the overview scale using gesture_distance, and very small gesture deltas are clamped above zero to avoid invalid swipe-end state.
| name | arguments | behavior |
|---|---|---|
hl.plugin.hyprview.configure |
table | Applies Hyprview configuration. The config resets to defaults before Hyprland config reloads. |
hl.plugin.hyprview.overview |
action string or { action = "toggle" } |
Controls the overview. |
hl.plugin.hyprview.close |
optional boolean or { select = true } |
Closes the overview. Pass false or { select = false } to close without switching to the hovered selection. |
hl.plugin.hyprview.move_hovered_window |
none | Moves the hovered overview window to the active workspace, focuses it, and warps the cursor to it. |
hl.plugin.hyprview.bind |
mods, key, function or false |
Registers an overview-local key override. The override only applies while Hyprview is open and keyboard.grab = true. Passing false removes that override. |
hl.plugin.hyprview.selection_left |
none | Moves overview keyboard selection left within the current workspace. |
hl.plugin.hyprview.selection_right |
none | Moves overview keyboard selection right within the current workspace. |
hl.plugin.hyprview.selection_up |
none | Moves overview keyboard selection to the previous workspace. |
hl.plugin.hyprview.selection_down |
none | Moves overview keyboard selection to the next workspace. |
hl.plugin.hyprview.selection_activate |
none | Activates the selected overview window. |
hl.plugin.hyprview.gesture |
{ fingers, direction, gesture, mod?, scale?, disable_inhibit? } |
Registers or removes a trackpad gesture. |
Keyboard dispatchers are plain Lua API calls. hl.plugin.hyprview.bind is the
recommended way to reuse keys like SUPER+LEFT for overview selection without
shadowing your normal Hyprland keybinds when the overview is closed.
Hyprview still does not own Hyprland submap state. If you prefer a fully modal overview layer, define a Hyprland submap in Lua and bind the selection functions inside that submap.
on_close is the intended bridge for modal key layers. It lets Lua reset a
submap even when the overview closes through mouse selection, touch, gesture, or
another plugin path.
Overview actions:
| action | behavior |
|---|---|
toggle |
Open the overview if closed, close it if open. |
select |
Select the hovered workspace/window and close the overview. |
bring |
Bring the topmost mapped, non-hidden window from the selected workspace to the active workspace, then close without switching workspace. |
off |
Close the overview. |
close |
Close the overview. |
disable |
Close the overview. |
on |
Open the overview if closed. |
enable |
Open the overview if closed. |
| unknown action | If the overview is closed, unknown actions open it. If the overview is already open, unknown actions are a no-op. |
Hyprview hooks Hyprland workspace rendering and monitor damage reporting while the overview is open. Normal workspace rendering is replaced by the overview render pass for the overview monitor only.
Live overview rendering projects real Hyprland windows and layer-shell surfaces into overview geometry. X11 windows are considered renderable when mapped. Wayland windows require a current surface texture.
The live backdrop clears to backdrop_col, renders BACKGROUND layer-shell surfaces through Hyprland's normal renderLayer path, then optionally uses Hyprland's preBlurForCurrentMonitor and monitor m_blurFB resource when background_blur = true.
When show_workspace_layers = true, BACKGROUND and BOTTOM layer-shell surfaces are also rendered inside each workspace card before window content. TOP and OVERLAY layers, layer popups, IME popups, and drag icons render above the overview in the native layer phase.
The overview uses live window and layer rendering. Hyprview does not keep cached window thumbnails or a snapshot fallback path. Closing windows may still draw Hyprland's own close-animation snapshot after their live surfaces are gone.
| quirk | detail |
|---|---|
| Lua-only plugin | The plugin refuses to initialize unless Hyprland is using Lua config. |
| ABI-sensitive | Build against the same Hyprland commit/API hash that will load the plugin. |
| Current monitor only | Workspaces from other monitors are excluded. |
| Special workspaces excluded | Special workspaces are not shown and are not valid insertion targets. |
| Empty workspace visibility | Empty workspaces are hidden unless they are the active workspace. |
| Pinned windows | Pinned windows are visible only on the active workspace and cannot be dragged across workspaces. |
| Horizontal pan scope | Right-drag panning only works for workspaces whose tiled algorithm name is scrolling; it pans tiled windows only. |
| Workspace insertion scope | Insertion markers only use positive numeric workspace IDs and obey monitor-bound workspace rules. |
| Unknown config strings | Unknown focus_indicator, workspace_annotation, or annotation position values fall back to the default behavior rather than failing config. |
| Background blur cost | background_blur = true renders live background layers and routes them through Hyprland's monitor preblur resource, which is heavier than an unblurred backdrop. |
| Cursor sync | Keybind-driven focus changes can warp the cursor to the focused live window entry for a short sync window. |
| Background freshness | The backdrop and window content are live-rendered from Hyprland surfaces while the overview is open. |
| Bring action target | bring does not require hovering a specific window. It chooses the topmost mapped, non-hidden window from the selected workspace. |
| Unknown overview actions | Unknown actions open the overview when closed and otherwise do nothing. |