woven
Press a key, see all your workspaces and windows at once, click to focus.
What It Does
Woven runs as a persistent background daemon. When you trigger it, a fullscreen overlay appears showing all your workspaces as columns with interactive window cards. Click any card to focus that window and close the overlay. Right-click or press any key to dismiss.
It doesn't replace your bar, your compositor's built-in features, or anything else. It just adds a keyboard-driven spatial overview that works consistently across Hyprland, Niri, and Sway.
Window Cards
Each window in the overview is a card showing its title. Hover a card to reveal action buttons:
| Button | Action |
|---|---|
| ✕ | Close window |
| ⧉ | Toggle float |
| ⊞ | Toggle fullscreen |
| ⬡ | Toggle pin |
Overview Controls
| Action | Result |
|---|---|
| Click a window card | Focus that window, close overlay |
| Hover a window card | Show action buttons |
| Right-click / any key | Close overlay |
| Scroll | Scroll through workspaces |
Compositor Support
| Compositor | Status | Notes |
|---|---|---|
| Hyprland | ✓ Full support | Primary target |
| Niri | ⚠ Performance issues | Known in v1, tracked for v2 |
| Sway | ⚠ Implemented, untested | Report issues on GitHub |
| GNOME | ✕ Not supported | Does not implement wlr-layer-shell |
| KDE | → Planned v2.5/3 | Limited support, not for daily use |
Project Structure
woven/
├── woven-sys/ main process — Lua VM, IPC server, compositor backend
├── woven-render/ render thread — Wayland surface, tiny-skia painter
├── woven-common/ shared types and IPC protocol
├── woven-ctrl/ Iced GUI + CLI control panel
├── runtime/ Lua runtime files shipped with the install
├── woven.lua default config
├── woven.service systemd user service
└── get.sh curl installer
| Path | Contents |
|---|---|
~/.config/woven/woven.lua | Your config |
~/.config/woven/runtime/ | Lua runtime files |
/run/user/$UID/woven.sock | IPC socket |
Installation
Quick Install
The fastest path. Downloads a prebuilt release, extracts it, and copies everything to the right places.
curl -fsSL https://raw.githubusercontent.com/viewerofall/woven/main/get.sh | bash
get.sh handles: binaries to ~/.local/bin/, runtime + config to ~/.config/woven/, service to ~/.config/systemd/user/, enables the service.
~/.local/bin is in your $PATH. Add export PATH="$HOME/.local/bin:$PATH" to your shell rc if not.Manual Install — From Release Archive
Download comp.tar.gz from the releases page, then:
tar -xzf comp.tar.gz
cp woven.service ~/.config/systemd/user/
cp -r runtime ~/.config/woven/
cp woven.lua ~/.config/woven/
cargo build --release
cp target/release/woven ~/.local/bin/
cp target/release/woven-ctrl ~/.local/bin/
Manual Install — From Git
git clone https://github.com/viewerofall/woven.git && cd woven
mkdir -p ~/.config/woven
cp -r runtime ~/.config/woven/
cp woven.lua ~/.config/woven/
cp woven.service ~/.config/systemd/user/
systemctl --user daemon-reload
systemctl --user enable woven.service
cargo build --release
cp target/release/woven ~/.local/bin/
cp target/release/woven-ctrl ~/.local/bin/
Shell Environment
Woven needs WOVEN_ROOT set to find its config and runtime files:
sudo touch /etc/profile.d/woven.sh
echo 'export WOVEN_ROOT="$HOME/.config/woven"' | sudo tee -a /etc/profile.d/woven.sh
source /etc/profile.d/woven.sh
First-Time Setup
On first launch, if no config exists, Woven opens the setup wizard automatically. It handles compositor detection, theme selection, and keybind instructions — no terminal interaction required. You can also run it manually:
woven-ctrl --setup
Compositor Keybind Setup
Hyprland
exec-once = woven
bind = SUPER, grave, exec, woven-ctrl --toggle
Niri
spawn-at-startup "woven"
binds {
Super+Grave { spawn "woven-ctrl" "--toggle"; }
}
Sway
exec woven
bindsym Super+grave exec woven-ctrl --toggle
Verify
systemctl --user status woven
woven-ctrl --toggle # should open the overlay
Uninstall
systemctl --user stop woven && systemctl --user disable woven
rm ~/.local/bin/woven ~/.local/bin/woven-ctrl
rm -rf ~/.config/woven
rm ~/.config/systemd/user/woven.service
systemctl --user daemon-reload
Configuration
Config lives at ~/.config/woven/woven.lua. Edit directly or open woven-ctrl for the GUI editor. Apply changes without restarting:
woven-ctrl --reload
woven.theme()
Controls all visual properties of the overlay.
woven.theme({
background = "#1e1e2e",
border = "#6c7086",
text = "#cdd6f4",
accent = "#cba6f7",
border_radius = 12,
font = "JetBrainsMono Nerd Font",
font_size = 13,
opacity = 0.92,
})
| Key | Type | Description |
|---|---|---|
background | hex string | Overlay background color |
border | hex string | Window card border color |
text | hex string | Window title text color |
accent | hex string | Focused window / active highlight |
border_radius | number | Card corner radius in pixels |
font | string | Font name — match exact fc-list output |
font_size | number | Font size in points |
opacity | float 0–1 | Overall overlay opacity |
woven.workspaces()
woven.workspaces({
show_empty = false,
min_width = 200,
max_width = 400,
})
| Key | Type | Default | Description |
|---|---|---|---|
show_empty | bool | false | Show workspaces with no windows |
min_width | number | 200 | Min workspace column width (px) |
max_width | number | 400 | Max workspace column width (px) |
woven.settings()
woven.settings({
scroll_dir = "horizontal",
overlay_opacity = 0.92,
})
| Key | Type | Default | Description |
|---|---|---|---|
scroll_dir | string | "horizontal" | "horizontal" or "vertical" |
overlay_opacity | float 0–1 | 0.92 | Overlay opacity |
woven.animations()
woven.animations({
overlay_open = { curve = "ease_out_cubic", duration_ms = 180 },
overlay_close = { curve = "ease_in_cubic", duration_ms = 120 },
scroll = { curve = "ease_in_out_cubic", duration_ms = 200 },
})
Available Curves
| Value | Description |
|---|---|
linear | Constant speed |
ease_out_cubic | Fast start, slow end — good for opening |
ease_in_cubic | Slow start, fast end — good for closing |
ease_in_out_cubic | Slow at both ends — good for scrolling |
spring | Physics-based spring motion |
Full Default Config
woven.theme({
background = "#1e1e2e",
border = "#6c7086",
text = "#cdd6f4",
accent = "#cba6f7",
border_radius = 12,
font = "JetBrainsMono Nerd Font",
font_size = 13,
opacity = 0.92,
})
woven.workspaces({
show_empty = false,
min_width = 200,
max_width = 400,
})
woven.settings({
scroll_dir = "horizontal",
overlay_opacity = 0.92,
})
woven.animations({
overlay_open = { curve = "ease_out_cubic", duration_ms = 180 },
overlay_close = { curve = "ease_in_cubic", duration_ms = 120 },
scroll = { curve = "ease_in_out_cubic", duration_ms = 200 },
})
Example: Deep Purple Theme
woven.theme({
background = "#0a0010",
border = "#331144",
text = "#d0c0ff",
accent = "#c792ea",
border_radius = 8,
font = "JetBrainsMono Nerd Font",
font_size = 12,
opacity = 0.95,
})
woven.workspaces({ show_empty = false, min_width = 220, max_width = 380 })
woven.settings({ scroll_dir = "horizontal" })
woven.animations({
overlay_open = { curve = "spring", duration_ms = 200 },
overlay_close = { curve = "ease_in_cubic", duration_ms = 100 },
scroll = { curve = "ease_in_out_cubic", duration_ms = 180 },
})
woven-ctrl
woven-ctrl is both a GUI control panel and a CLI tool. Run it bare for the GUI, pass flags to drive it from the terminal or compositor keybinds.
GUI Mode
woven-ctrl
Opens the graphical control panel where you can edit your theme visually, switch presets, use the built-in Lua editor, toggle the overlay, and run the setup wizard.
CLI Reference
| Command | Description |
|---|---|
woven-ctrl | Open the GUI control panel |
woven-ctrl --toggle | Toggle overlay open/closed |
woven-ctrl --show | Force overlay open |
woven-ctrl --hide | Force overlay closed |
woven-ctrl --reload | Reload woven.lua from disk |
woven-ctrl --setup | Run the first-time setup wizard |
--toggle
The primary keybind command. Bind this in your compositor config. See Installation for examples.
--show / --hide
Force a specific state regardless of current visibility. Useful in scripts where you need predictable state rather than a toggle.
--reload
Hot-reloads woven.lua without restarting the daemon or recreating the Wayland surface. The built-in editor in GUI mode calls this internally when you save.
--setup
Runs the first-time setup wizard. Handles compositor auto-detection, color theme selection, and keybind instructions. Also runs automatically on first launch if no config is found.
IPC Socket
woven-ctrl communicates with the daemon via:
/run/user/<UID>/woven.sock
If the socket isn't found (daemon not running), CLI commands will fail with a clear error.
Scripting Examples
Hide on game launch (Steam)
woven-ctrl --hide; %command%; woven-ctrl --show
Auto-reload on config save
while inotifywait -e close_write ~/.config/woven/woven.lua; do
woven-ctrl --reload
done
Check daemon status
if systemctl --user is-active --quiet woven; then
echo "woven is running"
fi
Architecture
How Woven works internally. Useful for contributors, debuggers, or anyone who wants to understand what's running.
Crate Layout
| Crate | Role |
|---|---|
woven-sys | Main process: Lua VM, IPC server, compositor backend |
woven-render | Render thread: Wayland surface, painter, input handling |
woven-common | Shared types and IPC protocol definitions |
woven-ctrl | Iced GUI + CLI control panel |
Daemon Lifecycle
systemd --user starts woven (woven.service)
└─ woven-sys initializes
└─ Lua VM loads ~/.config/woven/woven.lua
└─ IPC socket created at /run/user/$UID/woven.sock
└─ Compositor backend connects
└─ daemon idles, waiting for IPC commands
woven-ctrl --toggle received
└─ if overlay hidden → woven-render shows surface, queries
compositor, renders workspace cards
└─ if overlay visible → surface hidden
Wayland Surface
A single fullscreen zwlr_layer_shell_v1 surface kept alive between uses:
layer: TOP
anchor: TOP | BOTTOM | LEFT | RIGHT (fullscreen)
exclusive_zone: -1 (overlay — doesn't push other surfaces)
keyboard: exclusive while visible, None when hidden
Show/hide uses attach(None)+commit rather than destroying and recreating the surface, avoiding protocol re-negotiation and visual glitches on toggle.
Compositor Backends
Auto-detected on startup by checking environment variables. Each backend implements the same internal trait — woven-render doesn't need to know which compositor it's talking to.
| Compositor | Detection | IPC mechanism |
|---|---|---|
| Hyprland | $HYPRLAND_INSTANCE_SIGNATURE | Hyprland socket |
| Niri | $NIRI_SOCKET | Niri IPC socket |
| Sway | $SWAYSOCK | Sway IPC socket |
Lua VM
The Lua runtime (mlua, Lua 5.4) runs in woven-sys and is purely for config parsing. It loads woven.lua on startup and on --reload, converts the resulting config tables to Rust structs, then sits idle. It does not run a tick loop or drive rendering.
Render Pipeline
When the overlay opens:
1. Query compositor backend → Vec<Workspace { windows: Vec<Window> }>
2. Compute layout: workspace columns, card positions
3. tiny-skia renders to back buffer:
- background fill with opacity
- workspace column separators
- window cards (border, title, accent on focused window)
4. attach buffer → commit → overlay visible
5. Input loop:
click → focus command to compositor → close overlay
scroll → repaint
key / RMB → close overlay
IPC Protocol
Newline-delimited text over Unix socket:
CLIENT → SERVER: "toggle\n"
SERVER → CLIENT: "ok\n"
CLIENT → SERVER: "reload\n"
SERVER → CLIENT: "ok\n" | "err:<message>\n"
Key Dependencies
| Crate | Purpose |
|---|---|
smithay-client-toolkit | Wayland client abstractions |
wayland-client | Raw Wayland protocol bindings |
wayland-protocols | zwlr_layer_shell_v1 and friends |
mlua | Lua 5.4 runtime |
tiny-skia | Software rasterizer (painter backend) |
fontdue | Font rasterization |
iced | woven-ctrl GUI framework |
tokio | Async runtime (IPC server) |
Troubleshooting
Overlay doesn't appear on --toggle
Check the daemon is running:
systemctl --user status woven
If not running:
systemctl --user start woven
Check the IPC socket exists:
ls /run/user/$(id -u)/woven.sock
If not there, the daemon crashed. Check logs:
journalctl --user -u woven -n 50
Check WOVEN_ROOT is set:
echo $WOVEN_ROOT
# expected: /home/<you>/.config/woven
Daemon starts then immediately exits
Check logs for the reason:
journalctl --user -u woven -n 50
Common causes: Lua syntax error in woven.lua (line number shown), compositor not detected (check the relevant env var is set), or ~/.config/woven/runtime/ missing.
Lua error on startup / reload
The error message shows the file and line number. Common mistakes: missing comma between table fields, unclosed string, mismatched braces. Validate before reloading:
luac -p ~/.config/woven/woven.lua
Overlay appears but is completely blank
The compositor backend query is failing. Woven can't get workspace/window data.
Hyprland: check $HYPRLAND_INSTANCE_SIGNATURE is set and the socket exists at /tmp/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket.sock.
Niri: run niri msg workspaces — if it fails, Niri IPC isn't working.
Performance issues on Niri
Workaround: increase animation duration_ms slightly to mask the delay. Use show_empty = false to reduce rendered columns.
Overlay doesn't close on keypress
Another layer-shell surface may be grabbing exclusive keyboard focus. Check that no bar or other surface is set to keyboard = exclusive. Workaround: bind woven-ctrl --hide to a key explicitly.
Font not rendering / showing boxes
Check the exact font name:
fc-list | grep -i "jetbrains"
Use that exact string in woven.lua — font names are case sensitive and must match fc-list output exactly.
woven-ctrl GUI won't open
Check woven-ctrl is in PATH (which woven-ctrl) and that $WAYLAND_DISPLAY is set (echo $WAYLAND_DISPLAY).
Getting Help
File an issue at github.com/viewerofall/woven/issues. Include: Woven version, compositor + version, journalctl --user -u woven -n 50 output, your woven.lua.
Roadmap
v2 — In Progress
Window Thumbnails
Live window previews inside cards so you can visually identify windows without reading titles. The render pipeline will query compositor screencopy for each visible window and composite the thumbnail into the card. Windows without available thumbnails fall back to a colored placeholder.
Lua Plugin API
Load custom Lua modules from ~/.config/woven/plugins/. Plugins can extend the overlay with custom cards, inject data into the workspace display, or respond to overlay open/close events. Plugins hot-reload alongside woven.lua on --reload.
River Backend
River uses the river-status protocol rather than Hyprland/Niri/Sway IPC. v2 adds a River compositor backend so Woven works for River users without any config changes.
Niri-Style Popout + Top Bar
Two visual upgrades inspired by Niri's native overview:
- Popout: workspaces pop outward from their screen position when the overlay opens, giving spatial depth to the transition
- Top bar in overview: a bar along the top of the overlay surface showing current time, active workspace name, and quick actions — separate from your system panel, lives only inside the overview
Better X11-to-Wayland Compatibility
Improved handling of XWayland windows — better title detection, window class resolution for icons, correct geometry for windows that don't report Wayland-native size hints.
v2.5 / v3
KDE Support
KDE uses a completely different window management stack and does not implement wlr-layer-shell. Support is planned but will require a separate backend and won't be intended for daily use.
Not Planned
- GNOME — no
wlr-layer-shell, no viable path without a fundamentally different approach - macOS / Windows — out of scope
- Built-in system info widgets — use a bar for that