diff --git a/dot_config/umbriel/binds.toml b/dot_config/umbriel/binds.toml new file mode 100644 index 0000000..fde3238 --- /dev/null +++ b/dot_config/umbriel/binds.toml @@ -0,0 +1,165 @@ +# Keybinds. Defining [keybinds] replaces Umbriel's built-in set. +# Mod = Super. Actions: `umbriel msg --help`; `umbriel submap` shows the layer. +# +# Merged from niri (70-binds.kdl) and Hyprland (bindings.lua). Where both bind a +# key differently, niri's scrolling-layout bind wins, except the scratchpad keys: +# Hyprland's toggles carry over. Launchers and panels are Noctalia's, not +# vicinae's. Apps with one window cycle through umbriel-launch (focus or start). + +[keybinds] + +# ------------------------------------------------------------------------------ +# Shell (Noctalia) and session + +"Mod+Space" = { action = "spawn:noctalia msg panel-toggle launcher", repeat = false } +"Mod+Shift+Space" = { action = "spawn:noctalia msg panel-toggle launcher /emo", repeat = false } +"Mod+P" = { action = "spawn:noctalia msg panel-toggle clipboard", repeat = false } +"Mod+Period" = { action = "spawn:noctalia msg panel-toggle control-center", repeat = false } +"Mod+Comma" = { action = "spawn:noctalia msg settings-toggle", repeat = false } +"Super+Alt+Ctrl+N" = { action = "spawn:noctalia msg panel-open noctalia/notes:panel", repeat = false } +"Mod+Ctrl+R" = { action = "spawn:noctalia msg plugin prdlk/ostt:recorder focused toggle", repeat = false } +"Mod+Shift+Q" = { action = "spawn:noctalia msg panel-toggle session", repeat = false } +"Mod+Shift+E" = "session-quit" +"Mod+Escape" = { action = "shortcuts-inhibit-toggle", allow_when_inhibited = true, repeat = false } +"Mod+Tab" = { action = "overview-toggle", repeat = false } + +# Screenshots (Noctalia; no per-window capture) +"Mod+S" = { action = "spawn:noctalia msg screenshot-region", repeat = false } +"Mod+Ctrl+S" = { action = "spawn:noctalia msg screenshot-fullscreen", repeat = false } +"Mod+Ctrl+Shift+S" = { action = "spawn:noctalia msg screenshot-fullscreen all", repeat = false } +"Print" = { action = "spawn:noctalia msg screenshot-region", repeat = false } +"Ctrl+Print" = { action = "spawn:noctalia msg screenshot-fullscreen", repeat = false } + +"Mod+F24" = { action = "spawn:voxtype record toggle --profile hermes", repeat = false } + +# ------------------------------------------------------------------------------ +# Scratchpads (Hyprland special workspaces; rules.toml) + +"Mod+A" = "scratchpad-toggle:tern" +"Super+Alt+Ctrl+H" = "scratchpad-toggle:tern" +"Super+Alt+Ctrl+L" = "scratchpad-toggle:claude" +"Super+Ctrl+A" = "scratchpad-toggle:claude" +"Super+Alt+Ctrl+E" = "scratchpad-toggle:fastmail" +"Super+Alt+Ctrl+M" = "scratchpad-toggle:beeper" +"Super+Alt+Ctrl+B" = "scratchpad-toggle:beeper" +"Super+Ctrl+H" = "scratchpad-toggle:home" +"Super+Ctrl+L" = "scratchpad-toggle:lmstudio" + +# ------------------------------------------------------------------------------ +# Apps: focus or launch. Spawns run through /bin/sh with the session PATH, +# which lacks ~/.local/bin, hence the explicit path. + +"Super+Alt+Ctrl+J" = { action = "spawn:~/.local/bin/umbriel-launch '^(kitty|alacritty|com[.]mitchellh[.]ghostty)' kitty", repeat = false } +"Super+Alt+Ctrl+K" = { action = "spawn:~/.local/bin/umbriel-launch '^brave-origin$' brave-origin", repeat = false } +"Super+Alt+Ctrl+G" = { action = "spawn:~/.local/bin/umbriel-launch ghostty ghostty", repeat = false } +"Super+Alt+Ctrl+O" = { action = "spawn:~/.local/bin/umbriel-launch '^obsidian$' obsidian", repeat = false } +"Super+Alt+Ctrl+F" = { action = "spawn:~/.local/bin/umbriel-launch '^io[.]github[.]lgse[.]Strata$' gtk-launch io.github.lgse.Strata", repeat = false } +"Super+Alt+Ctrl+W" = { action = "spawn:~/.local/bin/umbriel-launch '^tldraw-offline$' gtk-launch tldraw-offline", repeat = false } +"Super+Alt+Ctrl+T" = { action = "spawn:~/.local/bin/umbriel-launch '^todoist' gtk-launch todoist", repeat = false } +"Super+Alt+Ctrl+Y" = { action = "spawn:~/.local/bin/umbriel-launch '^(webapp-|chrome-|brave-.+__)' gtk-launch YouTube", repeat = false } +"Super+Alt+Ctrl+Space" = { action = "spawn:~/.local/bin/umbriel-launch '^sticky-notest-canvas$' gtk-launch io.github.faridjaff.StickyNotesCanvas", repeat = false } +"Super+Ctrl+G" = { action = "spawn:~/.local/bin/umbriel-launch '^gemini-desktop$' gemini-desktop", repeat = false } +"Mod+Shift+G" = { action = "spawn:kitty --class=kitty-goliath --title=goliath --config ~/.config/kitty/goliath.conf", repeat = false } + +# ------------------------------------------------------------------------------ +# Focus + +"Mod+H" = "window-focus-left" +"Mod+L" = "window-focus-right" +"Mod+J" = "window-focus-or-workspace-down" +"Mod+K" = "window-focus-or-workspace-up" +"Mod+BackSpace" = "window-focus-last" +"Mod+WheelUp" = { action = "window-focus-left", cooldown_ms = 80 } +"Mod+WheelDown" = { action = "window-focus-right", cooldown_ms = 80 } + +# Outputs +"Mod+Left" = "output-focus-left" +"Mod+Right" = "output-focus-right" +"Mod+Up" = "output-focus-up" +"Mod+O" = "output-focus-next" +"Super+Ctrl+J" = "output-focus-next" +"Super+Ctrl+K" = "output-focus-previous" + +# ------------------------------------------------------------------------------ +# Move + +"Mod+Shift+H" = "column-move-left" +"Mod+Shift+L" = "column-move-right" +"Super+Ctrl+Shift+H" = "column-move-to-first" +"Super+Ctrl+Shift+L" = "column-move-to-last" +"Mod+Shift+J" = "column-move-to-workspace-next" +"Mod+Shift+K" = "column-move-to-workspace-previous" +"Mod+Shift+Down" = "column-move-to-output-right" +"Mod+Shift+Up" = "column-move-to-output-left" +"Mod+Shift+O" = "window-move-to-output-next" +"Mod+Ctrl+Shift+J" = "window-move-to-output-next" +"Mod+Ctrl+Shift+K" = "window-move-to-output-previous" + +# ------------------------------------------------------------------------------ +# Size and state + +"Mod+R" = "window-cycle-primary-extent" +"Mod+Shift+R" = "window-cycle-secondary-extent" +"Alt+Shift+H" = "window-modify-primary-extent:-0.1" +"Alt+Shift+L" = "window-modify-primary-extent:0.1" +"Alt+Shift+J" = "window-modify-secondary-extent:-0.1" +"Alt+Shift+K" = "window-modify-secondary-extent:0.1" +"Mod+F" = { action = "window-toggle-maximize", repeat = false } +"Mod+Shift+F" = { action = "window-toggle-maximize-to-edges", repeat = false } +"Mod+Ctrl+F" = { action = "window-toggle-fullscreen", repeat = false } +"Mod+C" = "column-center" +"Mod+Ctrl+O" = { action = "window-toggle-floating", repeat = false } +"Mod+W" = { action = "column-toggle-tabbed", repeat = false } +"Mod+Q" = { action = "window-close", repeat = false } + +# ------------------------------------------------------------------------------ +# Workspaces: Super+1–9 (Omarchy), Alt+Q…O → 1–8 (Hyprland) + +"Mod+1" = "workspace-switch:1" +"Mod+2" = "workspace-switch:2" +"Mod+3" = "workspace-switch:3" +"Mod+4" = "workspace-switch:4" +"Mod+5" = "workspace-switch:5" +"Mod+6" = "workspace-switch:6" +"Mod+7" = "workspace-switch:7" +"Mod+8" = "workspace-switch:8" +"Mod+9" = "workspace-switch:9" +"Mod+Shift+1" = "window-move-to-workspace:1" +"Mod+Shift+2" = "window-move-to-workspace:2" +"Mod+Shift+3" = "window-move-to-workspace:3" +"Mod+Shift+4" = "window-move-to-workspace:4" +"Mod+Shift+5" = "window-move-to-workspace:5" +"Mod+Shift+6" = "window-move-to-workspace:6" +"Mod+Shift+7" = "window-move-to-workspace:7" +"Mod+Shift+8" = "window-move-to-workspace:8" +"Mod+Shift+9" = "window-move-to-workspace:9" +"Alt+Q" = "workspace-switch:1" +"Alt+W" = "workspace-switch:2" +"Alt+E" = "workspace-switch:3" +"Alt+R" = "workspace-switch:4" +"Alt+Y" = "workspace-switch:5" +"Alt+U" = "workspace-switch:6" +"Alt+I" = "workspace-switch:7" +"Alt+O" = "workspace-switch:8" +"Alt+Shift+Q" = "window-move-to-workspace:1" +"Alt+Shift+W" = "window-move-to-workspace:2" +"Alt+Shift+E" = "window-move-to-workspace:3" +"Alt+Shift+R" = "window-move-to-workspace:4" +"Alt+Shift+Y" = "window-move-to-workspace:5" +"Alt+Shift+U" = "window-move-to-workspace:6" +"Alt+Shift+I" = "window-move-to-workspace:7" +"Alt+Shift+O" = "window-move-to-workspace:8" + +# ------------------------------------------------------------------------------ +# Hardware keys (Noctalia OSD) + +"XF86AudioRaiseVolume" = { action = "spawn:noctalia msg volume-up", allow_when_locked = true } +"XF86AudioLowerVolume" = { action = "spawn:noctalia msg volume-down", allow_when_locked = true } +"XF86AudioMute" = { action = "spawn:noctalia msg volume-mute", allow_when_locked = true, repeat = false } +"XF86AudioMicMute" = { action = "spawn:noctalia msg mic-mute", allow_when_locked = true, repeat = false } +"XF86AudioNext" = { action = "spawn:noctalia msg media next", allow_when_locked = true, repeat = false } +"XF86AudioPrev" = { action = "spawn:noctalia msg media previous", allow_when_locked = true, repeat = false } +"XF86AudioPlay" = { action = "spawn:noctalia msg media toggle", allow_when_locked = true, repeat = false } +"XF86AudioPause" = { action = "spawn:noctalia msg media toggle", allow_when_locked = true, repeat = false } +"XF86MonBrightnessUp" = { action = "spawn:noctalia msg brightness-up", allow_when_locked = true } +"XF86MonBrightnessDown" = { action = "spawn:noctalia msg brightness-down", allow_when_locked = true } diff --git a/dot_config/umbriel/config.toml b/dot_config/umbriel/config.toml new file mode 100644 index 0000000..52e138a --- /dev/null +++ b/dot_config/umbriel/config.toml @@ -0,0 +1,101 @@ +# Umbriel — scrolling compositor paired with the Noctalia shell. +# Reference: https://docs.noctalia.dev/umbriel/ · check: `umbriel config validate` +# +# Ported from the niri config (layout, rules, binds) plus Hyprland's named +# scratch workspaces, which map onto Umbriel scratchpads (rules.toml). Noctalia +# replaces vicinae, cliphist, and polkit-gnome: its launcher, clipboard history, +# and polkit agent run inside the one shell process. +# +# Included files apply in list order, this file last. Paths resolve against the +# symlink in ~/.config/umbriel, not the chezmoi source. + +[include] +files = ["outputs.toml", "rules.toml", "binds.toml"] + +# Theme colors, written by Noctalia's umbriel template on every theme change +# (not managed by chezmoi). Its post-hook keeps this entry last in the list. +[include.optional] +files = ["noctalia.toml"] + +[general] +# Noctalia brings bar, launcher, notifications, clipboard history, and the +# polkit agent; nothing else is started. +autostart = ["noctalia"] +show_cheatsheet = false + +# Umbriel owns XDG_CURRENT_DESKTOP and the session variables. +[environment] +ELECTRON_OZONE_PLATFORM_HINT = "auto" +QT_QPA_PLATFORM = "wayland" +QT_QPA_PLATFORMTHEME = "kde" +QT_STYLE_OVERRIDE = "Darkly" + +[input.keyboard] +layout = "us" +repeat_delay = 400 +repeat_rate = 50 +numlock_toggle = true + +[input.touchpad] +natural_scroll = true +tap = true +tap_button_map = "left_right_middle" +sensitivity = 1.0 + +[input.tablet] +map_to_output = "eDP-1" + +[input.touch] +map_to_output = "eDP-1" + +[input.cursor] +theme = "capitaine-cursors-light" +size = 24 +hide_when_typing = true + +[layout] +gap = 8 +extent_presets = [0.333, 0.5, 0.667] + +[layout.scrolling] +default_extent_fraction = 0.5 +center_focused = "never" +center_underfull_strip = true + +[appearance] +corner_radius = 10 + +# Optimized blur samples only the wallpaper: cheaper than blurring the windows +# behind each translucent surface. +[appearance.blur] +radius = 3 + +[appearance.tab_bar] +hide_when_single = true + +[overview] +zoom = 0.4 + +# niri's springs: under-damped, high stiffness — short settle, slight overshoot. +[animation.windows_in] +curve = "spring:0.82,1125" + +[animation.windows_out] +curve = "spring:0.88,2025" + +[animation.windows_move] +curve = "spring:0.85,1463" + +[animation.workspaces] +curve = "spring:0.78,1350" + +[animation.overview] +curve = "spring:0.85,1463" +workspace_curve = "spring:0.78,1350" + +# Hyprland's special workspaces show their app tiled over the whole output +# above a dimmed desktop; maximize is the scratchpad equivalent. +[animation.scratchpad] +curve = "spring:0.85,1463" +dim = 0.6 +maximize = true diff --git a/dot_config/umbriel/outputs.toml b/dot_config/umbriel/outputs.toml new file mode 100644 index 0000000..6300c6b --- /dev/null +++ b/dot_config/umbriel/outputs.toml @@ -0,0 +1,66 @@ +# Outputs by monitor identity (`umbriel outputs` → "Config name"), so the +# settings follow a display across ports. Unlisted displays use their preferred +# mode at scale 1. +# +# Scale: the externals land near 110 logical px per inch (the ultrawide's +# native density), so a window keeps its size moving between them. The laptop +# panel runs denser (149) at 1.5: at 2 its 1280x800 felt too zoomed in. +# +# display physical ppi scale logical logical ppi +# laptop panel 2560x1600 224 1.5 1706x1066 149 +# Studio Display 5120x2880 218 2 2560x1440 109 +# ultrawide 3440x1440 110 1 3440x1440 110 +# portables 1920x1080 141 1.25 1536x864 113 +# +# Layout: the laptop panel sits at the origin and each external display sits +# directly to its left (negative x), bottoms aligned. The externals are used +# one at a time, so they share that slot. Positions are logical, so a +# display's offset is its scaled size. +# +# niri needed `output "DP-5" { off }` for the Studio Display's phantom 640x480 +# connector; Umbriel leaves connectors without a preferred mode or identity off. + +# Laptop panel, eDP-1 (niri 91-internal-display): one full-width column. +[output."Tianma Microelectronics Ltd. TL134ADXP03 Unknown"] +position = [0, 0] +scale = 1.5 +vrr = "fullscreen" +focus_at_startup = true + +[output."Tianma Microelectronics Ltd. TL134ADXP03 Unknown".layout.scrolling] +default_extent_fraction = 1.0 + +# Apple Studio Display (niri 93-apple-display): 2560x1440 logical. +[output."Apple Computer Inc StudioDisplay 0x984262A7"] +position = [-2560, -374] +scale = 2.0 + +[output."Apple Computer Inc StudioDisplay 0x984262A7".layout.scrolling] +default_extent_fraction = 0.5 + +# Samsung ultrawide (niri 92-ultrawide, Hyprland monitors.lua): 3440x1440. +[output."Samsung Electric Company S34CG50 HNTY501103"] +mode = "3440x1440@100" +position = [-3440, -374] +scale = 1.0 +vrr = "fullscreen" + +[output."Samsung Electric Company S34CG50 HNTY501103".layout.scrolling] +default_extent_fraction = 0.333 + +# Portable HDMI panel, mounted portrait (niri 94-portable-hdmi): 864x1536. +[output."PNP(AOP) 16PM1QB 14490001D3X00"] +transform = "90" +position = [-864, -470] +scale = 1.25 + +[output."PNP(AOP) 16PM1QB 14490001D3X00".layout.scrolling] +default_extent_fraction = 1.0 + +# Portable USB-C panel (niri 95-portable-usbc): 1536x864. +[output."ASUSTek COMPUTER INC ASUS MB16AC M8LMTF195749"] +position = [-1536, 202] +scale = 1.25 + +[output."ASUSTek COMPUTER INC ASUS MB16AC M8LMTF195749".layout.scrolling] +default_extent_fraction = 1.0 diff --git a/dot_config/umbriel/rules.toml b/dot_config/umbriel/rules.toml new file mode 100644 index 0000000..1d975a9 --- /dev/null +++ b/dot_config/umbriel/rules.toml @@ -0,0 +1,155 @@ +# Window rules, layer rules, and scratchpads. Every matching rule applies; the +# later one wins a conflicting key. `umbriel windows` lists app ids and titles. + +# ============================================================================== +# Scratchpads — Hyprland's special workspaces (omarchy-toggle-scratch). Each +# app opens hidden in its scratchpad; toggling an empty one launches it. + +[[scratchpad]] +name = "tern" +spawn_when_empty = "tern" + +[[scratchpad]] +name = "claude" +spawn_when_empty = "claude-desktop" + +[[scratchpad]] +name = "fastmail" +spawn_when_empty = "gtk-launch com.fastmail.Fastmail" + +[[scratchpad]] +name = "beeper" +spawn_when_empty = "gtk-launch beeper" + +[[scratchpad]] +name = "home" +spawn_when_empty = "gtk-launch 'Google Home'" + +[[scratchpad]] +name = "lmstudio" +spawn_when_empty = "gtk-launch ai.elementlabs.lmstudio" + +# ============================================================================== +# Every window + +[[window_rule]] +blur = true + +# ============================================================================== +# Translucent apps (niri: opacity 0.9, blur, open focused) + +[[window_rule]] +match.app_id = [ + "^Folo$", "^Spotify$", "^zen$", "^brave-origin$", "^gemini-desktop$", + "^claude-desktop$", "^kitty", "^zed$", "^obsidian$", "^Beeper$", + "^chromium$", "^com[.]mitchellh[.]ghostty$", +] +opacity = 0.9 +default_floating = false +default_focused = true + +# ============================================================================== +# Floating utilities + +[[window_rule]] +match.app_id = "^dev[.]noctalia[.]Noctalia$" +default_floating = true +default_floating_size_px = { width = 1080, height = 920 } + +[[window_rule]] +match.app_id = "^dev[.]noctalia[.]UmbrielSharePicker$" +default_floating = true +default_floating_size_px = { width = 800, height = 600 } + +[[window_rule]] +match.app_id = ["^io[.]github[.]lgse[.]Strata$", "^org[.]gnome[.]Nautilus$", "^com[.]gabm[.]satty$"] +default_floating = true +default_focused = true +default_floating_size = { width = 0.5 } + +# Hyprland: YouTube web app floats pinned in the bottom-left corner. +[[window_rule]] +match.app_id = "^brave-(www[.])?youtube[.]com__" +default_floating = true +default_pinned = true +default_floating_size = { width = 0.3, height = 0.3 } +default_position = { x = 10, y = 10, anchor = "bottom_left" } + +# Picture-in-picture: top-right corner of the laptop panel (after the YouTube +# rule, so its own PiP window lands here too). +[[window_rule]] +match.title = "^(Picture-in-Picture|Picture in picture)$" +default_floating = true +default_maximize = false +default_focused = false +opacity = 1.0 +default_output = "eDP-1" +default_position = { x = 24, y = 24, anchor = "top_right" } + +[[window_rule]] +match.title = "^notificationtoasts_.+_desktop" +default_position = { x = 0, y = 0, anchor = "bottom_right" } +default_focused = false +default_pinned = true + +# ============================================================================== +# Placement (niri 91-internal-display / 93-apple-display) + +# Chat, mail, and dashboards open maximized on the laptop's first workspace. +[[window_rule]] +match.app_id = [ + "^chrome-youtube[.]com__-Default$", "^chromium$", "^org[.]gnome[.]Calendar$", + "^discord$", "^slack$", "^gemini-desktop$", "^Hermes$", "^todoist$", + "^WebApp-Cloudflare", +] +default_output = "eDP-1" +default_workspace = 1 +default_maximize = true +default_focused = true + +# Editors and terminals open on the Studio Display when it is connected. +[[window_rule]] +match.app_id = [ + "^zed$", "^obsidian$", "^sticky-notest-canvas$", "^vacuumtube$", "^Fladder$", + "^kitty$", "^kitty-herdr$", "^kitty-tmux$", +] +default_output = "Apple Computer Inc StudioDisplay 0x984262A7" + +[[window_rule]] +match.title = "^Sticky Notes$" +default_output = "Apple Computer Inc StudioDisplay 0x984262A7" + +# Scratchpad residents, last so they win over the placement rules above. Their +# presentation (maximized) is animation.scratchpad in config.toml. +[[window_rule]] +match.app_id = "^so[.]stencil[.]tern$" +default_scratchpad = "tern" + +[[window_rule]] +match.app_id = "^com[.]anthropic[.]Claude$" +default_scratchpad = "claude" + +[[window_rule]] +match.app_id = "^com[.-]fastmail[.-][Ff]astmail$" +default_scratchpad = "fastmail" + +[[window_rule]] +match.app_id = "^Beeper$" +default_scratchpad = "beeper" + +[[window_rule]] +match.app_id = "^brave-home[.]google[.]com__" +default_scratchpad = "home" + +[[window_rule]] +match.app_id = "^ai[.]elementlabs[.]lmstudio$" +default_scratchpad = "lmstudio" + +# ============================================================================== +# Layer rules + +[[layer_rule]] +match.namespace = "^noctalia-(bar-[^\"]+|notification|dock|panel|attached-panel|osd|desktop-widget-[^\"]*)$" +blur = true +blur_ignore_alpha = 0.5 +blur_popups = true