From f4b5663ddfe7ee0f80d41dd1b21e06bcfa5d61ae Mon Sep 17 00:00:00 2001 From: Prad Nukala Date: Wed, 14 Oct 2026 13:32:30 -0400 Subject: [PATCH] Import from root chezmoi source --- autostart.lua | 2 + bindings.lua | 331 ++++++++++++++++++++++++++++++++++++++++++++++++ dot_luarc.json | 11 ++ hyprland.lua | 97 ++++++++++++++ hyprsunset.conf | 14 ++ input.lua | 132 +++++++++++++++++++ looknfeel.lua | 143 +++++++++++++++++++++ monitors.lua | 19 +++ xdph.conf | 4 + 9 files changed, 753 insertions(+) create mode 100644 autostart.lua create mode 100644 bindings.lua create mode 100644 dot_luarc.json create mode 100644 hyprland.lua create mode 100644 hyprsunset.conf create mode 100644 input.lua create mode 100644 looknfeel.lua create mode 100644 monitors.lua create mode 100644 xdph.conf diff --git a/autostart.lua b/autostart.lua new file mode 100644 index 0000000..de1a69a --- /dev/null +++ b/autostart.lua @@ -0,0 +1,2 @@ +-- Extra autostart processes. +-- o.launch_on_start("my-service") diff --git a/bindings.lua b/bindings.lua new file mode 100644 index 0000000..0228cc3 --- /dev/null +++ b/bindings.lua @@ -0,0 +1,331 @@ +-- ============================================================================ +-- Personal overrides, ported from the niri config. +-- Append to ~/.config/hypr/hyprland.lua, after require("default.hypr.omarchy"). +-- Every rebind of an Omarchy default is unbound first. The comment after each +-- unbind says what the key did before. +-- ============================================================================ + +-- ─── Window management ────────────────────────────────────────────────────── + +-- Spawn Terminal +-- hl.unbind("SUPER + RETURN") -- was: toggle window transparency + +-- Mod+Q -> close. Omarchy already binds this, so it is only made explicit. +hl.unbind("SUPER + Q") +o.bind("SUPER + Q", "Close window", hl.dsp.window.close()) + +-- Mod+W was tabbed display. Here it flips the workspace layout, and sets every +-- column to 50% of the screen (niri's default-column-width) when the +-- workspace turns to scrolling. Dwindle needs no widths, because it re-tiles +-- on its own. Needs ~/.local/bin/omarchy-toggle-layout. +hl.unbind("SUPER + W") -- was: close window +o.bind("SUPER + W", "Toggle workspace layout", "omarchy-toggle-layout 0.5") + +-- Mod+BackSpace -> last window. +hl.unbind("SUPER + BACKSPACE") -- was: toggle window transparency +o.bind("SUPER + BACKSPACE", "Focus last window", hl.dsp.focus({ last = true })) + +-- Mod+Tab -> switch windows inside the focused workspace. Omarchy parks the +-- workspace hop here, but SUPER + CTRL + J/K already does that, so the key is +-- free. SUPER + CTRL + TAB (former workspace) keeps its default job. +-- +-- cyclenext is deliberately not used: it walks into a hidden special +-- workspace and drags the scratchpad into view. This ring is built from the +-- windows of the active workspace alone, and a workspace lives on exactly one +-- monitor, so focus never leaves the display. Grouped windows stay in the +-- ring (focusing a hidden member just flips the group to that tab); anything +-- else hidden is skipped. stable_id fixes the order, so repeated presses walk +-- the same ring instead of following focus history. +local function cycle_in_workspace(step) + return function() + local active = hl.get_active_window() + local workspace = active and active.workspace or hl.get_active_workspace() + if not workspace then + return + end + + local ring = {} + for _, window in ipairs(hl.get_workspace_windows(workspace.id)) do + if window.mapped and (window.group or not window.hidden) then + ring[#ring + 1] = window + end + end + if #ring < 2 then + return + end + table.sort(ring, function(a, b) + return a.stable_id < b.stable_id + end) + + local current = 1 + for index, window in ipairs(ring) do + if active and window.address == active.address then + current = index + break + end + end + + -- Raise after the focus, so a floating window comes up on top instead of + -- staying buried, the way Omarchy's ALT + TAB does. + hl.dispatch(hl.dsp.focus({ window = ring[(current - 1 + step) % #ring + 1] })) + hl.dispatch(hl.dsp.window.bring_to_top()) + end +end + +hl.unbind("SUPER + TAB") -- was: next workspace +hl.unbind("SUPER + SHIFT + TAB") -- was: previous workspace +o.bind("SUPER + TAB", "Focus on next window in workspace", cycle_in_workspace(1)) +o.bind("SUPER + SHIFT + TAB", "Focus on previous window in workspace", cycle_in_workspace(-1)) + +-- Tabs. Hyprland groups are the nearest thing to a niri tabbed column. +-- SUPER + G (group) and SUPER + ALT + TAB (next in group) are Omarchy defaults. + +-- ─── Focus and swap: hjkl ─────────────────────────────────────────────────── + +-- Focus steps through the windows of a group first, and leaves the group only +-- at the ends. This is the focus half of the smart-move behavior below, and +-- Hyprland does it on its own, so no script is needed. +hl.config({ binds = { movefocus_cycles_groupfirst = true } }) + +hl.unbind("SUPER + J") -- was: toggle window split +hl.unbind("SUPER + K") -- was: keybindings menu +hl.unbind("SUPER + L") -- was: toggle workspace layout + +o.bind("SUPER + H", "Focus on left window", hl.dsp.focus({ direction = "l" })) +o.bind("SUPER + J", "Focus on below window", hl.dsp.focus({ direction = "d" })) +o.bind("SUPER + K", "Focus on above window", hl.dsp.focus({ direction = "u" })) +o.bind("SUPER + L", "Focus on right window", hl.dsp.focus({ direction = "r" })) + +-- The old SUPER + J job, moved to a free key. Dwindle only; toggling needs +-- dwindle.preserve_split, which Omarchy already sets. +o.bind("SUPER + D", "Toggle window split", hl.dsp.layout("togglesplit")) + +-- Smart move: merge into the neighboring group if there is one, else swap. +-- A floating window is parked in a monitor corner instead, clockwise from +-- the bottom left: H bottom-left, J top-left, K top-right, L bottom-right. +-- Needs ~/.local/bin/omarchy-smart-move. +o.bind("SUPER + SHIFT + H", "Move or merge left / float to bottom-left", "omarchy-smart-move l") +o.bind("SUPER + SHIFT + J", "Move or merge down / float to top-left", "omarchy-smart-move d") +o.bind("SUPER + SHIFT + K", "Move or merge up / float to top-right", "omarchy-smart-move u") +o.bind("SUPER + SHIFT + L", "Move or merge right / float to bottom-right", "omarchy-smart-move r") + +-- Picture-in-picture for any window: float + pin, 30% of the focused +-- monitor on both axes, bottom-left corner; press again to tile it back. +-- Needs ~/.local/bin/omarchy-pip (omarchy-summon shares it). SUPER + SHIFT +-- + T is free in Omarchy; SUPER + T (toggle floating) stays as is. +o.bind("SUPER + SHIFT + T", "Toggle picture-in-picture", "omarchy-pip") + +-- ─── Workspaces and monitors ──────────────────────────────────────────────── + +hl.unbind("SUPER + CTRL + K") -- was: Herdr keybindings +o.bind("SUPER + CTRL + J", "Next workspace", hl.dsp.focus({ workspace = "e+1" })) +o.bind("SUPER + CTRL + K", "Previous workspace", hl.dsp.focus({ workspace = "e-1" })) + +-- Scratch workspaces. Omarchy's SUPER + S is taken by the region screenshot +-- below, so the main toggle (Tern) lands on SUPER + CTRL + S, with +-- SUPER + A (free in Omarchy) as the home-row alias. Claude Desktop has its +-- own scratch workspace on SUPER + CTRL + ALT + L. SUPER + ALT + S (send +-- window to the scratchpad) is still the Omarchy default and needs no line. +-- +-- All of them go through ~/.local/bin/omarchy-toggle-scratch rather than the +-- raw special-workspace toggle: it opens each workspace on its home monitor +-- (the raw toggle uses the focused one), and on reveal it starts whichever +-- resident is missing instead of sliding an empty workspace into view. The +-- residents are pinned to their workspaces by window rules in +-- hypr/hyprland.lua. Fastmail and Beeper have their own scratch workspaces +-- under SUPER + CTRL + ALT + E / M in the Applications block below. +hl.unbind("SUPER + CTRL + S") -- was: share menu +hl.unbind("SUPER + SHIFT + A") -- was: ChatGPT web app + +o.bind("SUPER + A", "Toggle scratch workspace", "omarchy-toggle-scratch scratchpad") +o.bind("SUPER + CTRL + ALT + H", "Toggle Tern scratch workspace", "omarchy-toggle-scratch scratchpad") +o.bind("SUPER + CTRL + ALT + L", "Toggle Claude scratch workspace", "omarchy-toggle-scratch claude") + +-- Workspaces 1 to 4 (Studio Display) on Q W E R and 5 to 8 (laptop panel) on +-- Y U I O: ALT to focus, ALT + SHIFT to send the window there. The pinning +-- lives in hypr/hyprland.lua, so focusing a workspace also moves to its +-- monitor. No Omarchy default uses ALT with a letter, so nothing is unbound. +for index, key in ipairs({ "Q", "W", "E", "R", "Y", "U", "I", "O" }) do + local workspace = tostring(index) + + o.bind("ALT + " .. key, "Switch to workspace " .. workspace, hl.dsp.focus({ workspace = workspace })) + o.bind("ALT + SHIFT + " .. key, "Move window to workspace " .. workspace, hl.dsp.window.move({ workspace = workspace })) +end + +hl.unbind("SUPER + O") -- was: pop window out +hl.unbind("SUPER + SHIFT + O") -- was: Obsidian (moved to SUPER + CTRL + ALT + O) +o.bind("SUPER + O", "Focus next monitor", hl.dsp.focus({ monitor = "+1" })) +o.bind("SUPER + SHIFT + O", "Move window to next monitor", hl.dsp.window.move({ monitor = "+1" })) + +o.bind("ALT + SHIFT + J", "Move window to next monitor", hl.dsp.window.move({ monitor = "+1" })) +o.bind("ALT + SHIFT + K", "Move window to previous monitor", hl.dsp.window.move({ monitor = "-1" })) + +-- Named workspaces, in place of niri-dynamic-workspaces. +o.bind("SUPER + SHIFT + CTRL + ALT + W", "Workspace w", hl.dsp.focus({ workspace = "name:w" })) +o.bind("SUPER + SHIFT + CTRL + ALT + H", "Workspace h", hl.dsp.focus({ workspace = "name:h" })) +o.bind("SUPER + CTRL + ALT + X", "Workspace x", hl.dsp.focus({ workspace = "name:x" })) + +-- ─── Sizing ───────────────────────────────────────────────────────────────── + +-- Mod+R / Mod+Shift+R. Needs ~/.local/bin/omarchy-cycle-window-size. +o.bind("SUPER + R", "Cycle window size", "omarchy-cycle-window-size") +o.bind("SUPER + SHIFT + R", "Cycle window size back", "omarchy-cycle-window-size prev") + +-- Hyprland resizes by pixels, not by percent, so 100 px replaces the niri +-- 10%. ALT + SHIFT + H / L step the width (ALT + SHIFT + J / K move the +-- window between monitors, above). +o.bind("ALT + SHIFT + H", "Shrink window width", hl.dsp.window.resize({ x = -100, y = 0, relative = true })) +o.bind("ALT + SHIFT + L", "Grow window width", hl.dsp.window.resize({ x = 100, y = 0, relative = true })) + +hl.unbind("SUPER + CTRL + H") -- was: toggle window gaps (now Google Home; SUPER + comma shrinks width) +hl.unbind("SUPER + CTRL + L") -- was: toggle window gaps (now LM Studio; SUPER + period grows width) +hl.unbind("SUPER + CTRL + K") -- was: toggle window gaps +hl.unbind("SUPER + CTRL + J") -- was: toggle window gaps + +o.bind("SUPER + CTRL + K", "Shrink window height", hl.dsp.window.resize({ x = 0, y = -100, relative = true })) +o.bind("SUPER + CTRL + J", "Grow window height", hl.dsp.window.resize({ x = 0, y = 100, relative = true })) + +-- Same width steps on SUPER + , / SUPER + . (the < and > keys). Key names are +-- the lower-case xkb keysyms; "COMMA"/"PERIOD" would not match. +hl.unbind("SUPER + comma") -- was: dismiss last notification (now SUPER + SHIFT + BACKSPACE) +o.bind("SUPER + comma", "Shrink window width", hl.dsp.window.resize({ x = -100, y = 0, relative = true })) +o.bind("SUPER + period", "Grow window width", hl.dsp.window.resize({ x = 100, y = 0, relative = true })) + +-- The notification dismiss SUPER + comma gave up. SUPER + SHIFT + comma still +-- dismisses all. +hl.unbind("SUPER + SHIFT + BACKSPACE") -- was: toggle window gaps +o.bind("SUPER + SHIFT + BACKSPACE", "Dismiss last notification", "omarchy-shell notifications dismissOne") + +-- Column fitting. These are scrolling layout messages, so they do nothing on +-- a dwindle workspace. Mod+F (expand to available width) has no exact match; +-- the Omarchy default SUPER + ALT + F (full width) is the closest. +o.bind("SUPER + ALT + C", "Fit active column", hl.dsp.layout("fit active")) +o.bind("SUPER + SHIFT + ALT + C", "Fit visible columns", hl.dsp.layout("fit visible")) + +-- Super+C (niri center-column). Floating windows go through centerwindow. +-- On a scrolling workspace the column is re-focused with focus_fit_method +-- briefly set to 0 (center), then restored, so plain focus moves keep +-- niri's center-focused-column "never". A relative "move" can't do this: the +-- tape stops at its first column, so the leftmost window never reaches the +-- middle. Dwindle and master have nothing to scroll; the key does nothing. +local function center_window() + local window = hl.get_active_window() + if not window then + return + end + if window.floating then + hl.dispatch(hl.dsp.window.center()) + return + end + if not window.workspace or window.workspace.tiled_layout ~= "scrolling" then + return + end + local fit_method = hl.get_config("scrolling.focus_fit_method") + hl.config({ scrolling = { focus_fit_method = 0 } }) + hl.dispatch(hl.dsp.focus({ window = window })) + hl.config({ scrolling = { focus_fit_method = fit_method } }) +end + +hl.unbind("SUPER + C") -- was: universal copy +o.bind("SUPER + C", "Center window / column", center_window) + +-- ─── Applications ─────────────────────────────────────────────────────────── +-- Same modifier block as the niri config. Each one focuses an open window +-- before it launches a new one. The launch command uses the same desktop +-- entry the vicinae URLs named, so no binary names are guessed. + +hl.unbind("SUPER + CTRL + A") -- was: audio panel + +o.bind("SUPER + CTRL + ALT + J", "Terminals", "omarchy-cycle-terminal") +o.bind("SUPER + CTRL + ALT + K", "Browsers", "omarchy-cycle-browser") +o.bind("SUPER + CTRL + ALT + G", "Ghostty", "omarchy-cycle ghostty") +o.bind("SUPER + CTRL + ALT + O", "Obsidian", "omarchy-cycle obsidian") +o.bind("SUPER + CTRL + ALT + F", "File manager", "omarchy-launch-or-focus nautilus \"gtk-launch org.gnome.Nautilus\"") +-- Fastmail, Beeper, Google Home and LM Studio each live on their own scratch +-- workspace (window rules in hypr/hyprland.lua); the toggle reveals it on the +-- focused monitor and starts the app only when no window of it exists -- the +-- web-app launcher would otherwise open a second window every time. +o.bind("SUPER + CTRL + ALT + E", "Fastmail", "omarchy-toggle-scratch fastmail") +o.bind("SUPER + CTRL + ALT + M", "Beeper", "omarchy-toggle-scratch beeper") +o.bind("SUPER + CTRL + H", "Google Home", "omarchy-toggle-scratch home") +o.bind("SUPER + CTRL + L", "LM Studio", "omarchy-toggle-scratch lmstudio") +-- YouTube is a floating pinned window in the bottom-left corner (window rule +-- in hypr/hyprland.lua). The summon launches it when no window exists, +-- otherwise carries the window to the focused monitor and focuses it. +o.bind("SUPER + CTRL + ALT + Y", "YouTube", "omarchy-summon youtube") -- was: web apps cycle +hl.unbind("SUPER + SHIFT + Y") -- was: YouTube via the desktop entry (new window every press) +o.bind("SUPER + SHIFT + Y", "YouTube", "omarchy-summon youtube") + +hl.unbind("SUPER + CTRL + ALT + T") -- was: show time +-- Pattern is the exact class from `hyprctl clients`; the window rule in +-- hypr/hyprland.lua sends it to workspace 5 on eDP-1 fullscreen. +o.bind("SUPER + CTRL + ALT + T", "tldraw offline", "omarchy-launch-or-focus tldraw-offline \"gtk-launch tldraw-offline\"") + +hl.unbind("SUPER + CTRL + ALT + W") -- was: toggle weather +-- gtk-launch takes the desktop-entry id: TLDraw.desktop, not tldraw_offline, +-- which does not exist (the old command could only focus, never launch). + +hl.unbind("SUPER + SHIFT + M") -- was: Spotify +hl.unbind("SUPER + SHIFT + G") -- was: Signal +-- The ssh/mosh command and tmux keymaps live in ~/.config/kitty/goliath.conf +-- (kitty cannot take --config from a `launch` action, hence a separate kitty +-- process, same trick as SUPER + T / SUPER + A). kitty-goliath is the app-id +-- looknfeel.lua's terminal blur rule expects. +o.bind("SUPER + SHIFT + G", "Goliath", { launch = "kitty --class=kitty-goliath --title=goliath --config ~/.config/kitty/goliath.conf" }) + +-- ─── Screenshots ──────────────────────────────────────────────────────────── + +hl.unbind("SUPER + S") -- was: toggle scratchpad (now SUPER + CTRL + S) +hl.unbind("SUPER + SHIFT + S") -- was: Google Maps +o.bind("SUPER + S", "Screenshot region", "omarchy-capture-screenshot region") +o.bind("SUPER + SHIFT + S", "Screenshot window", "omarchy-capture-screenshot windows") +-- Monitor capture moved off SUPER + CTRL + S when the scratchpad took that key. +o.bind("SUPER + SHIFT + ALT + S", "Screenshot monitor", "omarchy-capture-screenshot fullscreen") + +-- ─── Session ──────────────────────────────────────────────────────────────── + +hl.unbind("SUPER + SHIFT + E") -- was: Email (Fastmail is on SUPER + CTRL + ALT + E) +o.bind("SUPER + SHIFT + E", "System menu", "omarchy-menu toggle system") +o.bind("SUPER + SHIFT + Q", "System menu", "omarchy-menu toggle system") + +-- ─── Dictation ────────────────────────────────────────────────────────────── + +o.bind("SUPER + F24", "Dictate to Hermes", "voxtype record toggle --profile hermes") + +-- ─── Menus, in place of vicinae ───────────────────────────────────────────── +-- Same keys as the niri config, pointed at the Omarchy menus. + +-- Mod+Space (vicinae toggle) needs no line: SUPER + SPACE is already the +-- Omarchy menu. SUPER + ALT + SPACE is the apps menu. + +hl.unbind("SUPER + SHIFT + SPACE") -- was: toggle top bar +hl.unbind("SUPER + P") -- was: pseudo window +hl.unbind("SUPER + SPACE") -- was: pseudo window + +-- Omarchy's SUPER + SPACE is ALT + SPACE, so the key is free in Omarchy. +-- o.bind("ALT + SPACE", "Toggle Launcher", "omarchy-menu toggle launcher") + +-- Mod+Shift+Return was the vicinae window switcher. ScrollOverview on +-- ALT + TAB below takes that job, so SUPER + SHIFT + RETURN keeps its default +-- job of opening the browser. +-- +-- flea --default: begin. Written by `flea --default`; `flea --default off` removes the block whole. +hl.unbind("SUPER + SHIFT + F") +o.bind("SUPER + SHIFT + F", "File manager", { launch = 'flea --gui' }) +hl.unbind("SUPER + ALT + SHIFT + F") +o.bind("SUPER + ALT + SHIFT + F", "File manager (cwd)", { launch = 'flea --gui "$(omarchy-cmd-terminal-cwd)"' }) +-- flea --default: end. + +-- flea --picker: begin. Written by `flea --picker`; `flea --picker off` removes the block whole. +o.window("com.thisisgm.flea.picker", { tag = "+floating-window" }) +-- flea --picker: end. + +-- niri-style: SUPER + F maximizes (Omarchy's SUPER + ALT + F "Full width"), +-- SUPER + SHIFT + F goes true fullscreen. Kept below the flea blocks so it +-- wins over the file-manager bind `flea --default` writes onto +-- SUPER + SHIFT + F; flea stays on SUPER + ALT + SHIFT + F, Nautilus on +-- SUPER + CTRL + ALT + F. +hl.unbind("SUPER + F") -- was: full screen +o.bind("SUPER + F", "Maximize", hl.dsp.window.fullscreen({ mode = "maximized" })) +hl.unbind("SUPER + SHIFT + F") -- was: file manager (flea --gui) +o.bind("SUPER + SHIFT + F", "Full screen", hl.dsp.window.fullscreen({ mode = "fullscreen" })) diff --git a/dot_luarc.json b/dot_luarc.json new file mode 100644 index 0000000..afb4466 --- /dev/null +++ b/dot_luarc.json @@ -0,0 +1,11 @@ +{ + "workspace": { + "library": [ + "/usr/share/hypr/stubs" + ], + "checkThirdParty": false + }, + "diagnostics": { + "globals": ["hl", "o"] + } +} diff --git a/hyprland.lua b/hyprland.lua new file mode 100644 index 0000000..8051648 --- /dev/null +++ b/hyprland.lua @@ -0,0 +1,97 @@ +-- Learn how to configure Hyprland: https://wiki.hypr.land/Configuring/Start/ + +-- Omarchy's bootstrap keeps path setup out of this user config. +dofile((os.getenv("OMARCHY_PATH") or "/usr/share/omarchy") .. "/default/hypr/bootstrap.lua") + +-- Disable all Omarchy default bindings. Add your own in hypr/bindings.lua. +-- omarchy_default_bindings = false +-- +-- Or disable only bindings for Omarchy's preinstalled apps/web apps while +-- keeping core window-manager bindings: +-- omarchy_preinstalled_bindings = false + +-- Load Omarchy defaults. +require("default.hypr.omarchy") + +-- Put your personal overrides in these files. They're loaded after Omarchy's +-- defaults so package updates can improve the defaults without rewriting your +-- ~/.config/hypr files. +require("hypr.monitors") +require("hypr.input") +require("hypr.bindings") +require("hypr.looknfeel") +require("hypr.autostart") + +-- Toggle config flags dynamically. +require("default.hypr.toggles") + +-- Add any other personal Hyprland configuration below. +-- o.window("qemu", { workspace = "5" }) + +-- Pin workspaces to monitors: 1-4 on the Studio Display, 5-8 on the laptop +-- panel; 1 and 5 are what each shows on connect. Persistent so empty ones +-- stay on their monitor (the bar's workspace dots list them per monitor). +-- Matched on EDID description for the external display so it survives +-- landing on a different DP connector after re-docking. +for id = 1, 8 do + hl.workspace_rule({ + workspace = tostring(id), + monitor = id <= 4 and "desc:Apple Computer Inc StudioDisplay 0x984262A7" or "eDP-1", + default = id == 1 or id == 5, + persistent = true, + }) +end + +-- Always open these apps on a fixed workspace and follow them there. Classes +-- come from `hyprctl clients` -- the desktop files' StartupWMClass hints +-- disagree with reality for Beeper (claims BeeperTexts, actually reports +-- Beeper). Add " silent" to a workspace prop to place a window without +-- switching the view to it. +o.window([[^brave-www\.tldraw\.com__-Default$]], { workspace = "3" }) + +-- tldraw-offline gets workspace 5 to itself on the laptop panel, fullscreen. +o.window([[^tldraw-offline$]], { workspace = "5", fullscreen = true }) + +-- Scratch workspaces, toggled by ~/.local/bin/omarchy-toggle-scratch, which +-- also starts any resident that is missing and picks the monitor. Pinning +-- the residents here means they land on their workspace no matter who starts +-- them; "silent" leaves the reveal to the script. Under the scrolling layout +-- each resident gets its own column, in the order they appear. +-- +-- SUPER + A / SUPER + CTRL + S: Tern; SUPER + SHIFT + A: Claude Desktop. +-- Each alone on its own workspace, Studio Display. Tern has no per-window +-- class flag, so every Tern window lands on the scratchpad. +o.window([[^so\.stencil\.tern$]], { workspace = "special:scratchpad silent" }) +o.window([[^com\.anthropic\.Claude$]], { workspace = "special:claude silent" }) +-- SUPER + CTRL + ALT + E / M: Fastmail and Beeper, each alone on the +-- focused monitor. +o.window([[^com\.fastmail\.Fastmail$]], { workspace = "special:fastmail silent" }) +o.window([[^Beeper$]], { workspace = "special:beeper silent" }) +-- SUPER + CTRL + H: Google Home (Brave web app), on the focused monitor. +o.window([[^brave-home\.google\.com__.*]], { workspace = "special:home silent" }) +-- SUPER + CTRL + L: LM Studio, on the focused monitor. +o.window([[^ai\.elementlabs\.lmstudio$]], { workspace = "special:lmstudio silent" }) + +-- SUPER + CTRL + ALT + Y / SUPER + SHIFT + Y: the YouTube web app, summoned +-- by ~/.local/bin/omarchy-summon. Not a scratch workspace: a floating, +-- pinned window parked in the bottom-left corner at 30% of the monitor on +-- both axes, inset by general:gaps_out -- the same fit ~/.local/bin/omarchy-pip +-- applies (SUPER + SHIFT + T, and the summon after a monitor move); keep the +-- fraction and inset in sync. Chromium derives the class from the app URL +-- and the host spelling has varied (youtube.com vs www.youtube.com) once a +-- video plays, so match the family. Its picture-in-picture overlay shares +-- the class; the "negative:" title match keeps it out so Omarchy's pip rule +-- (float + pin, top right) still applies. Rule regexes are RE2 full matches: +-- no lookahead, and a pattern must cover the whole string. +o.window( + { class = [[brave-(www\.)?youtube\.com__.*]], title = [[negative:.*Picture.?in.?[Pp]icture.*]] }, + { + float = true, + pin = true, + size = { "(monitor_w*0.3)", "(monitor_h*0.3)" }, + -- Spelled from monitor_h, not window_h: move is evaluated against the + -- size Chromium asked for, not the one the size rule imposes, and that + -- put the window top-left. + move = { "10", "(monitor_h*0.7-10)" }, + } +) diff --git a/hyprsunset.conf b/hyprsunset.conf new file mode 100644 index 0000000..a3ba208 --- /dev/null +++ b/hyprsunset.conf @@ -0,0 +1,14 @@ +# Makes hyprsunset do nothing to the screen by default +# Without this, the default applies some tint to the monitor +profile { + time = 07:00 + identity = true +} + +# To enable auto switch to nightlight, add to your .config/hypr/autostart.lua: +# o.launch_on_start("hyprsunset") +# and use the following: +# profile { +# time = 20:00 +# temperature = 4000 +# } diff --git a/input.lua b/input.lua new file mode 100644 index 0000000..e7a17dc --- /dev/null +++ b/input.lua @@ -0,0 +1,132 @@ +-- Keep only your personal input overrides here. Uncommented settings below +-- replace Omarchy's defaults. + +-- Key repeat: Omarchy's 250ms delay kept, rate raised 40 -> 50Hz. 200 / 50 was +-- tried and felt too fast; the "typing lag" it was meant to fix turned out to be the +-- Pilot W-Corne's firmware (55d4:0461, Vial/QMK over a 2.4GHz dongle) +-- resolving its MT(LGUI,SPC) / MT(LCTL,SPC) thumb keys as holds the moment +-- another key went down. Fixed on the board itself: Vial QMK Settings -> +-- Tap-Hold -> "Ignore Mod Tap Interrupt" (qsid 8 bit 1), tapping term 150ms. +-- +-- The rest of the keystroke path was checked and is already optimal: the +-- dongle advertises bInterval=1 (1000Hz polling), udev pins power/control=on +-- (no USB autosuspend), nothing polls its raw-HID interface, WiFi sits on +-- 5GHz, and render:new_render_scheduling is off (no triple-buffer frame). +-- Remaining latency is the 60Hz Studio Display refresh and the fcitx5 +-- input-method hop (see omarchy-fcitx5.service), neither an input option. +hl.config({ + input = { + repeat_delay = 250, + repeat_rate = 50, + + -- niri has no focus-follows-mouse: hovering gives a window pointer focus + -- (scroll goes there) but keyboard focus moves only on click. Also keeps + -- the scrolling tape from jumping when the cursor brushes a neighbour. + follow_mouse = 2, + + -- niri: touchpad { natural-scroll; tap; }. Tap-to-click is libinput's + -- default already. + touchpad = { + natural_scroll = true, + }, + }, +}) + +-- niri's built-in touchpad gestures: three fingers sideways scroll the tape, +-- up/down switch workspace. +hl.gesture({ fingers = 3, direction = "horizontal", action = "scroll_move" }) +hl.gesture({ fingers = 3, direction = "vertical", action = "workspace" }) + +-- Keyboard layout and options. +-- See https://wiki.hypr.land/Configuring/Basics/Variables/#input +-- hl.config({ +-- input = { +-- -- Use multiple keyboard layouts and switch between them with Left Alt + Right Alt. +-- kb_layout = "us,dk,eu", +-- kb_options = "compose:caps,shift:both_capslock_cancel,grp:alts_toggle", +-- +-- -- Use a specific keyboard variant if needed (e.g. intl for international keyboards). +-- kb_variant = "intl", +-- +-- -- Change speed of keyboard repeat. +-- repeat_rate = 40, +-- repeat_delay = 250, +-- +-- -- Start with numlock on by default. +-- numlock_by_default = true, +-- +-- -- Increase sensitivity for mouse/trackpad (default: 0). +-- sensitivity = 0.35, +-- +-- -- Turn off mouse acceleration (default: adaptive). +-- accel_profile = "flat", +-- +-- touchpad = { +-- -- Use natural (inverse) scrolling. +-- natural_scroll = true, +-- +-- -- Use two-finger clicks for right-click instead of lower-right corner. +-- clickfinger_behavior = true, +-- +-- -- Control the speed of your scrolling. +-- scroll_factor = 0.4, +-- +-- -- Enable the touchpad while typing. +-- disable_while_typing = false, +-- +-- -- Left-click-and-drag with three fingers. +-- drag_3fg = 1, +-- }, +-- }, +-- }) + +-- App-specific touchpad scroll speeds. +-- o.window("(Alacritty|kitty|foot)", { scroll_touchpad = 1.5 }) +-- o.window("com.mitchellh.ghostty", { scroll_touchpad = 0.2 }) + +-- Touchpad gestures live in the override block at the top of this file. + +-- Tablets / pens. +-- Hyprland maps a tablet across the *whole* monitor layout by default +-- (input.tablet.output is empty), so a pen stretches over eDP-1 + the external +-- display at once. Pin each tablet to a single output instead. +-- Device names come from `hyprctl devices` (the "Tablet" entry, not "Tablet Pad"). + +-- Built-in digitizer of the Flow Z13: it physically *is* the laptop panel. +-- Pen and finger are separate devices; the finger one is the "Touch" entry. +hl.device({ name = "elan9008:00-04f3:43c7-stylus", output = "eDP-1" }) +-- Unbound, Hyprland maps a touch onto whichever monitor is *focused* +-- (onTouchDown falls back to the focused monitor when the device has no +-- output), so a tap on the panel landed on the Studio Display. Bound, the tap +-- focuses eDP-1 and then the window under the finger. +hl.device({ name = "elan9008:00-04f3:43c7", output = "eDP-1" }) +-- +-- Palm rejection while writing is libinput's job, not Hyprland's: with the +-- pen in proximity it drops touches on the paired touchscreen (same +-- LIBINPUT_DEVICE_GROUP) -- *all* of them only if the pen reports no tilt; +-- with tilt it drops just a 200x250mm rectangle whose side is guessed from +-- the tilt sign, and this pen reports ABS_TILT_X/Y, so a resting palm kept +-- landing as taps and scrolls. /etc/libinput/local-overrides.quirks (not +-- chezmoi-managed; the Flow Z13 stylus section) strips the tilt axes, which +-- flips arbitration to ignore-all. Cost: no tilt in drawing apps. The 90ms +-- re-enable after the pen leaves proximity is hard-coded in libinput. + +-- Wacom Intuos BT S: pinned to the Studio Display, matched on EDID description +-- so it survives a different DP connector after re-docking. With that display +-- absent, Hyprland maps across the whole layout, which undocked is just eDP-1. +-- +-- The tablet is 152x95mm (16:10) and the display is 16:9, so the active area is +-- cropped to a 152x85.5mm (16:9) band to keep strokes undistorted while still +-- reaching every pixel. The band starts at the top edge, leaving the bottom +-- 9.5mm of the tablet as dead space (the pen there parks at the screen bottom). +hl.device({ + name = "wacom-intuos-bt-s-pen", + output = "desc:Apple Computer Inc StudioDisplay 0x984262A7", + active_area_size = { 152, 85.5 }, + active_area_position = { 0, 0 }, +}) + +-- Left-handed use rotates the tablet 180 degrees: left_handed = true + +-- The 4 ExpressKeys ("Tablet Pad") cannot be bound from Hyprland; only apps +-- that implement the tablet-v2 pad protocol (e.g. Krita) see them. diff --git a/looknfeel.lua b/looknfeel.lua new file mode 100644 index 0000000..229dfe9 --- /dev/null +++ b/looknfeel.lua @@ -0,0 +1,143 @@ +-- Change the default Omarchy look'n'feel. +-- +-- Load order is Omarchy defaults -> current theme's hyprland.lua -> this file +-- (see $OMARCHY_PATH/default/hypr/omarchy.lua), so whatever is set here is the +-- last writer and wins over the theme. That is the point of this file: a theme +-- picks colours, the compositor's feel stays ours. + +-- Flat by default, glass for terminals only. +-- +-- Stock quattro is rounding 0, blur off, shadow off +-- ($OMARCHY_PATH/default/hypr/looknfeel.lua); themes that ship a hyprland.lua +-- turn all three on for every window (Cyberdream and Spectra both did), which +-- on this box is a bad trade: blur over an eDP panel plus a 5K Studio Display +-- is real frame time on an iGPU sharing system memory, and Chromium/Electron +-- windows cannot paint a transparent background, so fading them just shows +-- wallpaper where page background should be. +-- +-- Blur has to be enabled globally to exist at all -- there is no positive +-- `blur` window rule, only `no_blur`, and kitty's own `background_blur` (the +-- ext-background-effect-v1 protocol, which Hyprland 0.56 does implement) is a +-- no-op while decoration:blur:enabled is false. So: switch it on here, then +-- opt every window out again except the terminals below. +hl.config({ + general = { + -- Scrolling (niri-style tape) instead of Omarchy's dwindle. Per-workspace + -- layouts saved by omarchy-hyprland-workspace-layout-toggle (SUPER + W; + -- ~/.local/state/omarchy/workspace-layouts/*.lua) still win for the + -- workspaces they name. + layout = "scrolling", + }, + + decoration = { + rounding = 0, + + -- Backdrop behind every scratch (special) workspace: the desktop under it + -- is darkened by this fraction. + dim_special = 0.33, + + blur = { + enabled = true, + -- Cheaper than the themes' size 6 / 3 passes; with only terminals + -- blurred this is a handful of quads per frame, not the whole screen. + size = 5, + passes = 2, + brightness = 0.8, + contrast = 1.1, + vibrancy = 0.15, + noise = 0.01, + new_optimizations = true, + popups = false, + }, + + shadow = { + enabled = false, + }, + }, + + group = { + groupbar = { + -- Square tab plates, to match rounding = 0 above. + gradient_rounding = 0, + }, + }, +}) + +-- Omarchy tags every mapped window `default-opacity` and fades it to +-- 0.985 / 0.96 ($OMARCHY_PATH/default/hypr/windows.lua); that stock fade is +-- kept. Rules are last-match-wins, so this only opts every window out of the +-- blur enabled above -- the few percent of wallpaper that shows through an +-- inactive window is the same as stock, where blur is off entirely. +o.window({ tag = "default-opacity" }, { no_blur = true }) + +-- The exception: terminals carry their own alpha (kitty's +-- `background_opacity`, 0.9 in ~/.config/kitty/kitty.conf), so they are the one +-- surface where blurring the backdrop buys anything. Full class list because +-- the kitty hosts run under their own app-ids (kitty-herdr, kitty-tmux, +-- kitty-goliath) and Omarchy tags nothing as `terminal`. +o.window( + "^(kitty(-.*)?|Alacritty|com\\.mitchellh\\.ghostty|foot|org\\.codeberg\\.dnkl\\.foot|wezterm)$", + { no_blur = false } +) + +-- Themes are also free to re-enable the workspace slide (Spectra sets +-- `workspaces` with its "flow" bezier); Omarchy's default is off. Restate it. +-- workspacesIn/workspacesOut are not overridden anywhere, so they inherit this. +hl.animation({ leaf = "workspaces", enabled = false }) + +-- Slow, constant rotation of the active-border gradient (the theme's +-- hyprland_active_border; Cyberdream's is accent -> cyan). `loop` restarts +-- the angle each turn so it never eases or stops; speed is deciseconds per +-- revolution, so 100 = one lap every 10 s. Costs a redraw of the border each +-- frame while a window is mapped. +hl.animation({ leaf = "borderangle", enabled = true, speed = 100, bezier = "linear", style = "loop" }) + +-- https://wiki.hypr.land/Configuring/Basics/Variables/#general +-- hl.config({ +-- general = { +-- -- No gaps between windows or borders. +-- gaps_in = 0, +-- gaps_out = 0, +-- border_size = 0, +-- +-- -- Change to niri-like side-scrolling layout. +-- layout = "scrolling", +-- }, +-- }) + +-- https://wiki.hypr.land/Configuring/Basics/Variables/#animations +-- hl.config({ +-- animations = { +-- -- Disable all animations. +-- enabled = false, +-- }, +-- }) + +-- https://wiki.hypr.land/Configuring/Basics/Variables/#layout +-- hl.config({ +-- layout = { +-- -- Avoid overly wide single-window layouts on wide screens. +-- single_window_aspect_ratio = { 1, 1 }, +-- }, +-- }) + +-- Scrolling layout, matched to niri's layout block +-- (~/.config/niri/config.d/20-layout-and-overview.kdl). Wins over Omarchy's +-- column_width = 0.49. +hl.config({ + scrolling = { + -- default-column-width { proportion 0.5; } + column_width = 0.5, + -- preset-column-widths; SUPER + R / SUPER + SHIFT + R cycle these + -- (colresize +conf / -conf via omarchy-cycle-window-size). + explicit_column_widths = "0.333, 0.5, 0.667", + -- center-focused-column "never": scroll only as far as needed to fit the + -- focused column. SUPER + C centers on demand. + focus_fit_method = 1, + -- niri keeps a lone column at its own width instead of filling the screen. + fullscreen_on_one_column = false, + -- niri stops at the first/last column instead of wrapping around. + wrap_focus = false, + wrap_swapcol = false, + }, +}) diff --git a/monitors.lua b/monitors.lua new file mode 100644 index 0000000..24f1cfe --- /dev/null +++ b/monitors.lua @@ -0,0 +1,19 @@ +-- See https://wiki.hypr.land/Configuring/Basics/Monitors/ +-- List current monitors and supported resolutions with: hyprctl monitors all + +local omarchy_gdk_scale = 3 +local omarchy_monitor_scale = 3.2 + +hl.env("GDK_SCALE", tostring(omarchy_gdk_scale)) +hl.monitor({ output = "", mode = "preferred", position = "auto", scale = omarchy_monitor_scale }) + +-- Laptop panel (eDP-1) on the left, Apple Studio Display on the right. +-- Logical widths at scale 2: 2560/2 = 1280, so DP-4 starts at x=1280. +hl.monitor({ output = "eDP-1", mode = "2560x1600@180", position = "0x0", scale = 2 }) +hl.monitor({ output = "DP-4", mode = "5120x2880@60", position = "1280x0", scale = 2 }) + +-- Configure a specific monitor. +-- hl.monitor({ output = "DP-2", mode = "2560x1440@144", position = "0x0", scale = 1 }) + +-- Portrait/rotated secondary monitor (transform: 1 = 90°, 3 = 270°). +-- hl.monitor({ output = "DP-2", mode = "preferred", position = "auto", scale = 1, transform = 1 }) diff --git a/xdph.conf b/xdph.conf new file mode 100644 index 0000000..63b66be --- /dev/null +++ b/xdph.conf @@ -0,0 +1,4 @@ +screencopy { + allow_token_by_default = true + custom_picker_binary = hyprland-preview-share-picker +}