diff --git a/dotfiles/config/taffybar/Justfile b/dotfiles/config/taffybar/Justfile new file mode 100644 index 00000000..c8b43479 --- /dev/null +++ b/dotfiles/config/taffybar/Justfile @@ -0,0 +1,17 @@ +set shell := ["bash", "-euo", "pipefail", "-c"] + +# Run taffybar in the foreground (blocks the terminal). +run: + @scripts/taffybar-run + +# Restart taffybar in the background using cabal run. +restart: + @scripts/taffybar-restart + +# Capture the reserved top area (taffybar) on the focused monitor. +screenshot: + @scripts/taffybar-screenshot + +# Capture the reserved top area on all monitors (one file per monitor). +screenshot-all: + @scripts/taffybar-screenshot-all diff --git a/dotfiles/config/taffybar/palette.css b/dotfiles/config/taffybar/palette.css new file mode 100644 index 00000000..8480584b --- /dev/null +++ b/dotfiles/config/taffybar/palette.css @@ -0,0 +1,17 @@ +@define-color accent #f1b2b2; +@define-color bar-background rgba(12, 15, 24, 0.55); +@define-color bar-gradient-start rgba(20, 26, 40, 0.68); +@define-color bar-gradient-end rgba(12, 18, 30, 0.68); +@define-color bar-border rgba(255, 255, 255, 0.06); +@define-color menu-background-color #1f202a; +@define-color menu-font-color #e7e4ee; +@define-color menu-highlight #3a3550; +@define-color font-color #e7e4ee; +@define-color font-muted #b8b1c6; +@define-color pill-background rgba(48, 52, 69, 0.92); +@define-color pill-border rgba(92, 95, 120, 0.85); +@define-color pill-highlight rgba(255, 255, 255, 0.10); +@define-color pill-shadow rgba(0, 0, 0, 0.28); +@define-color transparent rgba(0.0, 0.0, 0.0, 0.0); +@define-color white #ffffff; +@define-color black #000000; diff --git a/dotfiles/config/taffybar/scripts/taffybar-restart b/dotfiles/config/taffybar/scripts/taffybar-restart new file mode 100755 index 00000000..f2bce305 --- /dev/null +++ b/dotfiles/config/taffybar/scripts/taffybar-restart @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -euo pipefail + +root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +pkill -u "$USER" -x taffybar || true + +cd "$root" +setsid -f direnv exec . cabal run >/tmp/taffybar.log 2>&1 + +echo "Started taffybar in the background. Logs: /tmp/taffybar.log" diff --git a/dotfiles/config/taffybar/scripts/taffybar-run b/dotfiles/config/taffybar/scripts/taffybar-run new file mode 100755 index 00000000..832862c8 --- /dev/null +++ b/dotfiles/config/taffybar/scripts/taffybar-run @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +set -euo pipefail + +root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +cd "$root" + +exec direnv exec . cabal run diff --git a/dotfiles/config/taffybar/scripts/taffybar-screenshot b/dotfiles/config/taffybar/scripts/taffybar-screenshot new file mode 100755 index 00000000..51dd22bc --- /dev/null +++ b/dotfiles/config/taffybar/scripts/taffybar-screenshot @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -euo pipefail + +monitors="$(hyprctl monitors -j)" +height="$(jq -r '.[] | select(.focused) | .reserved[1]' <<<"$monitors")" +geo="$(jq -r '.[] | select(.focused) | "\(.x),\(.y) \(.width)x\(.reserved[1])"' <<<"$monitors")" + +if [[ -z "$geo" || "$height" == "0" ]]; then + echo "No top reserved area found on focused monitor." >&2 + echo "Is taffybar reserving space?" >&2 + exit 1 +fi + +out="/tmp/taffybar-$(date +%Y%m%d-%H%M%S).png" +grim -g "$geo" "$out" +echo "$out" diff --git a/dotfiles/config/taffybar/scripts/taffybar-screenshot-all b/dotfiles/config/taffybar/scripts/taffybar-screenshot-all new file mode 100755 index 00000000..16449fae --- /dev/null +++ b/dotfiles/config/taffybar/scripts/taffybar-screenshot-all @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +set -euo pipefail + +ts="$(date +%Y%m%d-%H%M%S)" +found="0" + +while IFS=$'\t' read -r name geo; do + found="1" + out="/tmp/taffybar-${name}-${ts}.png" + grim -g "$geo" "$out" + echo "$out" +done < <(hyprctl monitors -j | jq -r '.[] | select(.reserved[1] > 0) | "\(.name)\t\(.x),\(.y) \(.width)x\(.reserved[1])"') + +if [[ "$found" == "0" ]]; then + echo "No monitors with a top reserved area found." >&2 + exit 1 +fi diff --git a/dotfiles/config/taffybar/stack.yaml b/dotfiles/config/taffybar/stack.yaml deleted file mode 100644 index 91bd2fa9..00000000 --- a/dotfiles/config/taffybar/stack.yaml +++ /dev/null @@ -1,53 +0,0 @@ -flags: {} -extra-package-dbs: [] -packages: -- . -- location: ./taffybar - extra-dep: true -- location: ../xmonad/xmonad-contrib - extra-dep: true -- location: ../xmonad/xmonad - extra-dep: true -# - location: ../../../../Projects/status-notifier-item -# extra-dep: true -# - location: ../../../../Projects/gtk-sni-tray -# extra-dep: true -extra-deps: -- ConfigFile-1.1.4 -- broadcast-chan-0.2.0.2 -- dbus-hslogger-0.1.0.1 -- gi-cairo-connector-0.0.1 -- gi-cairo-render-0.0.1 -- gi-dbusmenu-0.4.6 -- gi-dbusmenugtk3-0.4.7 -- gi-gdkx11-3.0.8 -- gi-xlib-2.0.7 -- gtk-sni-tray-0.1.6.0 -- gtk-strut-0.1.3.0 -- haskell-gi-0.22.6 -- rate-limit-1.4.1 -- status-notifier-item-0.3.0.3 -- time-units-1.0.0 -- xml-helpers-1.0.0 -resolver: nightly-2019-06-19 -allow-newer: true -nix: - packages: - - cairo - - gcc - - gnome2.pango - - gobjectIntrospection - - gtk3 - - hicolor-icon-theme - - libdbusmenu-glib - - libdbusmenu-gtk3 - - libxml2 - - numix-icon-theme-circle - - pkgconfig - - x11 - - xorg.libX11 - - xorg.libXext - - xorg.libXinerama - - xorg.libXrandr - - xorg.libXrender - - zlib diff --git a/dotfiles/config/taffybar/taffybar.css b/dotfiles/config/taffybar/taffybar.css index 28e59992..7eefee26 100644 --- a/dotfiles/config/taffybar/taffybar.css +++ b/dotfiles/config/taffybar/taffybar.css @@ -1,40 +1,46 @@ -@define-color magenta #888ca6; -@define-color active-window-color @white; -@define-color urgent-window-color @taffy-blue; -@define-color font-color @white; -@define-color menu-background-color @white; -@define-color menu-font-color @black; -@define-color bar-background rgba(0, 0, 0, .6); -@define-color accent @red; -@define-color menu-highlight @magenta; - -@define-color transparent rgba(0.0, 0.0, 0.0, 0.0); -@define-color white #FFFFFF; -@define-color black #000000; - /* Top level styling */ +.taffy-window { + background-color: @bar-background; + background-image: linear-gradient(to bottom, @bar-gradient-start, @bar-gradient-end); + border-bottom: 1px solid @bar-border; + box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45); +} + .taffy-window * { - font-family: "Noto Sans", sans-serif; - font-size: 10pt; - font-weight: bold; + font-family: "Iosevka Aile", "Noto Sans", sans-serif; + font-size: 9pt; + font-weight: 600; color: @font-color; background-color: @transparent; - text-shadow: 1px 1px @font-shadow-color; + text-shadow: none; } .taffy-box { - border-color: @white; - border-style: solid; - background-color: @bar-background; - padding: 2px; - margin: 1px; - border-radius: 4px; + border-width: 0px; + background-color: @transparent; + padding: 0px; + margin: 0px; + border-radius: 0px; + box-shadow: none; +} + +.outer-pad { + background-color: @pill-background; + border: 1px solid @pill-border; + border-radius: 6px; + margin: 4px 6px; + box-shadow: 0 1px 0 @pill-highlight, 0 6px 14px @pill-shadow; +} + +.inner-pad { + padding: 1px 8px; + border-radius: 5px; } .contents { - padding: 2px; - transition: background-color .5s; + padding: 0px; + transition: background-color .2s; opacity: 1; } @@ -47,20 +53,21 @@ .workspaces .contents { box-shadow: none; - border-radius: 4px; - border-width: 1px; + border-radius: 5px; + border-width: 0px; border-style: solid; border-color: @transparent; + padding: 0px 4px; } .workspace-label { - padding-right: 4px; + padding-right: 6px; padding-left: 2px; padding-top: 0px; - font-size: 10pt; - opacity: 1; - font-weight: bold; - transition: color .5s; + font-size: 9pt; + opacity: 0.95; + font-weight: 600; + transition: color .2s; } .contents .window-icon { @@ -72,7 +79,8 @@ } .active .contents { - background-color: rgba(0, 0, 0, 0.3); + background-color: rgba(255, 255, 255, 0.10); + border-radius: 999px; opacity: 1; } @@ -82,32 +90,35 @@ .active .overlay-box { padding: 0px; - /* background-color: rgba(0, 0, 0, 1.0); */ - border-color: @white; - border-width: 3px; + border-color: @transparent; + border-width: 0px; opacity: 1; } .visible .contents { - background-color: rgba(255.0, 255.0, 255.0, 0.15); + background-color: rgba(255, 255, 255, 0.06); } .window-icon-container { - transition: opacity .5s, box-shadow .5s; + transition: opacity .2s, box-shadow .2s; opacity: 1; border-radius: 5px; - transition: background-color 1s; + transition: background-color .2s; + background-color: rgba(255, 255, 255, 0.04); + padding: 1px 4px; + border: 1px solid rgba(255, 255, 255, 0.08); } /* This gives space for the box-shadow (they look like underlines) that follow. This will actually affect all widgets, (not just the workspace icons), but that is what we want since we want the icons to look the same. */ .auto-size-image, .sni-tray { - padding: 1px; + padding: 1px 4px; } .window-icon-container.active { - background-color: rgba(255.0, 255.0, 255.0, 0.4); + background-color: rgba(255, 255, 255, 0.13); + border-color: rgba(255, 255, 255, 0.16); } .window-icon-container.urgent { @@ -132,7 +143,8 @@ button { } button:checked, button:hover .Contents:hover { - box-shadow: inset 0 -3px @accent; + box-shadow: inset 0 -2px @accent; + background-color: rgba(255, 255, 255, 0.06); } /* Menu styling */ @@ -155,3 +167,13 @@ button:checked, button:hover .Contents:hover { .taffy-window menuitem:hover > label, menuitem:hover > label { color: @white; } + +.clock label, +.mpris label, +.battery-text label { + letter-spacing: 0.2px; +} + +.mpris label { + color: @font-muted; +} diff --git a/dotfiles/config/taffybar/taffybar.hs b/dotfiles/config/taffybar/taffybar.hs index 8f34d902..c467f8d3 100644 --- a/dotfiles/config/taffybar/taffybar.hs +++ b/dotfiles/config/taffybar/taffybar.hs @@ -167,6 +167,13 @@ iconNameVariants raw = in [dotted, dashed, dashedDotted, underscored, underscoredDotted, name] in nub $ concatMap variantsFor baseNames +-- Hyprland "special" workspaces (e.g. "special:slack") are scratchpad-like and +-- usually not something we want visible in the workspace widget. +isSpecialHyprWorkspace :: Hyprland.HyprlandWorkspace -> Bool +isSpecialHyprWorkspace ws = + let name = Data.Text.toLower $ Data.Text.pack $ Hyprland.workspaceName ws + in Data.Text.isPrefixOf "special" name || Hyprland.workspaceIdx ws < 0 + hyprlandIconCandidates :: Hyprland.HyprlandWindow -> [Data.Text.Text] hyprlandIconCandidates windowData = let baseNames = map Data.Text.pack $ catMaybes @@ -224,11 +231,11 @@ hyprlandFallbackIcon size _ = fallbackIconPixbuf size cssFilesByHostname = - [ ("uber-loaner", ["uber-loaner.css"]) - , ("imalison-home", ["taffybar.css"]) - , ("ivanm-dfinity-razer", ["taffybar.css"]) - , ("ryzen-shine", ["taffybar.css"]) - , ("stevie-nixos", ["taffybar.css"]) + [ ("uber-loaner", ["palette.css", "uber-loaner.css"]) + , ("imalison-home", ["palette.css", "taffybar.css"]) + , ("ivanm-dfinity-razer", ["palette.css", "taffybar.css"]) + , ("ryzen-shine", ["palette.css", "taffybar.css"]) + , ("stevie-nixos", ["palette.css", "taffybar.css"]) ] main = do @@ -236,7 +243,7 @@ main = do hostName <- getHostName backend <- detectBackend - let relativeFiles = fromMaybe ["taffybar.css"] $ lookup hostName cssFilesByHostname + let relativeFiles = fromMaybe ["palette.css", "taffybar.css"] $ lookup hostName cssFilesByHostname cssFiles <- mapM (getUserConfigFile "taffybar") relativeFiles let myCPU = deocrateWithSetClassAndBoxes "cpu" $ @@ -252,23 +259,27 @@ main = do myWorkspaces = flip widgetSetClassGI "workspaces" =<< X11Workspaces.workspacesNew X11Workspaces.defaultWorkspacesConfig - { minIcons = 1 - , getWindowIconPixbuf = + { X11Workspaces.minIcons = 1 + , X11Workspaces.getWindowIconPixbuf = X11Workspaces.scaledWindowIconPixbufGetter $ X11Workspaces.getWindowIconPixbufFromChrome <|||> X11Workspaces.unscaledDefaultGetWindowIconPixbuf <|||> (\size _ -> fallbackIconPixbuf size) - , widgetGap = 0 - , showWorkspaceFn = X11Workspaces.hideEmpty - , updateRateLimitMicroseconds = 100000 - , labelSetter = workspaceNamesLabelSetter - , widgetBuilder = X11Workspaces.buildLabelOverlayController + , X11Workspaces.widgetGap = 0 + , X11Workspaces.showWorkspaceFn = X11Workspaces.hideEmpty + , X11Workspaces.updateRateLimitMicroseconds = 100000 + , X11Workspaces.labelSetter = workspaceNamesLabelSetter + , X11Workspaces.widgetBuilder = X11Workspaces.buildLabelOverlayController } myHyprWorkspaces = flip widgetSetClassGI "workspaces" =<< Hyprland.hyprlandWorkspacesNew Hyprland.defaultHyprlandWorkspacesConfig { Hyprland.widgetGap = 0 , Hyprland.minIcons = 1 + -- Don't show Hyprland "special:*" workspaces. + , Hyprland.showWorkspaceFn = + (\ws -> Hyprland.workspaceState ws /= X11Workspaces.Empty && + not (isSpecialHyprWorkspace ws)) , Hyprland.getWindowIconPixbuf = hyprlandManualIconGetter <|||> Hyprland.defaultHyprlandGetWindowIconPixbuf <|||> @@ -280,10 +291,11 @@ main = do { clockUpdateStrategy = RoundedTargetInterval 60 0.0 , clockFormatString = "%a %b %_d, 🕑%I:%M %p" } - myTray = deocrateWithSetClassAndBoxes "tray" $ - sniTrayNewFromParams defaultTrayParams { trayLeftClickAction = PopupMenu - , trayRightClickAction = Activate - } + -- Disabled for now: StatusNotifierWatcher errors under Hyprland. + -- myTray = deocrateWithSetClassAndBoxes "tray" $ + -- sniTrayNewFromParams defaultTrayParams { trayLeftClickAction = PopupMenu + -- , trayRightClickAction = Activate + -- } myMpris = mpris2NewWithConfig MPRIS2Config { mprisWidgetWrapper = deocrateWithSetClassAndBoxes "mpris" . return , updatePlayerWidget = @@ -296,8 +308,7 @@ main = do deocrateWithSetClassAndBoxes "battery-text" $ textBatteryNew "$percentage$%" batteryWidgets = [myBatteryIcon, myBatteryText] baseEndWidgets = - [ myTray - , myMpris + [ myMpris ] fullEndWidgets = baseEndWidgets ++ [ myCPU, myMem, myNet, myMpris ] laptopEndWidgets = batteryWidgets ++ baseEndWidgets @@ -307,8 +318,8 @@ main = do , endWidgets = baseEndWidgets , barPosition = Top , widgetSpacing = 0 - , barPadding = 0 - , barHeight = ScreenRatio $ 1/27 + , barPadding = 4 + , barHeight = ScreenRatio $ 1/36 , cssPaths = cssFiles , centerWidgets = [ myClock ] } @@ -318,8 +329,8 @@ main = do , endWidgets = baseEndWidgets , barPosition = Top , widgetSpacing = 0 - , barPadding = 0 - , barHeight = ScreenRatio $ 1/27 + , barPadding = 4 + , barHeight = ScreenRatio $ 1/36 , cssPaths = cssFiles , centerWidgets = [ myClock ] } @@ -342,7 +353,7 @@ main = do , baseConfigX11 { endWidgets = laptopEndWidgets } ) , ( "nixquick" - , baseConfigX11 { endWidgets = [ myTray , myMpris ] } + , baseConfigX11 { endWidgets = [ myMpris ] } ) ] waylandOverrides = @@ -362,7 +373,7 @@ main = do , baseConfigWayland { endWidgets = laptopEndWidgets } ) , ( "nixquick" - , baseConfigWayland { endWidgets = [ myTray , myMpris ] } + , baseConfigWayland { endWidgets = [ myMpris ] } ) ] selectedConfig = @@ -377,7 +388,6 @@ main = do -- , startWidgets = [] } startTaffybar $ - appendHook (void $ getTrayHost False) $ withLogServer $ withToggleServer $ toTaffybarConfig simpleTaffyConfig