diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..ebd202e Binary files /dev/null and b/.DS_Store differ diff --git a/.serena/.gitignore b/.serena/.gitignore new file mode 100644 index 0000000..2e510af --- /dev/null +++ b/.serena/.gitignore @@ -0,0 +1,2 @@ +/cache +/project.local.yml diff --git a/.serena/project.yml b/.serena/project.yml new file mode 100644 index 0000000..60e913e --- /dev/null +++ b/.serena/project.yml @@ -0,0 +1,132 @@ +# the name by which the project can be referenced within Serena +project_name: "dotfiles" + + +# list of languages for which language servers are started; choose from: +# al angular ansible bash clojure +# cpp cpp_ccls crystal csharp csharp_omnisharp +# dart elixir elm erlang fortran +# fsharp go groovy haskell haxe +# hlsl html java json julia +# kotlin lean4 lua luau markdown +# matlab msl nix ocaml pascal +# perl php php_phpactor powershell python +# python_jedi python_ty r rego ruby +# ruby_solargraph rust scala scss solidity +# swift systemverilog terraform toml typescript +# typescript_vts vue yaml zig +# (This list may be outdated. For the current list, see values of Language enum here: +# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py +# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.) +# Note: +# - For C, use cpp +# - For JavaScript, use typescript +# - For Angular projects, use angular (subsumes typescript+html; requires `npm install` in the project root) +# - For SCSS / Sass / plain CSS, use scss (some-sass-language-server handles all three) +# - For Free Pascal/Lazarus, use pascal +# Special requirements: +# Some languages require additional setup/installations. +# See here for details: https://oraios.github.io/serena/01-about/020_programming-languages.html#language-servers +# When using multiple languages, the first language server that supports a given file will be used for that file. +# The first language is the default language and the respective language server will be used as a fallback. +# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored. +languages: +- lua + +# the encoding used by text files in the project +# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings +encoding: "utf-8" + +# line ending convention to use when writing source files. +# Possible values: unset (use global setting), "lf", "crlf", or "native" (platform default) +# This does not affect Serena's own files (e.g. memories and configuration files), which always use native line endings. +line_ending: + +# The language backend to use for this project. +# If not set, the global setting from serena_config.yml is used. +# Valid values: LSP, JetBrains +# Note: the backend is fixed at startup. If a project with a different backend +# is activated post-init, an error will be returned. +language_backend: + +# whether to use project's .gitignore files to ignore files +ignore_all_files_in_gitignore: true + +# advanced configuration option allowing to configure language server-specific options. +# Maps the language key to the options. +# Have a look at the docstring of the constructors of the LS implementations within solidlsp (e.g., for C# or PHP) to see which options are available. +# No documentation on options means no options are available. +ls_specific_settings: {} + +# list of additional paths to ignore in this project. +# Same syntax as gitignore, so you can use * and **. +# Note: global ignored_paths from serena_config.yml are also applied additively. +ignored_paths: [] + +# whether the project is in read-only mode +# If set to true, all editing tools will be disabled and attempts to use them will result in an error +# Added on 2025-04-18 +read_only: false + +# list of tool names to exclude. +# This extends the existing exclusions (e.g. from the global configuration) +# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html +excluded_tools: [] + +# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default). +# This extends the existing inclusions (e.g. from the global configuration). +# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html +included_optional_tools: [] + +# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools. +# This cannot be combined with non-empty excluded_tools or included_optional_tools. +# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html +fixed_tools: [] + +# list of mode names that are to be activated by default, overriding the setting in the global configuration. +# The full set of modes to be activated is base_modes (from global config) + default_modes + added_modes. +# If the setting is undefined/empty, the default_modes from the global configuration (serena_config.yml) apply. +# Otherwise, this overrides the setting from the global configuration (serena_config.yml). +# Therefore, you can set this to [] if you do not want the default modes defined in the global config to apply +# for this project. +# This setting can, in turn, be overridden by CLI parameters (--mode). +# See https://oraios.github.io/serena/02-usage/050_configuration.html#modes +default_modes: + +# list of mode names to be activated additionally for this project, e.g. ["query-projects"] +# The full set of modes to be activated is base_modes (from global config) + default_modes + added_modes. +# See https://oraios.github.io/serena/02-usage/050_configuration.html#modes +added_modes: + +# initial prompt for the project. It will always be given to the LLM upon activating the project +# (contrary to the memories, which are loaded on demand). +initial_prompt: "" + +# time budget (seconds) per tool call for the retrieval of additional symbol information +# such as docstrings or parameter information. +# This overrides the corresponding setting in the global configuration; see the documentation there. +# If null or missing, use the setting from the global configuration. +symbol_info_budget: + +# list of regex patterns which, when matched, mark a memory entry as read‑only. +# Extends the list from the global configuration, merging the two lists. +read_only_memory_patterns: [] + +# list of regex patterns for memories to completely ignore. +# Matching memories will not appear in list_memories or activate_project output +# and cannot be accessed via read_memory or write_memory. +# To access ignored memory files, use the read_file tool on the raw file path. +# Extends the list from the global configuration, merging the two lists. +# Example: ["_archive/.*", "_episodes/.*"] +ignored_memory_patterns: [] + +# list of additional workspace folder paths for cross-package reference support (e.g. in monorepos). +# Paths can be absolute or relative to the project root. +# Each folder is registered as an LSP workspace folder, enabling language servers to discover +# symbols and references across package boundaries. +# Currently supported for: TypeScript. +# Example: +# additional_workspace_folders: +# - ../sibling-package +# - ../shared-lib +additional_workspace_folders: [] diff --git a/AGENT.md b/AGENT.md new file mode 100644 index 0000000..49ee78d --- /dev/null +++ b/AGENT.md @@ -0,0 +1,87 @@ +# AGENT.md + +## Про что этот репозиторий + +`dotfiles` - личный репозиторий конфигураций для двух разных зон: + +- `nvim/` - конфиг Neovim для основной development-среды. +- `fedora/` - текущий основной профиль рабочего стола Fedora с Niri. +- `bazzite/` - старый профиль Bazzite/Niri, который теперь нужен в основном как + legacy reference для миграции, сравнения и поддержки старых машин. + +`CLAUDE.md` в этом репозитории покрывает в основном Neovim. Для desktop-задач +он не является полной картой проекта, поэтому сначала ориентируйся по +`README.md`, а затем по `fedora/README.md` или `bazzite/README.md`. + +## Верхнеуровневая структура + +- `README.md` - общий вход в репозиторий и краткая карта текущей структуры. +- `CLAUDE.md` - узкий guide по `nvim/`. +- `nvim/` - Neovim config: + - `init.lua` - точка входа; + - `lua/config/` - базовые options, keymaps, autocmds; + - `lua/plugins/` - конфиги плагинов; + - `lazy-lock.json` - зафиксированные версии плагинов. +- `fedora/` - основной desktop profile: + - `README.md` - каноническое описание профиля; + - `niri/config.kdl` - compositor/session behavior; + - `waybar/config.jsonc` и `waybar/style.css` - панель и стили; + - `khal-calendar/` и `mail-counter/` - Rust-модули для Waybar. +- `bazzite/` - legacy profile со структурой, близкой к `fedora/`. + +Скрытые каталоги вроде `.git`, `.claude`, `.serena` считай tooling/infrastructure +metadata, а не рабочей частью конфигурации. + +## Что читать для контекста + +Всегда начинай с `README.md`. + +Дальше маршрут такой: + +- Если задача про Neovim: + 1. `CLAUDE.md` + 2. `nvim/init.lua` + 3. нужные файлы из `nvim/lua/config/` или `nvim/lua/plugins/` + +- Если задача про текущую Fedora-машину, Niri, Waybar, wallpaper, Steam, + Bottles, FlClash или desktop behavior: + 1. `fedora/README.md` + 2. нужный целевой конфиг, обычно один из: + - `fedora/niri/config.kdl` + - `fedora/waybar/config.jsonc` + - `fedora/waybar/style.css` + - `fedora/fuzzel/fuzzel.ini` + - `fedora/xsettingsd/xsettingsd.conf` + +- Если задача про старую Bazzite-конфигурацию или миграцию между Bazzite и + Fedora: + 1. `bazzite/README.md` + 2. затем сравнивай с соответствующим файлом в `fedora/` + +- Если задача про кастомные Waybar-модули: + 1. `fedora/khal-calendar/README.md` или `bazzite/khal-calendar/README.md` + 2. `fedora/khal-calendar/Cargo.toml` или `bazzite/khal-calendar/Cargo.toml` + 3. `fedora/mail-counter/README.md` или `bazzite/mail-counter/README.md` + 4. `fedora/mail-counter/Cargo.toml` или `bazzite/mail-counter/Cargo.toml` + +## Repo-specific правила + +- По умолчанию считай `fedora/` текущим и основным направлением. +- Не переноси автоматически старые предположения из `bazzite/` в `fedora/`. +- Если пользователь говорит просто "на федоре", "в desktop-конфиге" или + "в niri", сначала смотри `fedora/`. +- Если пользователь просит исторический контекст, сравнение или миграцию, + используй `bazzite/` как reference, но не как source of truth для новых + изменений. +- Не считай `CLAUDE.md` полной документацией по всему репозиторию: он уже, чем + реальная структура проекта. + +## Как работать безопасно + +- Для desktop-задач не ограничивайся общими рассуждениями: сначала читай + subtree README, потом точечный конфиг. +- Не дублируй содержимое `fedora/README.md` и `bazzite/README.md` в новых + документах без необходимости; эти файлы уже являются каноническими описаниями + профилей. +- Если в рабочем дереве есть несвязанные изменения, не откатывай их и не + расширяй задачу за пределы нужного subtree без явного запроса. diff --git a/bazzite/.DS_Store b/bazzite/.DS_Store new file mode 100644 index 0000000..39ff9b8 Binary files /dev/null and b/bazzite/.DS_Store differ diff --git a/bazzite/README.md b/bazzite/README.md new file mode 100644 index 0000000..36739a7 --- /dev/null +++ b/bazzite/README.md @@ -0,0 +1,113 @@ +# Конфиги рабочего стола Bazzite + +Эта папка содержит конфигурацию рабочего стола Bazzite для Wayland-сессии на +Niri. Идея в том, чтобы подключать эти файлы в `~/.config` на Bazzite-машине и +держать живую конфигурацию рабочего стола синхронизированной с этим +репозиторием. + +## Что здесь настроено + +| Путь | Назначение | +|---|---| +| `niri/config.kdl` | Конфиг Niri: ввод, монитор, раскладка окон, автозапуск, правила окон, хоткеи. | +| `waybar/` | Верхняя панель: конфиг, CSS в стиле Catppuccin и custom-модули. | +| `waybar/scripts/gpu.sh` | Модуль Waybar для загрузки и температуры NVIDIA GPU через `nvidia-smi`. | +| `waybar/scripts/weather.sh` | Модуль Waybar с погодой через `wttr.in`. | +| `khal-calendar/` | Rust-версия календарного модуля Waybar. | +| `mail-counter/` | Rust-модуль Waybar для счетчика непрочитанных писем Thunderbird. | +| `fuzzel/fuzzel.ini` | Тема лаунчера Fuzzel на цветах Catppuccin Latte. | +| `btop/btop.conf` | Настройки системного монитора btop. | +| `glow/glow.yml` | Настройки терминального Markdown-viewer Glow. | +| `gtk-3.0/`, `gtk-4.0/` | GTK-настройки и цветовые файлы Breeze/Catppuccin. | +| `xsettingsd/xsettingsd.conf` | Настройки GTK, курсора, иконок, шрифта и DPI для X11/XWayland-приложений. | +| `environment.d/intel-gtk-fix.conf` | Переменная окружения сессии: `GSK_RENDERER=gl`. | +| `autostart/steam.desktop` | Автозапуск Steam через Bazzite Steam wrapper. | +| `wallpapers/image.png` | Текущая картинка рабочего стола для `swaybg`. | + +## Как это работает + +Niri запускает `swaybg` и `waybar` из `niri/config.kdl`. `swaybg` рисует +wallpaper, Waybar поднимает: + +- встроенные Niri-модули для рабочих столов, заголовка окна и раскладки + клавиатуры; +- системные модули для звука, сети, CPU, памяти, температуры, диска, privacy, + power profile и tray; +- custom-модули для почты Thunderbird, погоды, NVIDIA GPU и календаря из + `khal`. + +Календарный модуль печатает JSON для Waybar и показывает Pango-tooltip с текущим +месяцем, подсвеченными днями с событиями и ближайшими событиями из `khal`. +Активная реализация сейчас - скомпилированный Rust-бинарник: + +```jsonc +"exec": "~/.config/waybar/scripts/khal-calendar" +``` + +## Ожидаемые программы + +Для полной работы конфигурации на Bazzite ожидаются: + +- `niri` +- `waybar` +- `fuzzel` +- `btop` +- `glow` +- `xsettingsd` +- `alacritty` +- `swaybg` +- `khal`, `ikhal`, `vdirsyncer` +- `nvidia-smi` +- `curl` +- `cargo` для пересборки `khal-calendar` и `mail-counter` + +## Установка + +Подключить директории в `~/.config`: + +```bash +for dir in niri fuzzel waybar btop glow gtk-3.0 gtk-4.0 xsettingsd environment.d autostart; do + ln -sfn ~/dotfiles/bazzite/$dir ~/.config/$dir +done +mkdir -p ~/Pictures/Wallpapers +ln -sfn ~/dotfiles/bazzite/wallpapers/image.png ~/Pictures/Wallpapers/image.png +``` + +Если репозиторий лежит не в `~/dotfiles`, путь нужно поправить. + +## Сборка календарного модуля + +Собрать и установить Rust-бинарник календаря на Bazzite: + +```bash +cd ~/dotfiles/bazzite/khal-calendar +cargo build --release +install -m 755 target/release/khal-calendar ~/.config/waybar/scripts/khal-calendar +``` + +Исходники остаются в `khal-calendar/`; установленный бинарник лежит в +`~/.config/waybar/scripts/`. + +## Сборка почтового модуля + +Собрать и установить Rust-бинарник счетчика писем на Bazzite: + +```bash +cd ~/dotfiles/bazzite/mail-counter +cargo build --release +install -m 755 target/release/mail-counter ~/.config/waybar/scripts/mail +``` + +Модуль читает `global-messages-db.sqlite` из профиля Thunderbird и печатает JSON +для `custom/mail` в Waybar. + +## Перезагрузка + +После изменения конфига или CSS Waybar: + +```bash +pkill -SIGUSR2 waybar +``` + +Если Waybar не подхватил изменения, нужно перезапустить Waybar вручную или +перезапустить Niri-сессию. diff --git a/bazzite/khal-calendar/.gitignore b/bazzite/khal-calendar/.gitignore new file mode 100644 index 0000000..b83d222 --- /dev/null +++ b/bazzite/khal-calendar/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/bazzite/khal-calendar/Cargo.lock b/bazzite/khal-calendar/Cargo.lock new file mode 100644 index 0000000..b530709 --- /dev/null +++ b/bazzite/khal-calendar/Cargo.lock @@ -0,0 +1,383 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "cc" +version = "1.2.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" +dependencies = [ + "cfg-if", + "futures-util", + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "khal-calendar" +version = "0.1.0" +dependencies = [ + "chrono", + "serde_json", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "wasm-bindgen" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/bazzite/khal-calendar/Cargo.toml b/bazzite/khal-calendar/Cargo.toml new file mode 100644 index 0000000..9799e34 --- /dev/null +++ b/bazzite/khal-calendar/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "khal-calendar" +version = "0.1.0" +edition = "2024" + +[dependencies] +chrono = "0.4" +serde_json = "1" + +[lints.rust] +unsafe_code = "warn" + +[lints.clippy] +all = "warn" +pedantic = "warn" diff --git a/bazzite/khal-calendar/README.md b/bazzite/khal-calendar/README.md new file mode 100644 index 0000000..9e8de6d --- /dev/null +++ b/bazzite/khal-calendar/README.md @@ -0,0 +1,18 @@ +# khal-calendar + +Waybar custom calendar module that prints JSON with the current time and a Pango +tooltip. Event days and upcoming events are read from `khal`. + +## Build On Bazzite + +```bash +cd ~/dotfiles/bazzite/khal-calendar +cargo build --release +install -m 755 target/release/khal-calendar ~/.config/waybar/scripts/khal-calendar +``` + +Waybar config should call the compiled binary: + +```jsonc +"exec": "~/.config/waybar/scripts/khal-calendar" +``` diff --git a/bazzite/khal-calendar/src/main.rs b/bazzite/khal-calendar/src/main.rs new file mode 100644 index 0000000..72d74d6 --- /dev/null +++ b/bazzite/khal-calendar/src/main.rs @@ -0,0 +1,160 @@ +use std::{ + collections::BTreeSet, + process::{Command, Stdio}, + thread::sleep, + time::{Duration, Instant}, +}; + +use chrono::{Datelike, Local, NaiveDate, Timelike}; +use serde_json::json; + +const COMMAND_TIMEOUT: Duration = Duration::from_secs(5); +const MONTH_NAMES: [&str; 13] = [ + "", + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", +]; + +fn run_khal(args: &[&str]) -> Option { + let mut child = Command::new("khal") + .args(args) + .stdout(Stdio::piped()) + .stderr(Stdio::null()) + .spawn() + .ok()?; + + let deadline = Instant::now() + COMMAND_TIMEOUT; + loop { + match child.try_wait() { + Ok(Some(_status)) => { + let output = child.wait_with_output().ok()?; + return Some(String::from_utf8_lossy(&output.stdout).trim().to_owned()); + } + Ok(None) if Instant::now() >= deadline => { + let _ = child.kill(); + let _ = child.wait(); + return None; + } + Ok(None) => sleep(Duration::from_millis(25)), + Err(_) => return None, + } + } +} + +fn days_in_month(year: i32, month: u32) -> u32 { + let (next_year, next_month) = if month == 12 { + (year + 1, 1) + } else { + (year, month + 1) + }; + + NaiveDate::from_ymd_opt(next_year, next_month, 1) + .and_then(|date| date.pred_opt()) + .map_or(31, |date| date.day()) +} + +fn get_event_days(year: i32, month: u32) -> BTreeSet { + let Some(first) = NaiveDate::from_ymd_opt(year, month, 1) else { + return BTreeSet::new(); + }; + let Some(last) = NaiveDate::from_ymd_opt(year, month, days_in_month(year, month)) else { + return BTreeSet::new(); + }; + + let start = first.format("%d.%m.%Y").to_string(); + let end = last.format("%d.%m.%Y").to_string(); + let Some(stdout) = run_khal(&[ + "list", + &start, + &end, + "--format", + "{start-date}", + "--day-format", + "", + ]) else { + return BTreeSet::new(); + }; + + stdout + .lines() + .filter_map(|line| NaiveDate::parse_from_str(line.trim(), "%d.%m.%Y").ok()) + .filter(|date| date.month() == month) + .map(|date| date.day()) + .collect() +} + +fn build_calendar(year: i32, month: u32, event_days: &BTreeSet, today: NaiveDate) -> String { + let Some(first) = NaiveDate::from_ymd_opt(year, month, 1) else { + return String::new(); + }; + + let month_name = MONTH_NAMES + .get(month as usize) + .copied() + .unwrap_or("Unknown"); + let mut lines = vec![ + format!(r##"{month_name} {year}"##), + r##"Mo Tu We Th Fr Sa Su"##.to_owned(), + ]; + + let mut week = vec![" ".to_owned(); (first.weekday().number_from_monday() - 1) as usize]; + for day in 1..=days_in_month(year, month) { + let day_string = if day == today.day() && month == today.month() && year == today.year() { + format!(r##"{day:2}"##) + } else if event_days.contains(&day) { + format!(r##"{day:2}"##) + } else { + format!(r##"{day:2}"##) + }; + week.push(day_string); + + if week.len() == 7 { + lines.push(week.join(" ")); + week.clear(); + } + } + + if !week.is_empty() { + week.resize(7, " ".to_owned()); + lines.push(week.join(" ")); + } + + lines.join("\n") +} + +fn get_upcoming_events() -> String { + run_khal(&["list", "today", "7d"]).unwrap_or_default() +} + +fn main() { + let now = Local::now(); + let today = now.date_naive(); + let event_days = get_event_days(today.year(), today.month()); + let calendar = build_calendar(today.year(), today.month(), &event_days, today); + let events = get_upcoming_events(); + + let tooltip = if events.is_empty() { + calendar + } else { + format!(r##"{calendar}\n\nUpcoming\n{events}"##) + }; + + println!( + "{}", + json!({ + "text": format!("{:02}:{:02}", now.hour(), now.minute()), + "tooltip": tooltip, + "alt": today.format("%Y-%m-%d").to_string(), + }) + ); +} diff --git a/bazzite/mail-counter/.gitignore b/bazzite/mail-counter/.gitignore new file mode 100644 index 0000000..b83d222 --- /dev/null +++ b/bazzite/mail-counter/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/bazzite/mail-counter/Cargo.lock b/bazzite/mail-counter/Cargo.lock new file mode 100644 index 0000000..36e96e7 --- /dev/null +++ b/bazzite/mail-counter/Cargo.lock @@ -0,0 +1,213 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bitflags" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" + +[[package]] +name = "cc" +version = "1.2.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "libsqlite3-sys" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "mail-counter" +version = "0.1.0" +dependencies = [ + "rusqlite", + "serde_json", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rusqlite" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" +dependencies = [ + "bitflags", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/bazzite/mail-counter/Cargo.toml b/bazzite/mail-counter/Cargo.toml new file mode 100644 index 0000000..6a5c456 --- /dev/null +++ b/bazzite/mail-counter/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "mail-counter" +version = "0.1.0" +edition = "2024" + +[dependencies] +rusqlite = { version = "0.37", features = ["bundled"] } +serde_json = "1" + +[lints.rust] +unsafe_code = "warn" + +[lints.clippy] +all = "warn" +pedantic = "warn" diff --git a/bazzite/mail-counter/README.md b/bazzite/mail-counter/README.md new file mode 100644 index 0000000..5cc118e --- /dev/null +++ b/bazzite/mail-counter/README.md @@ -0,0 +1,18 @@ +# mail-counter + +Waybar custom mail module for Thunderbird. It reads Thunderbird's +`global-messages-db.sqlite` and prints JSON for Waybar. + +## Build On Bazzite + +```bash +cd ~/dotfiles/bazzite/mail-counter +cargo build --release +install -m 755 target/release/mail-counter ~/.config/waybar/scripts/mail +``` + +Waybar config should call the compiled binary: + +```jsonc +"exec": "~/.config/waybar/scripts/mail" +``` diff --git a/bazzite/mail-counter/src/main.rs b/bazzite/mail-counter/src/main.rs new file mode 100644 index 0000000..e49a56d --- /dev/null +++ b/bazzite/mail-counter/src/main.rs @@ -0,0 +1,113 @@ +use std::{ + env, fs, + path::{Path, PathBuf}, +}; + +use rusqlite::{Connection, OpenFlags}; +use serde_json::{Value, json}; + +const MAIL_ICON: &str = "\u{f01ee}"; + +fn thunderbird_profile() -> Option { + let home = PathBuf::from(env::var_os("HOME")?); + let roots = [ + home.join(".var/app/org.mozilla.Thunderbird/.thunderbird"), + home.join(".thunderbird"), + ]; + + roots.iter().find_map(|root| { + let mut profiles = fs::read_dir(root) + .ok()? + .filter_map(Result::ok) + .map(|entry| entry.path()) + .filter(|path| { + path.is_dir() + && path + .file_name() + .and_then(|name| name.to_str()) + .is_some_and(|name| name.ends_with(".default-esr")) + }) + .collect::>(); + profiles.sort(); + profiles.into_iter().next() + }) +} + +fn open_readonly_database(path: &Path) -> rusqlite::Result { + Connection::open_with_flags( + path, + OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_NO_MUTEX, + ) +} + +fn unread_from_messages(conn: &Connection) -> rusqlite::Result { + conn.query_row( + r#"SELECT COUNT(*) + FROM messages + WHERE jsonAttributes LIKE '%"read":false%' + OR jsonAttributes LIKE '%"read": false%'"#, + [], + |row| row.get(0), + ) +} + +fn unread_from_folders(conn: &Connection) -> rusqlite::Result { + conn.query_row( + "SELECT SUM(numNewMessages) FROM folderlocations WHERE numNewMessages > 0", + [], + |row| row.get::<_, Option>(0), + ) + .map(Option::unwrap_or_default) +} + +fn unread_count(db_path: &Path) -> i64 { + let Ok(conn) = open_readonly_database(db_path) else { + return 0; + }; + + unread_from_messages(&conn) + .or_else(|_| unread_from_folders(&conn)) + .unwrap_or_default() +} + +fn waybar_json(count: i64) -> Value { + if count > 0 { + json!({ + "text": format!("{MAIL_ICON} {count}"), + "tooltip": format!("{count} unread emails"), + "class": "unread", + }) + } else { + json!({ + "text": MAIL_ICON, + "tooltip": "No unread emails", + }) + } +} + +fn main() { + let Some(profile) = thunderbird_profile() else { + println!( + "{}", + json!({ + "text": "", + "tooltip": "No Thunderbird profile found", + }) + ); + return; + }; + + let db_path = profile.join("global-messages-db.sqlite"); + if !db_path.exists() { + println!( + "{}", + json!({ + "text": "", + "tooltip": "No message database", + }) + ); + return; + } + + println!("{}", waybar_json(unread_count(&db_path))); +} diff --git a/bazzite/niri/config.kdl b/bazzite/niri/config.kdl index 6968f61..3177dbf 100644 --- a/bazzite/niri/config.kdl +++ b/bazzite/niri/config.kdl @@ -145,6 +145,7 @@ environment { } // screenshot-path "~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png" +spawn-at-startup "sh" "-c" "exec ~/.local/bin/swaybg -i ~/Pictures/Wallpapers/image.png -m fill" spawn-at-startup "waybar" hotkey-overlay { // skip-at-startup @@ -365,6 +366,14 @@ window-rule { // open-fullscreen false } +window-rule { + match title="khal-popup" + open-floating true + default-floating-position x=0 y=0 relative-to="top-right" + default-column-width { fixed 600; } + default-window-height { fixed 400; } +} + window-rule { match is-active=false // opacity 0.95 diff --git a/bazzite/screenshots/waybar-icons-fixed.png b/bazzite/screenshots/waybar-icons-fixed.png new file mode 100644 index 0000000..bbb94e6 Binary files /dev/null and b/bazzite/screenshots/waybar-icons-fixed.png differ diff --git a/bazzite/wallpapers/image.png b/bazzite/wallpapers/image.png new file mode 100644 index 0000000..213256f Binary files /dev/null and b/bazzite/wallpapers/image.png differ diff --git a/bazzite/wallpapers/raspberry.png b/bazzite/wallpapers/raspberry.png new file mode 100644 index 0000000..cf7b725 Binary files /dev/null and b/bazzite/wallpapers/raspberry.png differ diff --git a/bazzite/waybar/config.jsonc b/bazzite/waybar/config.jsonc index bc19250..9711ce1 100644 --- a/bazzite/waybar/config.jsonc +++ b/bazzite/waybar/config.jsonc @@ -13,18 +13,17 @@ "modules-right": [ "privacy", "custom/mail", - "idle_inhibitor", "custom/weather", "pulseaudio", "network", + "niri/language", "power-profiles-daemon", "cpu", "memory", "temperature", "custom/gpu", "disk", - "niri/language", - "clock", + "custom/calendar", "tray" ], @@ -34,11 +33,15 @@ "niri/window": { "format": "{}", - "max-length": 50 + "max-length": 50, + "align": 0.5, + "justify": "center" }, "niri/language": { - "format": "{short}" + "format": "{short}", + "align": 0.5, + "justify": "center" }, "privacy": { @@ -53,20 +56,14 @@ }, "custom/mail": { - "format": "{}", + "format": "{}", "return-type": "json", - "exec": "python3 ~/.config/waybar/scripts/mail.py", + "exec": "~/.config/waybar/scripts/mail", "interval": 60, "tooltip": true, - "on-click": "flatpak run org.mozilla.Thunderbird" - }, - - "idle_inhibitor": { - "format": "{icon}", - "format-icons": { - "activated": "", - "deactivated": "" - } + "on-click": "flatpak run org.mozilla.Thunderbird", + "align": 0.5, + "justify": "center" }, "custom/weather": { @@ -74,87 +71,109 @@ "return-type": "json", "exec": "~/.config/waybar/scripts/weather.sh", "interval": 900, - "tooltip": true + "tooltip": true, + "align": 0.5, + "justify": "center" }, "custom/gpu": { - "format": " {}", + "format": "{}", "return-type": "json", "exec": "~/.config/waybar/scripts/gpu.sh", "interval": 5, - "tooltip": true + "tooltip": true, + "align": 0.5, + "justify": "center" }, "disk": { - "format": "{percentage_used}% ", + "format": " {percentage_used}%", "path": "/", - "tooltip-format": "{used} / {total} ({percentage_used}%)" + "tooltip-format": "{used} / {total} ({percentage_used}%)", + "align": 0.5, + "justify": "center" }, "tray": { "spacing": 10 }, - "clock": { - "tooltip-format": "{:%Y %B}\n{calendar}", - "format": "{:%H:%M}", - "format-alt": "{:%Y-%m-%d}" + "custom/calendar": { + "format": " {}", + "return-type": "json", + "exec": "~/.config/waybar/scripts/khal-calendar", + "interval": 30, + "tooltip": true, + "on-click": "alacritty --title khal-popup -e bash -c 'vdirsyncer sync 2>/dev/null; ikhal'", + "align": 0.5, + "justify": "center" }, "cpu": { - "format": "{usage}% ", - "tooltip": false + "format": " {usage}%", + "tooltip": false, + "align": 0.5, + "justify": "center" }, "memory": { - "format": "{}% " + "format": " {}%", + "align": 0.5, + "justify": "center" }, "temperature": { "critical-threshold": 80, - "format": "{temperatureC}°C {icon}", - "format-icons": ["", "", ""] + "format": " {temperatureC}°C", + "align": 0.5, + "justify": "center" }, "network": { - "format-wifi": "{icon}", + "format-wifi": "{icon}", "format-ethernet": "", "format-icons": ["󰤯", "󰤟", "󰤢", "󰤥", "󰤨"], "tooltip-format-wifi": "{essid} ({signalStrength}%)\n{ipaddr}/{cidr}", "tooltip-format-ethernet": "{ifname}\n{ipaddr}/{cidr}", "format-linked": "{ifname} (No IP) ", - "format-disconnected": "󰤭", - "on-click": "" + "format-disconnected": "󰤭", + "on-click": "", + "align": 0.5, + "justify": "center" }, "pulseaudio": { - "format": "{volume}% {icon} {format_source}", - "format-bluetooth": "{volume}% {icon} {format_source}", - "format-bluetooth-muted": " {icon} {format_source}", - "format-muted": " {format_source}", - "format-source": "{volume}% ", - "format-source-muted": "", + "format": "{icon} {volume}% {format_source}", + "format-bluetooth": "{icon} {volume}% {format_source}", + "format-bluetooth-muted": " {format_source}", + "format-muted": " {format_source}", + "format-source": "{volume}%", + "format-source-muted": "", "format-icons": { - "headphone": "", - "hands-free": "", - "headset": "", - "phone": "", - "portable": "", - "car": "", - "default": ["", "", ""] + "headphone": "", + "hands-free": "", + "headset": "", + "phone": "", + "portable": "", + "car": "", + "default": ["", "", ""] }, - "on-click": "pavucontrol" + "on-click": "pavucontrol", + "align": 0.5, + "justify": "center" }, "power-profiles-daemon": { - "format": "{icon}", + "format": "{icon}", "tooltip-format": "Power profile: {profile}\nDriver: {driver}", "tooltip": true, + "align": 0.5, + "justify": "center", "format-icons": { - "default": "", - "performance": "", - "balanced": "", - "power-saver": "" + "default": "", + "performance": "", + "balanced": "", + "power-saver": "" } } } diff --git a/bazzite/waybar/scripts/gpu.sh b/bazzite/waybar/scripts/gpu.sh old mode 100644 new mode 100755 index 420f528..fb870d1 --- a/bazzite/waybar/scripts/gpu.sh +++ b/bazzite/waybar/scripts/gpu.sh @@ -1,9 +1,9 @@ #!/bin/bash data=$(nvidia-smi --query-gpu=utilization.gpu,temperature.gpu --format=csv,noheader,nounits 2>/dev/null) if [ -z "$data" ]; then - echo '{"text": "N/A", "tooltip": "nvidia-smi not available"}' + echo '{"text": "GPU N/A", "tooltip": "nvidia-smi not available"}' exit 0 fi usage=$(echo "$data" | cut -d',' -f1 | tr -d ' ') temp=$(echo "$data" | cut -d',' -f2 | tr -d ' ') -echo "{\"text\": \"${usage}% ${temp}°C\", \"tooltip\": \"GPU: ${usage}%\\nTemp: ${temp}°C\", \"class\": \"\"}" +echo "{\"text\": \"GPU ${usage}% ${temp}°C\", \"tooltip\": \"GPU: ${usage}%\\nTemp: ${temp}°C\", \"class\": \"\"}" diff --git a/bazzite/waybar/scripts/mail.py b/bazzite/waybar/scripts/mail.py deleted file mode 100644 index d249345..0000000 --- a/bazzite/waybar/scripts/mail.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python3 -import glob -import json -import sqlite3 -import os - -profile_dirs = glob.glob( - os.path.expanduser("~/.var/app/org.mozilla.Thunderbird/.thunderbird/*.default-esr") -) or glob.glob( - os.path.expanduser("~/.thunderbird/*.default-esr") -) - -if not profile_dirs: - print(json.dumps({"text": "", "tooltip": "No Thunderbird profile found"})) - exit() - -db_path = os.path.join(profile_dirs[0], "global-messages-db.sqlite") -if not os.path.exists(db_path): - print(json.dumps({"text": "", "tooltip": "No message database"})) - exit() - -try: - conn = sqlite3.connect(f"file:{db_path}?mode=ro&nolock=1", uri=True) - cursor = conn.cursor() - cursor.execute( - "SELECT COUNT(*) FROM messages WHERE jsonAttributes LIKE '%\"read\":false%' OR jsonAttributes LIKE '%\"read\": false%'" - ) - count = cursor.fetchone()[0] - conn.close() -except Exception: - # fallback: try folderStatus - try: - conn = sqlite3.connect(f"file:{db_path}?mode=ro&nolock=1", uri=True) - cursor = conn.cursor() - cursor.execute("SELECT SUM(numNewMessages) FROM folderlocations WHERE numNewMessages > 0") - row = cursor.fetchone() - count = row[0] if row and row[0] else 0 - conn.close() - except Exception: - count = 0 - -if count > 0: - print(json.dumps({"text": f"󰇮 {count}", "tooltip": f"{count} unread emails", "class": "unread"})) -else: - print(json.dumps({"text": "󰇮", "tooltip": "No unread emails"})) diff --git a/bazzite/waybar/scripts/weather.sh b/bazzite/waybar/scripts/weather.sh old mode 100644 new mode 100755 diff --git a/bazzite/waybar/style.css b/bazzite/waybar/style.css index 18ba1a2..4cffa06 100644 --- a/bazzite/waybar/style.css +++ b/bazzite/waybar/style.css @@ -27,13 +27,13 @@ @define-color crust #dce0e8; * { - font-family: 'JetBrainsMono Nerd Font', 'Noto Sans Mono', monospace; + font-family: 'Noto Sans Mono', 'Symbols Nerd Font Mono', 'Symbols Nerd Font', 'Font Awesome 6 Free', monospace; font-size: 15px; font-weight: bold; } window#waybar { - background-color: #1e1e2e; + background-color: rgba(30, 30, 46, 0.72); color: @text; transition-property: background-color; transition-duration: .5s; @@ -46,7 +46,7 @@ window#waybar.hidden { button { box-shadow: inset 0 -3px transparent; border: none; - border-radius: 0; + border-radius: 16px; } button:hover { @@ -56,27 +56,30 @@ button:hover { /* Workspaces */ #workspaces button { - padding: 0 6px; - background-color: transparent; - color: #a6adc8; + min-height: 24px; + padding: 4px 8px; + background-color: #000000; + color: @overlay2; + border-radius: 16px; } #workspaces button:hover { - background: alpha(@surface0, 0.5); + background: #000000; } #workspaces button.active { - color: #cdd6f4; + color: @blue; box-shadow: inset 0 -2px @blue; } #workspaces button.urgent { - background-color: alpha(@red, 0.2); - color: @red; + background-color: #000000; + color: @base; } /* Common module styling */ -#clock, +#window, +#custom-calendar, #cpu, #memory, #disk, @@ -85,27 +88,50 @@ button:hover { #pulseaudio, #wireplumber, #tray, -#idle_inhibitor, #power-profiles-daemon, #language, #custom-gpu, #custom-weather, #privacy { - padding: 0 10px; + min-height: 24px; + padding: 4px 8px; + background-color: #000000; + border-radius: 16px; color: @text; } +#workspaces button label, +#window label, +#custom-calendar label, +#cpu label, +#memory label, +#disk label, +#temperature label, +#network label, +#pulseaudio label, +#wireplumber label, +#tray label, +#power-profiles-daemon label, +#language label, +#custom-gpu label, +#custom-weather label, +#privacy label { + min-height: 24px; + padding: 0; + margin: 0; +} + #window, #workspaces { margin: 0 4px; } #window { - color: #cdd6f4; + color: @text; } -/* Clock */ -#clock { +/* Calendar/Clock */ +#custom-calendar { color: @blue; font-weight: bold; } @@ -154,15 +180,6 @@ button:hover { font-weight: bold; } -/* Idle inhibitor */ -#idle_inhibitor { - color: @overlay1; -} - -#idle_inhibitor.activated { - color: @peach; -} - /* Power profiles */ #power-profiles-daemon { color: @subtext0; @@ -206,24 +223,28 @@ button:hover { /* Privacy */ #privacy { + background-color: transparent; padding: 0; } #privacy-item { - padding: 0 5px; + min-height: 24px; + padding: 4px 8px; color: @base; + background-color: #000000; + border-radius: 16px; } #privacy-item.screenshare { - background-color: @peach; + background-color: #000000; } #privacy-item.audio-in { - background-color: @red; + background-color: #000000; } #privacy-item.audio-out { - background-color: @blue; + background-color: #000000; } /* Tray */ diff --git a/fedora/README.md b/fedora/README.md new file mode 100644 index 0000000..d2d70d5 --- /dev/null +++ b/fedora/README.md @@ -0,0 +1,89 @@ +# Конфиги рабочего стола Fedora + +Эта папка содержит профиль Fedora KDE/Workstation, где основной рабочей +Wayland-сессией становится Niri. Профиль получен из старого `bazzite/`, но +адаптирован под обычную mutable Fedora: пакеты ставятся через `dnf`, Steam +запускается обычным `/usr/bin/steam`, а локальные Bazzite helpers не требуются. + +## Что здесь настроено + +| Путь | Назначение | +|---|---| +| `niri/config.kdl` | Конфиг Niri: монитор `DP-1`, ввод, раскладка окон, автозапуск, правила окон, хоткеи. | +| `waybar/` | Верхняя панель: конфиг, CSS в стиле Catppuccin и custom-модули. | +| `waybar/scripts/gpu.sh` | Модуль Waybar для загрузки и температуры NVIDIA GPU через `nvidia-smi`. | +| `waybar/scripts/weather.sh` | Модуль Waybar с погодой через `wttr.in`. | +| `khal-calendar/` | Rust-версия календарного модуля Waybar. | +| `mail-counter/` | Rust-модуль Waybar для счетчика непрочитанных писем Thunderbird. | +| `fuzzel/fuzzel.ini` | Тема лаунчера Fuzzel на цветах Catppuccin Latte. | +| `btop/btop.conf` | Настройки системного монитора btop. | +| `glow/glow.yml` | Настройки терминального Markdown-viewer Glow. | +| `gtk-3.0/`, `gtk-4.0/` | GTK-настройки и цветовые файлы Breeze/Catppuccin. | +| `xsettingsd/xsettingsd.conf` | Настройки GTK, курсора, иконок, шрифта и DPI для X11/XWayland-приложений. | +| `environment.d/intel-gtk-fix.conf` | Переменная окружения сессии: `GSK_RENDERER=gl`. | +| `autostart/steam.desktop` | Автозапуск Steam через обычный Fedora Steam. | +| `wallpapers/raspberry.png` | Текущая картинка рабочего стола для `swaybg`. | + +## Ожидаемые программы + +```bash +sudo dnf install -y \ + niri waybar fuzzel alacritty swaybg swaylock \ + btop glow xsettingsd pavucontrol xsel mpv nautilus \ + khal vdirsyncer thunderbird rustup cargo +``` + +Также ожидается уже настроенный NVIDIA-драйвер с рабочим `nvidia-smi`. + +## Установка + +Подключить директории в `~/.config`: + +```bash +for dir in niri fuzzel waybar btop glow gtk-3.0 gtk-4.0 xsettingsd environment.d autostart; do + ln -sfn ~/dotfiles/fedora/$dir ~/.config/$dir +done +mkdir -p ~/Pictures/Wallpapers +ln -sfn ~/dotfiles/fedora/wallpapers/raspberry.png ~/Pictures/Wallpapers/raspberry.png +``` + +Если репозиторий лежит не в `~/dotfiles`, путь нужно поправить. + +## Сборка календарного модуля + +```bash +cd ~/dotfiles/fedora/khal-calendar +cargo build --release +install -m 755 target/release/khal-calendar ~/.config/waybar/scripts/khal-calendar +``` + +## Сборка почтового модуля + +```bash +cd ~/dotfiles/fedora/mail-counter +cargo build --release +install -m 755 target/release/mail-counter ~/.config/waybar/scripts/mail +``` + +Модуль читает `global-messages-db.sqlite` из профиля Thunderbird и печатает JSON +для `custom/mail` в Waybar. + +## Fedora-specific отличия от Bazzite + +- Steam запускается через `/usr/bin/steam`, не через `/usr/bin/bazzite-steam`. +- Wallpaper запускается через `swaybg` из `$PATH`, не через `~/.local/bin/swaybg`. +- Hotkeys на `refresh.sh` отключены, потому что этого локального helper пока нет. +- Clipboard hotkeys больше не зависят от локальной команды `t`. +- Firefox/Telegram правила Niri отправляют окна на `DP-1`, текущий подключенный + выход на этой Fedora-машине. + +## Перезагрузка + +После изменения конфига или CSS Waybar: + +```bash +pkill -SIGUSR2 waybar +``` + +Если Waybar не подхватил изменения, нужно перезапустить Waybar вручную или +перезапустить Niri-сессию. diff --git a/fedora/autostart/steam.desktop b/fedora/autostart/steam.desktop new file mode 100644 index 0000000..0cd14eb --- /dev/null +++ b/fedora/autostart/steam.desktop @@ -0,0 +1,281 @@ +[Desktop Entry] +Name=Steam +Comment=Application for managing and playing games on Steam +Comment[pt_BR]=Aplicativo para jogar e gerenciar jogos no Steam +Comment[bg]=Приложение за ръководене и пускане на игри в Steam +Comment[cs]=Aplikace pro spravování a hraní her ve službě Steam +Comment[da]=Applikation til at håndtere og spille spil på Steam +Comment[nl]=Applicatie voor het beheer en het spelen van games op Steam +Comment[fi]=Steamin pelien hallintaan ja pelaamiseen tarkoitettu sovellus +Comment[fr]=Application de gestion et d'utilisation des jeux sur Steam +Comment[de]=Anwendung zum Verwalten und Spielen von Spielen auf Steam +Comment[el]=Εφαρμογή διαχείρισης παιχνιδιών στο Steam +Comment[hu]=Alkalmazás a Steames játékok futtatásához és kezeléséhez +Comment[it]=Applicazione per la gestione e l'esecuzione di giochi su Steam +Comment[ja]=Steam 上でゲームを管理&プレイするためのアプリケーション +Comment[ko]=Steam에 있는 게임을 관리하고 플레이할 수 있는 응용 프로그램 +Comment[no]=Program for å administrere og spille spill på Steam +Comment[pt_PT]=Aplicação para organizar e executar jogos no Steam +Comment[pl]=Aplikacja do zarządzania i uruchamiania gier na platformie Steam +Comment[ro]=Aplicație pentru administrarea și jucatul jocurilor pe Steam +Comment[ru]=Приложение для игр и управления играми в Steam +Comment[es]=Aplicación para administrar y ejecutar juegos en Steam +Comment[sv]=Ett program för att hantera samt spela spel på Steam +Comment[zh_CN]=管理和进行 Steam 游戏的应用程序 +Comment[zh_TW]=管理並執行 Steam 遊戲的應用程式 +Comment[th]=โปรแกรมสำหรับจัดการและเล่นเกมบน Steam +Comment[tr]=Steam üzerinden oyun oynama ve düzenleme uygulaması +Comment[uk]=Програма для керування іграми та запуску ігор у Steam +Comment[vi]=Ứng dụng để quản lý và chơi trò chơi trên Steam +Exec=/usr/bin/steam -silent %U +Icon=steam +Terminal=false +Type=Application +Categories=Network;FileTransfer;Game; +MimeType=x-scheme-handler/steam;x-scheme-handler/steamlink; +Actions=Store;Community;Library;Servers;Screenshots;News;Settings;BigPicture;Friends; +X-Desktop-File-Install-Version=0.28 + +[Desktop Action Store] +Name=Store +Name[pt_BR]=Loja +Name[bg]=Магазин +Name[cs]=Obchod +Name[da]=Butik +Name[nl]=Winkel +Name[fi]=Kauppa +Name[fr]=Magasin +Name[de]=Shop +Name[el]=ΚΑΤΑΣΤΗΜΑ +Name[hu]=Áruház +Name[it]=Negozio +Name[ja]=ストア +Name[ko]=상점 +Name[no]=Butikk +Name[pt_PT]=Loja +Name[pl]=Sklep +Name[ro]=Magazin +Name[ru]=Магазин +Name[es]=Tienda +Name[sv]=Butik +Name[zh_CN]=商店 +Name[zh_TW]=商店 +Name[th]=ร้านค้า +Name[tr]=Mağaza +Name[uk]=Крамниця +Name[vi]=Cửa hàng +Exec=/usr/bin/steam steam://store + +[Desktop Action Community] +Name=Community +Name[pt_BR]=Comunidade +Name[bg]=Общност +Name[cs]=Komunita +Name[da]=Fællesskab +Name[nl]=Community +Name[fi]=Yhteisö +Name[fr]=Communauté +Name[de]=Community +Name[el]=Κοινότητα +Name[hu]=Közösség +Name[it]=Comunità +Name[ja]=コミュニティ +Name[ko]=커뮤니티 +Name[no]=Samfunn +Name[pt_PT]=Comunidade +Name[pl]=Społeczność +Name[ro]=Comunitate +Name[ru]=Сообщество +Name[es]=Comunidad +Name[sv]=Gemenskap +Name[zh_CN]=社区 +Name[zh_TW]=社群 +Name[th]=ชุมชน +Name[tr]=Topluluk +Name[uk]=Спільнота +Name[vi]=Cộng đồng +Exec=/usr/bin/steam steam://url/CommunityHome/ + +[Desktop Action Library] +Name=Library +Name[pt_BR]=Biblioteca +Name[bg]=Библиотека +Name[cs]=Knihovna +Name[da]=Bibliotek +Name[nl]=Bibliotheek +Name[fi]=Kokoelma +Name[fr]=Bibliothèque +Name[de]=Bibliothek +Name[el]=Συλλογή +Name[hu]=Könyvtár +Name[it]=Libreria +Name[ja]=ライブラリ +Name[ko]=라이브러리 +Name[no]=Bibliotek +Name[pt_PT]=Biblioteca +Name[pl]=Biblioteka +Name[ro]=Colecţie +Name[ru]=Библиотека +Name[es]=Biblioteca +Name[sv]=Bibliotek +Name[zh_CN]=库 +Name[zh_TW]=收藏庫 +Name[th]=คลัง +Name[tr]=Kütüphane +Name[uk]=Бібліотека +Name[vi]=Thư viện +Exec=/usr/bin/steam steam://open/games + +[Desktop Action Servers] +Name=Servers +Name[pt_BR]=Servidores +Name[bg]=Сървъри +Name[cs]=Servery +Name[da]=Servere +Name[nl]=Servers +Name[fi]=Palvelimet +Name[fr]=Serveurs +Name[de]=Server +Name[el]=Διακομιστές +Name[hu]=Szerverek +Name[it]=Server +Name[ja]=サーバー +Name[ko]=서버 +Name[no]=Tjenere +Name[pt_PT]=Servidores +Name[pl]=Serwery +Name[ro]=Servere +Name[ru]=Серверы +Name[es]=Servidores +Name[sv]=Servrar +Name[zh_CN]=服务器 +Name[zh_TW]=伺服器 +Name[th]=เซิร์ฟเวอร์ +Name[tr]=Sunucular +Name[uk]=Сервери +Name[vi]=Máy chủ +Exec=/usr/bin/steam steam://open/servers + +[Desktop Action Screenshots] +Name=Screenshots +Name[pt_BR]=Capturas de tela +Name[bg]=Снимки +Name[cs]=Snímky obrazovky +Name[da]=Skærmbilleder +Name[nl]=Screenshots +Name[fi]=Kuvankaappaukset +Name[fr]=Captures d'écran +Name[de]=Screenshots +Name[el]=Φωτογραφίες +Name[hu]=Képernyőmentések +Name[it]=Screenshot +Name[ja]=スクリーンショット +Name[ko]=스크린샷 +Name[no]=Skjermbilder +Name[pt_PT]=Capturas de ecrã +Name[pl]=Zrzuty ekranu +Name[ro]=Capturi de ecran +Name[ru]=Скриншоты +Name[es]=Capturas +Name[sv]=Skärmdumpar +Name[zh_CN]=截图 +Name[zh_TW]=螢幕擷圖 +Name[th]=ภาพหน้าจอ +Name[tr]=Ekran Görüntüleri +Name[uk]=Скріншоти +Name[vi]=Ảnh chụp +Exec=/usr/bin/steam steam://open/screenshots + +[Desktop Action News] +Name=News +Name[pt_BR]=Notícias +Name[bg]=Новини +Name[cs]=Zprávy +Name[da]=Nyheder +Name[nl]=Nieuws +Name[fi]=Uutiset +Name[fr]=Actualités +Name[de]=Neuigkeiten +Name[el]=Νέα +Name[hu]=Hírek +Name[it]=Notizie +Name[ja]=ニュース +Name[ko]=뉴스 +Name[no]=Nyheter +Name[pt_PT]=Novidades +Name[pl]=Aktualności +Name[ro]=Știri +Name[ru]=Новости +Name[es]=Noticias +Name[sv]=Nyheter +Name[zh_CN]=新闻 +Name[zh_TW]=新聞 +Name[th]=ข่าวสาร +Name[tr]=Haberler +Name[uk]=Новини +Name[vi]=Tin tức +Exec=/usr/bin/steam steam://openurl/https://store.steampowered.com/news + +[Desktop Action Settings] +Name=Settings +Name[pt_BR]=Configurações +Name[bg]=Настройки +Name[cs]=Nastavení +Name[da]=Indstillinger +Name[nl]=Instellingen +Name[fi]=Asetukset +Name[fr]=Paramètres +Name[de]=Einstellungen +Name[el]=Ρυθμίσεις +Name[hu]=Beállítások +Name[it]=Impostazioni +Name[ja]=設定 +Name[ko]=설정 +Name[no]=Innstillinger +Name[pt_PT]=Definições +Name[pl]=Ustawienia +Name[ro]=Setări +Name[ru]=Настройки +Name[es]=Parámetros +Name[sv]=Inställningar +Name[zh_CN]=设置 +Name[zh_TW]=設定 +Name[th]=การตั้งค่า +Name[tr]=Ayarlar +Name[uk]=Налаштування +Name[vi]=Thiết lập +Exec=/usr/bin/steam steam://open/settings + +[Desktop Action BigPicture] +Name=Big Picture +Exec=/usr/bin/steam steam://open/bigpicture + +[Desktop Action Friends] +Name=Friends +Name[pt_BR]=Amigos +Name[bg]=Приятели +Name[cs]=Přátelé +Name[da]=Venner +Name[nl]=Vrienden +Name[fi]=Kaverit +Name[fr]=Amis +Name[de]=Freunde +Name[el]=Φίλοι +Name[hu]=Barátok +Name[it]=Amici +Name[ja]=フレンド +Name[ko]=친구 +Name[no]=Venner +Name[pt_PT]=Amigos +Name[pl]=Znajomi +Name[ro]=Prieteni +Name[ru]=Друзья +Name[es]=Amigos +Name[sv]=Vänner +Name[zh_CN]=好友 +Name[zh_TW]=好友 +Name[th]=เพื่อน +Name[tr]=Arkadaşlar +Name[uk]=Друзі +Name[vi]=Bạn bè +Exec=/usr/bin/steam steam://open/friends diff --git a/fedora/btop/btop.conf b/fedora/btop/btop.conf new file mode 100644 index 0000000..b1f33d0 --- /dev/null +++ b/fedora/btop/btop.conf @@ -0,0 +1,257 @@ +#? Config file for btop v. 1.4.5 + +#* Name of a btop++/bpytop/bashtop formatted ".theme" file, "Default" and "TTY" for builtin themes. +#* Themes should be placed in "../share/btop/themes" relative to binary or "$HOME/.config/btop/themes" +color_theme = "Default" + +#* If the theme set background should be shown, set to False if you want terminal background transparency. +theme_background = True + +#* Sets if 24-bit truecolor should be used, will convert 24-bit colors to 256 color (6x6x6 color cube) if false. +truecolor = True + +#* Set to true to force tty mode regardless if a real tty has been detected or not. +#* Will force 16-color mode and TTY theme, set all graph symbols to "tty" and swap out other non tty friendly symbols. +force_tty = False + +#* Define presets for the layout of the boxes. Preset 0 is always all boxes shown with default settings. Max 9 presets. +#* Format: "box_name:P:G,box_name:P:G" P=(0 or 1) for alternate positions, G=graph symbol to use for box. +#* Use whitespace " " as separator between different presets. +#* Example: "cpu:0:default,mem:0:tty,proc:1:default cpu:0:braille,proc:0:tty" +presets = "cpu:1:default,proc:0:default cpu:0:default,mem:0:default,net:0:default cpu:0:block,net:0:tty" + +#* Set to True to enable "h,j,k,l,g,G" keys for directional control in lists. +#* Conflicting keys for h:"help" and k:"kill" is accessible while holding shift. +vim_keys = False + +#* Rounded corners on boxes, is ignored if TTY mode is ON. +rounded_corners = True + +#* Default symbols to use for graph creation, "braille", "block" or "tty". +#* "braille" offers the highest resolution but might not be included in all fonts. +#* "block" has half the resolution of braille but uses more common characters. +#* "tty" uses only 3 different symbols but will work with most fonts and should work in a real TTY. +#* Note that "tty" only has half the horizontal resolution of the other two, so will show a shorter historical view. +graph_symbol = "braille" + +# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty". +graph_symbol_cpu = "default" + +# Graph symbol to use for graphs in gpu box, "default", "braille", "block" or "tty". +graph_symbol_gpu = "default" + +# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty". +graph_symbol_mem = "default" + +# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty". +graph_symbol_net = "default" + +# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty". +graph_symbol_proc = "default" + +#* Manually set which boxes to show. Available values are "cpu mem net proc" and "gpu0" through "gpu5", separate values with whitespace. +shown_boxes = "proc cpu mem net" + +#* Update time in milliseconds, recommended 2000 ms or above for better sample times for graphs. +update_ms = 1000 + +#* Processes sorting, "pid" "program" "arguments" "threads" "user" "memory" "cpu lazy" "cpu direct", +#* "cpu lazy" sorts top process over time (easier to follow), "cpu direct" updates top process directly. +proc_sorting = "memory" + +#* Reverse sorting order, True or False. +proc_reversed = False + +#* Show processes as a tree. +proc_tree = False + +#* Use the cpu graph colors in the process list. +proc_colors = True + +#* Use a darkening gradient in the process list. +proc_gradient = True + +#* If process cpu usage should be of the core it's running on or usage of the total available cpu power. +proc_per_core = False + +#* Show process memory as bytes instead of percent. +proc_mem_bytes = True + +#* Show cpu graph for each process. +proc_cpu_graphs = True + +#* Use /proc/[pid]/smaps for memory information in the process info box (very slow but more accurate) +proc_info_smaps = False + +#* Show proc box on left side of screen instead of right. +proc_left = False + +#* (Linux) Filter processes tied to the Linux kernel(similar behavior to htop). +proc_filter_kernel = False + +#* In tree-view, always accumulate child process resources in the parent process. +proc_aggregate = False + +#* Sets the CPU stat shown in upper half of the CPU graph, "total" is always available. +#* Select from a list of detected attributes from the options menu. +cpu_graph_upper = "Auto" + +#* Sets the CPU stat shown in lower half of the CPU graph, "total" is always available. +#* Select from a list of detected attributes from the options menu. +cpu_graph_lower = "Auto" + +#* If gpu info should be shown in the cpu box. Available values = "Auto", "On" and "Off". +show_gpu_info = "Auto" + +#* Toggles if the lower CPU graph should be inverted. +cpu_invert_lower = True + +#* Set to True to completely disable the lower CPU graph. +cpu_single_graph = False + +#* Show cpu box at bottom of screen instead of top. +cpu_bottom = False + +#* Shows the system uptime in the CPU box. +show_uptime = True + +#* Shows the CPU package current power consumption in watts. Requires running `make setcap` or `make setuid` or running with sudo. +show_cpu_watts = True + +#* Show cpu temperature. +check_temp = True + +#* Which sensor to use for cpu temperature, use options menu to select from list of available sensors. +cpu_sensor = "Auto" + +#* Show temperatures for cpu cores also if check_temp is True and sensors has been found. +show_coretemp = True + +#* Set a custom mapping between core and coretemp, can be needed on certain cpus to get correct temperature for correct core. +#* Use lm-sensors or similar to see which cores are reporting temperatures on your machine. +#* Format "x:y" x=core with wrong temp, y=core with correct temp, use space as separator between multiple entries. +#* Example: "4:0 5:1 6:3" +cpu_core_map = "" + +#* Which temperature scale to use, available values: "celsius", "fahrenheit", "kelvin" and "rankine". +temp_scale = "celsius" + +#* Use base 10 for bits/bytes sizes, KB = 1000 instead of KiB = 1024. +base_10_sizes = False + +#* Show CPU frequency. +show_cpu_freq = True + +#* Draw a clock at top of screen, formatting according to strftime, empty string to disable. +#* Special formatting: /host = hostname | /user = username | /uptime = system uptime +clock_format = "%X" + +#* Update main ui in background when menus are showing, set this to false if the menus is flickering too much for comfort. +background_update = True + +#* Custom cpu model name, empty string to disable. +custom_cpu_name = "" + +#* Optional filter for shown disks, should be full path of a mountpoint, separate multiple values with whitespace " ". +#* Only disks matching the filter will be shown. Prepend exclude= to only show disks not matching the filter. Examples: disk_filter="/boot /home/user", disks_filter="exclude=/boot /home/user" +disks_filter = "" + +#* Show graphs instead of meters for memory values. +mem_graphs = True + +#* Show mem box below net box instead of above. +mem_below_net = False + +#* Count ZFS ARC in cached and available memory. +zfs_arc_cached = True + +#* If swap memory should be shown in memory box. +show_swap = True + +#* Show swap as a disk, ignores show_swap value above, inserts itself after first disk. +swap_disk = True + +#* If mem box should be split to also show disks info. +show_disks = True + +#* Filter out non physical disks. Set this to False to include network disks, RAM disks and similar. +only_physical = True + +#* Read disks list from /etc/fstab. This also disables only_physical. +use_fstab = True + +#* Setting this to True will hide all datasets, and only show ZFS pools. (IO stats will be calculated per-pool) +zfs_hide_datasets = False + +#* Set to true to show available disk space for privileged users. +disk_free_priv = False + +#* Toggles if io activity % (disk busy time) should be shown in regular disk usage view. +show_io_stat = True + +#* Toggles io mode for disks, showing big graphs for disk read/write speeds. +io_mode = False + +#* Set to True to show combined read/write io graphs in io mode. +io_graph_combined = False + +#* Set the top speed for the io graphs in MiB/s (100 by default), use format "mountpoint:speed" separate disks with whitespace " ". +#* Example: "/mnt/media:100 /:20 /boot:1". +io_graph_speeds = "" + +#* Set fixed values for network graphs in Mebibits. Is only used if net_auto is also set to False. +net_download = 100 + +net_upload = 100 + +#* Use network graphs auto rescaling mode, ignores any values set above and rescales down to 10 Kibibytes at the lowest. +net_auto = True + +#* Sync the auto scaling for download and upload to whichever currently has the highest scale. +net_sync = True + +#* Starts with the Network Interface specified here. +net_iface = "" + +#* "True" shows bitrates in base 10 (Kbps, Mbps). "False" shows bitrates in binary sizes (Kibps, Mibps, etc.). "Auto" uses base_10_sizes. +base_10_bitrate = "Auto" + +#* Show battery stats in top right if battery is present. +show_battery = True + +#* Which battery to use if multiple are present. "Auto" for auto detection. +selected_battery = "Auto" + +#* Show power stats of battery next to charge indicator. +show_battery_watts = True + +#* Set loglevel for "~/.config/btop/btop.log" levels are: "ERROR" "WARNING" "INFO" "DEBUG". +#* The level set includes all lower levels, i.e. "DEBUG" will show all logging info. +log_level = "WARNING" + +#* Measure PCIe throughput on NVIDIA cards, may impact performance on certain cards. +nvml_measure_pcie_speeds = True + +#* Measure PCIe throughput on AMD cards, may impact performance on certain cards. +rsmi_measure_pcie_speeds = True + +#* Horizontally mirror the GPU graph. +gpu_mirror_graph = True + +#* Custom gpu0 model name, empty string to disable. +custom_gpu_name0 = "" + +#* Custom gpu1 model name, empty string to disable. +custom_gpu_name1 = "" + +#* Custom gpu2 model name, empty string to disable. +custom_gpu_name2 = "" + +#* Custom gpu3 model name, empty string to disable. +custom_gpu_name3 = "" + +#* Custom gpu4 model name, empty string to disable. +custom_gpu_name4 = "" + +#* Custom gpu5 model name, empty string to disable. +custom_gpu_name5 = "" diff --git a/fedora/environment.d/intel-gtk-fix.conf b/fedora/environment.d/intel-gtk-fix.conf new file mode 100644 index 0000000..75fe23c --- /dev/null +++ b/fedora/environment.d/intel-gtk-fix.conf @@ -0,0 +1 @@ +GSK_RENDERER=gl diff --git a/fedora/fuzzel/fuzzel.ini b/fedora/fuzzel/fuzzel.ini new file mode 100644 index 0000000..b784c59 --- /dev/null +++ b/fedora/fuzzel/fuzzel.ini @@ -0,0 +1,18 @@ +# Catppuccin Latte (Blue) + 70% opacity background + +[colors] +background=eff1f5b3 +text=4c4f69ff +prompt=5c5f77ff +placeholder=8c8fa1ff +input=4c4f69ff +match=1e66f5ff +selection=acb0beff +selection-text=4c4f69ff +selection-match=1e66f5ff +counter=8c8fa1ff +border=f38ba8ff + +[border] +width=2 +radius=6 diff --git a/fedora/glow/glow.yml b/fedora/glow/glow.yml new file mode 100644 index 0000000..749e1ec --- /dev/null +++ b/fedora/glow/glow.yml @@ -0,0 +1,10 @@ +# style name or JSON path (default "auto") +style: "auto" +# mouse support (TUI-mode only) +mouse: false +# use pager to display markdown +pager: false +# word-wrap at width +width: 80 +# show all files, including hidden and ignored. +all: false diff --git a/fedora/gtk-3.0/colors.css b/fedora/gtk-3.0/colors.css new file mode 100644 index 0000000..0c919b2 --- /dev/null +++ b/fedora/gtk-3.0/colors.css @@ -0,0 +1,84 @@ +@define-color borders_breeze #bcbebf; +@define-color content_view_bg_breeze #ffffff; +@define-color error_color_backdrop_breeze #da4453; +@define-color error_color_breeze #da4453; +@define-color error_color_insensitive_backdrop_breeze #f0b1b8; +@define-color error_color_insensitive_breeze #f0b1b8; +@define-color insensitive_base_color_breeze #f3f3f3; +@define-color insensitive_base_fg_color_breeze #aaabac; +@define-color insensitive_bg_color_breeze #e3e5e7; +@define-color insensitive_borders_breeze #d2d4d6; +@define-color insensitive_fg_color_breeze #a0a1a3; +@define-color insensitive_selected_bg_color_breeze #e3e5e7; +@define-color insensitive_selected_fg_color_breeze #a0a1a3; +@define-color insensitive_unfocused_bg_color_breeze #e3e5e7; +@define-color insensitive_unfocused_fg_color_breeze #a0a1a3; +@define-color insensitive_unfocused_selected_bg_color_breeze #e3e5e7; +@define-color insensitive_unfocused_selected_fg_color_breeze #a0a1a3; +@define-color link_color_breeze #2980b9; +@define-color link_visited_color_breeze #9b59b6; +@define-color success_color_backdrop_breeze #27ae60; +@define-color success_color_breeze #27ae60; +@define-color success_color_insensitive_backdrop_breeze #abd8be; +@define-color success_color_insensitive_breeze #abd8be; +@define-color theme_base_color_breeze #ffffff; +@define-color theme_bg_color_breeze #eff0f1; +@define-color theme_button_background_backdrop_breeze #fcfcfc; +@define-color theme_button_background_backdrop_insensitive_breeze #f0f0f0; +@define-color theme_button_background_insensitive_breeze #f0f0f0; +@define-color theme_button_background_normal_breeze #fcfcfc; +@define-color theme_button_decoration_focus_backdrop_breeze #3daee9; +@define-color theme_button_decoration_focus_backdrop_insensitive_breeze #a2d8f3; +@define-color theme_button_decoration_focus_breeze #3daee9; +@define-color theme_button_decoration_focus_insensitive_breeze #a2d8f3; +@define-color theme_button_decoration_hover_backdrop_breeze #3daee9; +@define-color theme_button_decoration_hover_backdrop_insensitive_breeze #a2d8f3; +@define-color theme_button_decoration_hover_breeze #3daee9; +@define-color theme_button_decoration_hover_insensitive_breeze #a2d8f3; +@define-color theme_button_foreground_active_backdrop_breeze #232629; +@define-color theme_button_foreground_active_backdrop_insensitive_breeze #a0a1a3; +@define-color theme_button_foreground_active_breeze #ffffff; +@define-color theme_button_foreground_active_insensitive_breeze #a0a1a3; +@define-color theme_button_foreground_backdrop_breeze #232629; +@define-color theme_button_foreground_backdrop_insensitive_breeze #a8a9aa; +@define-color theme_button_foreground_insensitive_breeze #a8a9aa; +@define-color theme_button_foreground_normal_breeze #232629; +@define-color theme_fg_color_breeze #232629; +@define-color theme_header_background_backdrop_breeze #eff0f1; +@define-color theme_header_background_breeze #dee0e2; +@define-color theme_header_background_light_breeze #eff0f1; +@define-color theme_header_foreground_backdrop_breeze #232629; +@define-color theme_header_foreground_breeze #232629; +@define-color theme_header_foreground_insensitive_backdrop_breeze #232629; +@define-color theme_header_foreground_insensitive_breeze #232629; +@define-color theme_hovering_selected_bg_color_breeze #3daee9; +@define-color theme_selected_bg_color_breeze #3daee9; +@define-color theme_selected_fg_color_breeze #ffffff; +@define-color theme_text_color_breeze #232629; +@define-color theme_titlebar_background_backdrop_breeze #eff0f1; +@define-color theme_titlebar_background_breeze #dee0e2; +@define-color theme_titlebar_background_light_breeze #eff0f1; +@define-color theme_titlebar_foreground_backdrop_breeze #232629; +@define-color theme_titlebar_foreground_breeze #232629; +@define-color theme_titlebar_foreground_insensitive_backdrop_breeze #232629; +@define-color theme_titlebar_foreground_insensitive_breeze #232629; +@define-color theme_unfocused_base_color_breeze #ffffff; +@define-color theme_unfocused_bg_color_breeze #eff0f1; +@define-color theme_unfocused_fg_color_breeze #232629; +@define-color theme_unfocused_selected_bg_color_alt_breeze #c2e0f5; +@define-color theme_unfocused_selected_bg_color_breeze #c2e0f5; +@define-color theme_unfocused_selected_fg_color_breeze #232629; +@define-color theme_unfocused_text_color_breeze #232629; +@define-color theme_unfocused_view_bg_color_breeze #f3f3f3; +@define-color theme_unfocused_view_text_color_breeze #aaabac; +@define-color theme_view_active_decoration_color_breeze #3daee9; +@define-color theme_view_hover_decoration_color_breeze #3daee9; +@define-color tooltip_background_breeze #f7f7f7; +@define-color tooltip_border_breeze #c2c3c4; +@define-color tooltip_text_breeze #232629; +@define-color unfocused_borders_breeze #bcbebf; +@define-color unfocused_insensitive_borders_breeze #d2d4d6; +@define-color warning_color_backdrop_breeze #f67400; +@define-color warning_color_breeze #f67400; +@define-color warning_color_insensitive_backdrop_breeze #fbc289; +@define-color warning_color_insensitive_breeze #fbc289; diff --git a/fedora/gtk-3.0/gtk.css b/fedora/gtk-3.0/gtk.css new file mode 100644 index 0000000..c9763f7 --- /dev/null +++ b/fedora/gtk-3.0/gtk.css @@ -0,0 +1 @@ +@import 'colors.css'; \ No newline at end of file diff --git a/fedora/gtk-3.0/settings.ini b/fedora/gtk-3.0/settings.ini new file mode 100644 index 0000000..104481d --- /dev/null +++ b/fedora/gtk-3.0/settings.ini @@ -0,0 +1,17 @@ +[Settings] +gtk-application-prefer-dark-theme=false +gtk-button-images=true +gtk-cursor-blink=true +gtk-cursor-blink-time=1000 +gtk-cursor-theme-name=breeze_cursors +gtk-cursor-theme-size=24 +gtk-decoration-layout=icon:minimize,maximize,close +gtk-enable-animations=true +gtk-font-name=Noto Sans, 10 +gtk-icon-theme-name=breeze +gtk-menu-images=true +gtk-modules=colorreload-gtk-module +gtk-primary-button-warps-slider=true +gtk-sound-theme-name=ocean +gtk-toolbar-style=3 +gtk-xft-dpi=98304 diff --git a/fedora/gtk-4.0/colors.css b/fedora/gtk-4.0/colors.css new file mode 100644 index 0000000..0c919b2 --- /dev/null +++ b/fedora/gtk-4.0/colors.css @@ -0,0 +1,84 @@ +@define-color borders_breeze #bcbebf; +@define-color content_view_bg_breeze #ffffff; +@define-color error_color_backdrop_breeze #da4453; +@define-color error_color_breeze #da4453; +@define-color error_color_insensitive_backdrop_breeze #f0b1b8; +@define-color error_color_insensitive_breeze #f0b1b8; +@define-color insensitive_base_color_breeze #f3f3f3; +@define-color insensitive_base_fg_color_breeze #aaabac; +@define-color insensitive_bg_color_breeze #e3e5e7; +@define-color insensitive_borders_breeze #d2d4d6; +@define-color insensitive_fg_color_breeze #a0a1a3; +@define-color insensitive_selected_bg_color_breeze #e3e5e7; +@define-color insensitive_selected_fg_color_breeze #a0a1a3; +@define-color insensitive_unfocused_bg_color_breeze #e3e5e7; +@define-color insensitive_unfocused_fg_color_breeze #a0a1a3; +@define-color insensitive_unfocused_selected_bg_color_breeze #e3e5e7; +@define-color insensitive_unfocused_selected_fg_color_breeze #a0a1a3; +@define-color link_color_breeze #2980b9; +@define-color link_visited_color_breeze #9b59b6; +@define-color success_color_backdrop_breeze #27ae60; +@define-color success_color_breeze #27ae60; +@define-color success_color_insensitive_backdrop_breeze #abd8be; +@define-color success_color_insensitive_breeze #abd8be; +@define-color theme_base_color_breeze #ffffff; +@define-color theme_bg_color_breeze #eff0f1; +@define-color theme_button_background_backdrop_breeze #fcfcfc; +@define-color theme_button_background_backdrop_insensitive_breeze #f0f0f0; +@define-color theme_button_background_insensitive_breeze #f0f0f0; +@define-color theme_button_background_normal_breeze #fcfcfc; +@define-color theme_button_decoration_focus_backdrop_breeze #3daee9; +@define-color theme_button_decoration_focus_backdrop_insensitive_breeze #a2d8f3; +@define-color theme_button_decoration_focus_breeze #3daee9; +@define-color theme_button_decoration_focus_insensitive_breeze #a2d8f3; +@define-color theme_button_decoration_hover_backdrop_breeze #3daee9; +@define-color theme_button_decoration_hover_backdrop_insensitive_breeze #a2d8f3; +@define-color theme_button_decoration_hover_breeze #3daee9; +@define-color theme_button_decoration_hover_insensitive_breeze #a2d8f3; +@define-color theme_button_foreground_active_backdrop_breeze #232629; +@define-color theme_button_foreground_active_backdrop_insensitive_breeze #a0a1a3; +@define-color theme_button_foreground_active_breeze #ffffff; +@define-color theme_button_foreground_active_insensitive_breeze #a0a1a3; +@define-color theme_button_foreground_backdrop_breeze #232629; +@define-color theme_button_foreground_backdrop_insensitive_breeze #a8a9aa; +@define-color theme_button_foreground_insensitive_breeze #a8a9aa; +@define-color theme_button_foreground_normal_breeze #232629; +@define-color theme_fg_color_breeze #232629; +@define-color theme_header_background_backdrop_breeze #eff0f1; +@define-color theme_header_background_breeze #dee0e2; +@define-color theme_header_background_light_breeze #eff0f1; +@define-color theme_header_foreground_backdrop_breeze #232629; +@define-color theme_header_foreground_breeze #232629; +@define-color theme_header_foreground_insensitive_backdrop_breeze #232629; +@define-color theme_header_foreground_insensitive_breeze #232629; +@define-color theme_hovering_selected_bg_color_breeze #3daee9; +@define-color theme_selected_bg_color_breeze #3daee9; +@define-color theme_selected_fg_color_breeze #ffffff; +@define-color theme_text_color_breeze #232629; +@define-color theme_titlebar_background_backdrop_breeze #eff0f1; +@define-color theme_titlebar_background_breeze #dee0e2; +@define-color theme_titlebar_background_light_breeze #eff0f1; +@define-color theme_titlebar_foreground_backdrop_breeze #232629; +@define-color theme_titlebar_foreground_breeze #232629; +@define-color theme_titlebar_foreground_insensitive_backdrop_breeze #232629; +@define-color theme_titlebar_foreground_insensitive_breeze #232629; +@define-color theme_unfocused_base_color_breeze #ffffff; +@define-color theme_unfocused_bg_color_breeze #eff0f1; +@define-color theme_unfocused_fg_color_breeze #232629; +@define-color theme_unfocused_selected_bg_color_alt_breeze #c2e0f5; +@define-color theme_unfocused_selected_bg_color_breeze #c2e0f5; +@define-color theme_unfocused_selected_fg_color_breeze #232629; +@define-color theme_unfocused_text_color_breeze #232629; +@define-color theme_unfocused_view_bg_color_breeze #f3f3f3; +@define-color theme_unfocused_view_text_color_breeze #aaabac; +@define-color theme_view_active_decoration_color_breeze #3daee9; +@define-color theme_view_hover_decoration_color_breeze #3daee9; +@define-color tooltip_background_breeze #f7f7f7; +@define-color tooltip_border_breeze #c2c3c4; +@define-color tooltip_text_breeze #232629; +@define-color unfocused_borders_breeze #bcbebf; +@define-color unfocused_insensitive_borders_breeze #d2d4d6; +@define-color warning_color_backdrop_breeze #f67400; +@define-color warning_color_breeze #f67400; +@define-color warning_color_insensitive_backdrop_breeze #fbc289; +@define-color warning_color_insensitive_breeze #fbc289; diff --git a/fedora/gtk-4.0/gtk.css b/fedora/gtk-4.0/gtk.css new file mode 100644 index 0000000..15fda59 --- /dev/null +++ b/fedora/gtk-4.0/gtk.css @@ -0,0 +1,2 @@ +@import 'colors.css'; +@import 'kde_window_geometry.css'; \ No newline at end of file diff --git a/fedora/gtk-4.0/settings.ini b/fedora/gtk-4.0/settings.ini new file mode 100644 index 0000000..f8754a6 --- /dev/null +++ b/fedora/gtk-4.0/settings.ini @@ -0,0 +1,14 @@ +[Settings] +gtk-application-prefer-dark-theme=false +gtk-cursor-blink=true +gtk-cursor-blink-time=1000 +gtk-cursor-theme-name=breeze_cursors +gtk-cursor-theme-size=24 +gtk-decoration-layout=icon:minimize,maximize,close +gtk-enable-animations=true +gtk-font-name=Noto Sans, 10 +gtk-icon-theme-name=breeze +gtk-modules=colorreload-gtk-module +gtk-primary-button-warps-slider=true +gtk-sound-theme-name=ocean +gtk-xft-dpi=98304 diff --git a/fedora/khal-calendar/.gitignore b/fedora/khal-calendar/.gitignore new file mode 100644 index 0000000..b83d222 --- /dev/null +++ b/fedora/khal-calendar/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/fedora/khal-calendar/Cargo.lock b/fedora/khal-calendar/Cargo.lock new file mode 100644 index 0000000..b530709 --- /dev/null +++ b/fedora/khal-calendar/Cargo.lock @@ -0,0 +1,383 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "cc" +version = "1.2.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chrono" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" +dependencies = [ + "cfg-if", + "futures-util", + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "khal-calendar" +version = "0.1.0" +dependencies = [ + "chrono", + "serde_json", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "wasm-bindgen" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.120" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/fedora/khal-calendar/Cargo.toml b/fedora/khal-calendar/Cargo.toml new file mode 100644 index 0000000..9799e34 --- /dev/null +++ b/fedora/khal-calendar/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "khal-calendar" +version = "0.1.0" +edition = "2024" + +[dependencies] +chrono = "0.4" +serde_json = "1" + +[lints.rust] +unsafe_code = "warn" + +[lints.clippy] +all = "warn" +pedantic = "warn" diff --git a/fedora/khal-calendar/README.md b/fedora/khal-calendar/README.md new file mode 100644 index 0000000..9e8de6d --- /dev/null +++ b/fedora/khal-calendar/README.md @@ -0,0 +1,18 @@ +# khal-calendar + +Waybar custom calendar module that prints JSON with the current time and a Pango +tooltip. Event days and upcoming events are read from `khal`. + +## Build On Bazzite + +```bash +cd ~/dotfiles/bazzite/khal-calendar +cargo build --release +install -m 755 target/release/khal-calendar ~/.config/waybar/scripts/khal-calendar +``` + +Waybar config should call the compiled binary: + +```jsonc +"exec": "~/.config/waybar/scripts/khal-calendar" +``` diff --git a/fedora/khal-calendar/src/main.rs b/fedora/khal-calendar/src/main.rs new file mode 100644 index 0000000..72d74d6 --- /dev/null +++ b/fedora/khal-calendar/src/main.rs @@ -0,0 +1,160 @@ +use std::{ + collections::BTreeSet, + process::{Command, Stdio}, + thread::sleep, + time::{Duration, Instant}, +}; + +use chrono::{Datelike, Local, NaiveDate, Timelike}; +use serde_json::json; + +const COMMAND_TIMEOUT: Duration = Duration::from_secs(5); +const MONTH_NAMES: [&str; 13] = [ + "", + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", +]; + +fn run_khal(args: &[&str]) -> Option { + let mut child = Command::new("khal") + .args(args) + .stdout(Stdio::piped()) + .stderr(Stdio::null()) + .spawn() + .ok()?; + + let deadline = Instant::now() + COMMAND_TIMEOUT; + loop { + match child.try_wait() { + Ok(Some(_status)) => { + let output = child.wait_with_output().ok()?; + return Some(String::from_utf8_lossy(&output.stdout).trim().to_owned()); + } + Ok(None) if Instant::now() >= deadline => { + let _ = child.kill(); + let _ = child.wait(); + return None; + } + Ok(None) => sleep(Duration::from_millis(25)), + Err(_) => return None, + } + } +} + +fn days_in_month(year: i32, month: u32) -> u32 { + let (next_year, next_month) = if month == 12 { + (year + 1, 1) + } else { + (year, month + 1) + }; + + NaiveDate::from_ymd_opt(next_year, next_month, 1) + .and_then(|date| date.pred_opt()) + .map_or(31, |date| date.day()) +} + +fn get_event_days(year: i32, month: u32) -> BTreeSet { + let Some(first) = NaiveDate::from_ymd_opt(year, month, 1) else { + return BTreeSet::new(); + }; + let Some(last) = NaiveDate::from_ymd_opt(year, month, days_in_month(year, month)) else { + return BTreeSet::new(); + }; + + let start = first.format("%d.%m.%Y").to_string(); + let end = last.format("%d.%m.%Y").to_string(); + let Some(stdout) = run_khal(&[ + "list", + &start, + &end, + "--format", + "{start-date}", + "--day-format", + "", + ]) else { + return BTreeSet::new(); + }; + + stdout + .lines() + .filter_map(|line| NaiveDate::parse_from_str(line.trim(), "%d.%m.%Y").ok()) + .filter(|date| date.month() == month) + .map(|date| date.day()) + .collect() +} + +fn build_calendar(year: i32, month: u32, event_days: &BTreeSet, today: NaiveDate) -> String { + let Some(first) = NaiveDate::from_ymd_opt(year, month, 1) else { + return String::new(); + }; + + let month_name = MONTH_NAMES + .get(month as usize) + .copied() + .unwrap_or("Unknown"); + let mut lines = vec![ + format!(r##"{month_name} {year}"##), + r##"Mo Tu We Th Fr Sa Su"##.to_owned(), + ]; + + let mut week = vec![" ".to_owned(); (first.weekday().number_from_monday() - 1) as usize]; + for day in 1..=days_in_month(year, month) { + let day_string = if day == today.day() && month == today.month() && year == today.year() { + format!(r##"{day:2}"##) + } else if event_days.contains(&day) { + format!(r##"{day:2}"##) + } else { + format!(r##"{day:2}"##) + }; + week.push(day_string); + + if week.len() == 7 { + lines.push(week.join(" ")); + week.clear(); + } + } + + if !week.is_empty() { + week.resize(7, " ".to_owned()); + lines.push(week.join(" ")); + } + + lines.join("\n") +} + +fn get_upcoming_events() -> String { + run_khal(&["list", "today", "7d"]).unwrap_or_default() +} + +fn main() { + let now = Local::now(); + let today = now.date_naive(); + let event_days = get_event_days(today.year(), today.month()); + let calendar = build_calendar(today.year(), today.month(), &event_days, today); + let events = get_upcoming_events(); + + let tooltip = if events.is_empty() { + calendar + } else { + format!(r##"{calendar}\n\nUpcoming\n{events}"##) + }; + + println!( + "{}", + json!({ + "text": format!("{:02}:{:02}", now.hour(), now.minute()), + "tooltip": tooltip, + "alt": today.format("%Y-%m-%d").to_string(), + }) + ); +} diff --git a/fedora/mail-counter/.gitignore b/fedora/mail-counter/.gitignore new file mode 100644 index 0000000..b83d222 --- /dev/null +++ b/fedora/mail-counter/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/fedora/mail-counter/Cargo.lock b/fedora/mail-counter/Cargo.lock new file mode 100644 index 0000000..36e96e7 --- /dev/null +++ b/fedora/mail-counter/Cargo.lock @@ -0,0 +1,213 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bitflags" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" + +[[package]] +name = "cc" +version = "1.2.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "libsqlite3-sys" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "133c182a6a2c87864fe97778797e46c7e999672690dc9fa3ee8e241aa4a9c13f" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "mail-counter" +version = "0.1.0" +dependencies = [ + "rusqlite", + "serde_json", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rusqlite" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" +dependencies = [ + "bitflags", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/fedora/mail-counter/Cargo.toml b/fedora/mail-counter/Cargo.toml new file mode 100644 index 0000000..6a5c456 --- /dev/null +++ b/fedora/mail-counter/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "mail-counter" +version = "0.1.0" +edition = "2024" + +[dependencies] +rusqlite = { version = "0.37", features = ["bundled"] } +serde_json = "1" + +[lints.rust] +unsafe_code = "warn" + +[lints.clippy] +all = "warn" +pedantic = "warn" diff --git a/fedora/mail-counter/README.md b/fedora/mail-counter/README.md new file mode 100644 index 0000000..5cc118e --- /dev/null +++ b/fedora/mail-counter/README.md @@ -0,0 +1,18 @@ +# mail-counter + +Waybar custom mail module for Thunderbird. It reads Thunderbird's +`global-messages-db.sqlite` and prints JSON for Waybar. + +## Build On Bazzite + +```bash +cd ~/dotfiles/bazzite/mail-counter +cargo build --release +install -m 755 target/release/mail-counter ~/.config/waybar/scripts/mail +``` + +Waybar config should call the compiled binary: + +```jsonc +"exec": "~/.config/waybar/scripts/mail" +``` diff --git a/fedora/mail-counter/src/main.rs b/fedora/mail-counter/src/main.rs new file mode 100644 index 0000000..e49a56d --- /dev/null +++ b/fedora/mail-counter/src/main.rs @@ -0,0 +1,113 @@ +use std::{ + env, fs, + path::{Path, PathBuf}, +}; + +use rusqlite::{Connection, OpenFlags}; +use serde_json::{Value, json}; + +const MAIL_ICON: &str = "\u{f01ee}"; + +fn thunderbird_profile() -> Option { + let home = PathBuf::from(env::var_os("HOME")?); + let roots = [ + home.join(".var/app/org.mozilla.Thunderbird/.thunderbird"), + home.join(".thunderbird"), + ]; + + roots.iter().find_map(|root| { + let mut profiles = fs::read_dir(root) + .ok()? + .filter_map(Result::ok) + .map(|entry| entry.path()) + .filter(|path| { + path.is_dir() + && path + .file_name() + .and_then(|name| name.to_str()) + .is_some_and(|name| name.ends_with(".default-esr")) + }) + .collect::>(); + profiles.sort(); + profiles.into_iter().next() + }) +} + +fn open_readonly_database(path: &Path) -> rusqlite::Result { + Connection::open_with_flags( + path, + OpenFlags::SQLITE_OPEN_READ_ONLY | OpenFlags::SQLITE_OPEN_NO_MUTEX, + ) +} + +fn unread_from_messages(conn: &Connection) -> rusqlite::Result { + conn.query_row( + r#"SELECT COUNT(*) + FROM messages + WHERE jsonAttributes LIKE '%"read":false%' + OR jsonAttributes LIKE '%"read": false%'"#, + [], + |row| row.get(0), + ) +} + +fn unread_from_folders(conn: &Connection) -> rusqlite::Result { + conn.query_row( + "SELECT SUM(numNewMessages) FROM folderlocations WHERE numNewMessages > 0", + [], + |row| row.get::<_, Option>(0), + ) + .map(Option::unwrap_or_default) +} + +fn unread_count(db_path: &Path) -> i64 { + let Ok(conn) = open_readonly_database(db_path) else { + return 0; + }; + + unread_from_messages(&conn) + .or_else(|_| unread_from_folders(&conn)) + .unwrap_or_default() +} + +fn waybar_json(count: i64) -> Value { + if count > 0 { + json!({ + "text": format!("{MAIL_ICON} {count}"), + "tooltip": format!("{count} unread emails"), + "class": "unread", + }) + } else { + json!({ + "text": MAIL_ICON, + "tooltip": "No unread emails", + }) + } +} + +fn main() { + let Some(profile) = thunderbird_profile() else { + println!( + "{}", + json!({ + "text": "", + "tooltip": "No Thunderbird profile found", + }) + ); + return; + }; + + let db_path = profile.join("global-messages-db.sqlite"); + if !db_path.exists() { + println!( + "{}", + json!({ + "text": "", + "tooltip": "No message database", + }) + ); + return; + } + + println!("{}", waybar_json(unread_count(&db_path))); +} diff --git a/fedora/niri/config.kdl b/fedora/niri/config.kdl new file mode 100644 index 0000000..6c4ae8b --- /dev/null +++ b/fedora/niri/config.kdl @@ -0,0 +1,626 @@ +debug { + // preview-render "screencast" + // preview-render "screen-capture" + // dbus-interfaces-in-non-session-instances + // wait-for-frame-completion-before-queueing + // enable-overlay-planes + disable-cursor-plane + // disable-direct-scanout + // render-drm-device "/dev/dri/renderD129" + // render-drm-device "/dev/dri/card1" + // enable-color-transformations-capability + // emulate-zero-presentation-time +} + +input { + keyboard { + xkb { + + } + + repeat-delay 500 + repeat-rate 30 + + // track-layout "window" + } + + touchpad { + tap + // dwt + // dwtp + natural-scroll + accel-speed 0.4 + // click-method "clickfinger" + // accel-profile "flat" + // tap-button-map "left-middle-right" + } + + mouse { + // natural-scroll + // accel-speed -1.0 + // accel-profile "flat" + } + + tablet { + // map-to-output "eDP-1" + // map-to-output "HDMI-A-1" + } + + // disable-power-key-handling + // warp-mouse-to-focus + // focus-follows-mouse + // workspace-auto-back-and-forth +} + +output "winit" { + // scale 2.0 + // scale 3.0 + // transform "flipped-90" + position x=100 y=200 +} + +output "DP-1" { + // off + + scale 1.0 + // transform "90" + position x=0 y=0 + // mode "1920x1200" + mode "2560x1440@170.001" + // mode "1280x1024" + // mode "1280x720" + // mode "640x480" + + // variable-refresh-rate +} + +layout { + focus-ring { + off + width 2 + // catppuccin-mocha Sapphire + active-color "#74c7ec" + // catppuccin-mocha Surface2 + inactive-color "#585b70" + + // active-gradient from="#80c8ff" to="#bbddff" angle=45 + } + + border { + // off + width 2 + // width 128 + + // active-color "#f38ba8" + // inactive-color "#585b70" + + active-gradient from="#f38ba8" to="#f9e2af" angle=45 relative-to="workspace-view" + inactive-gradient from="#585b70" to="#7f849c" angle=45 relative-to="workspace-view" + + // inactive-gradient from="green" to="lightblue" angle=45 relative-to="workspace-view" + // active-gradient from="#f00" to="#0f0" angle=90 + + // active-gradient from="yellow" to="red" angle=45 relative-to="workspace-view" + // inactive-gradient from="yellow" to="red" angle=45 + } + + preset-column-widths { + proportion 0.3333333333333333 + proportion 0.5 + proportion 0.66667 + // proportion 0.25 + // fixed 1000 + } + + default-column-width { proportion 0.3333333333333333; } + // default-column-width { } + + gaps 4 + + struts { + // left 64 + // right 64 + // top 44 + // bottom 64 + // top 64 + } + + // center-focused-column "on-overflow" + // center-focused-column "always" +} + +cursor { + // xcursor-theme "breeze_cursors" + // xcursor-theme "arstieonart" + // xcursor-size 48 +} + +prefer-no-csd + +// spawn-at-startup "alacritty" "-e" "fish" + +environment { + // GSK_RENDERER "cairo" + // WAYLAND_DISPLAY null +} + +// screenshot-path "~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png" +spawn-at-startup "sh" "-c" "exec swaybg -i ~/Pictures/Wallpapers/raspberry.png -m fill" +spawn-at-startup "waybar" +hotkey-overlay { + // skip-at-startup +} + +animations { + // off + // slowdown 10.0 + // slowdown 4.0 + // slowdown 2.0 + // slowdown 1.5 + + workspace-switch { + // off + // duration-ms 2500 + // curve "ease-out-cubic" + // spring damping-ratio=1.0 stiffness=1000 epsilon=0.0001 + } + + window-open { + // off + // duration-ms 2500 + // curve "ease-out-expo" + // spring damping-ratio=0.8 stiffness=1000 epsilon=0.0001 + } + + window-close { + // off + // duration-ms 2500 + // curve "ease-out-cubic" + // spring damping-ratio=0.8 stiffness=1000 epsilon=0.0001 + } + + horizontal-view-movement { + // off + // duration-ms 500 + // curve "ease-out-cubic" + // spring damping-ratio=1.0 stiffness=20 epsilon=0.00001 + // spring damping-ratio=10.0 stiffness=800 epsilon=0.0001 + } + + window-movement { + // off + // duration-ms 750 + // curve "ease-out-cubic" + // spring damping-ratio=1.0 stiffness=20 epsilon=0.00001 + // spring damping-ratio=0.2 stiffness=800 epsilon=0.0001 + } + + window-resize { + // off + // duration-ms 500 + // duration-ms 2500 + // curve "ease-out-cubic" + // spring damping-ratio=0.2 stiffness=800 epsilon=0.0001 + + custom-shader r" + vec4 resize_color(vec3 coords_curr_geo, vec3 size_curr_geo) { + vec3 coords_next_geo = niri_curr_geo_to_next_geo * coords_curr_geo; + vec3 coords_prev_geo = niri_curr_geo_to_prev_geo * coords_curr_geo; + + vec3 coords_crop = niri_geo_to_tex_next * coords_next_geo; + vec3 coords_stretch = niri_geo_to_tex_next * coords_curr_geo; + vec3 coords_stretch_prev = niri_geo_to_tex_prev * coords_curr_geo; + + // We can crop if the current window size is smaller than the next window + // size. One way to tell is by comparing to 1.0 the X and Y scaling + // coefficients in the current-to-next transformation matrix. + bool can_crop_by_x = niri_curr_geo_to_next_geo[0][0] <= 1.0; + bool can_crop_by_y = niri_curr_geo_to_next_geo[1][1] <= 1.0; + bool crop = can_crop_by_x && can_crop_by_y; + + vec4 color; + + if (crop) { + // However, when we crop, we also want to crop out anything outside the + // current geometry. This is because the area of the shader is unspecified + // and usually bigger than the current geometry, so if we don't fill pixels + // outside with transparency, the texture will leak out. + // + // When stretching, this is not an issue because the area outside will + // correspond to client-side decoration shadows, which are already supposed + // to be outside. + if (coords_curr_geo.x < 0.0 || 1.0 < coords_curr_geo.x || + coords_curr_geo.y < 0.0 || 1.0 < coords_curr_geo.y) { + color = vec4(0.0); + } else { + color = texture2D(niri_tex_next, coords_crop.st); + } + } else { + // If we can't crop, then crossfade. + color = texture2D(niri_tex_next, coords_stretch.st); + vec4 color_prev = texture2D(niri_tex_prev, coords_stretch_prev.st); + color = mix(color_prev, color, niri_clamped_progress); + } + + return color; + } + " + } + + config-notification-open-close { + // off + // duration-ms 250 + // curve "ease-out-cubic" + // spring damping-ratio=0.1 stiffness=1000 epsilon=0.001 + } +} + +window-rule { + // match app-id="Alacritty" + // match app-id="Adwaita" + // opacity 0.9 + // opacity 0.5 + // opacity 0.98 + + // open-on-output "eDP-1" + // default-column-width { proportion 0.5; } + // default-column-width { fixed 400; } + // default-column-width {} + // open-maximized true + // open-fullscreen true + // block-out-from "screencast" + + // min-width 400 + // max-width 400 + // min-height 400 + // max-height 400 + + focus-ring { + // on + // active-gradient from="red" to="green" + // active-color "lightgreen" + // width 1 + } + + border { + // off + // on + // width 12 + // width 0 + // active-color "#ff808080" + // active-color "red" + } + + // draw-border-with-background true + // draw-border-with-background false + + // geometry-corner-radius 12 24 32 64 + // geometry-corner-radius 999 + // geometry-corner-radius 1 + geometry-corner-radius 6 + clip-to-geometry true +} + +/-window-rule { + match app-id="Adwaita" + // opacity 0.5 + block-out-from "screencast" + geometry-corner-radius 12 + clip-to-geometry false + border { + // off + width 12 + } +} + +/-window-rule { + match app-id="terminal" + clip-to-geometry false + geometry-corner-radius 8 8 0 0 + border { + // width 4 + } +} + +/-window-rule { + match app-id="code" + // opacity 0.98 + border { + // off + } +} + +/-window-rule { + match app-id="weston." + // block-out-from "screencast" + geometry-corner-radius 12 24 32 64 + // geometry-corner-radius 999 + clip-to-geometry true +} + +// window-rule { +// match app-id="Alacritty" +// geometry-corner-radius 12 12 0 0 +// border { +// width 2 +// } +// } + +window-rule { + match title="simple-egl" + // min-width 128 + // max-width 128 + // min-height 128 + // max-height 128 + // match is-focused=true + // match is-active=false + // opacity 0.9 + // block-out-from "screencast" + // block-out-from "screen-capture" + // min-width 1000 + draw-border-with-background false + // match app-id="mpv" + // open-on-output "HDMI-A-1" + // open-maximized true + // default-column-width { proportion 0.5; } + // open-fullscreen false +} + +window-rule { + match title="khal-popup" + open-floating true + default-floating-position x=0 y=0 relative-to="top-right" + default-column-width { fixed 600; } + default-window-height { fixed 400; } +} + +window-rule { + match is-active=false + // opacity 0.95 +} + +window-rule { + match app-id=r#"^org\.telegram\.desktop$"# title="^Media viewer$" + open-fullscreen false + default-column-width { proportion 0.5; } +} + +window-rule { + match app-id=r#"^org\.wezfurlong\.wezterm$"# + match app-id="^mpv$" + default-column-width {} +} + +// Qt used by OBS doesn't signal min size yet, so do it manually. +window-rule { + match app-id=r#"^com\.obsproject\.Studio$"# + min-width 876 +} + +window-rule { + match app-id="^blender$" + match app-id="^gimp" + default-column-width { fixed 1200; } +} + +window-rule { + match app-id="^obsidian$" + default-column-width { fixed 1000; } +} + +window-rule { + match app-id=r#"^org\.mozilla\.firefox$"# + match app-id=r#"^org\.telegram\.desktop$"# + exclude app-id=r#"^org\.telegram\.desktop$"# title="^Media viewer$" + + open-on-output "DP-1" +} + +window-rule { + match app-id=r#"^org\.mozilla\.firefox$"# + + default-column-width { proportion 0.66667; } +} + +window-rule { + match app-id=r#"^org\.keepassxc\.KeePassXC$"# + match app-id=r#"^org\.gnome\.World\.Secrets$"# + match app-id=r#"^org\.telegram\.desktop$"# + + // Doesn't quite work: Firefox changes the title one frame early. + match app-id=r#"^org\.mozilla\.firefox$"# title="- Gmail " + match app-id=r#"^org\.mozilla\.firefox$"# title="^Google Calendar " + match app-id=r#"^org\.mozilla\.firefox$"# title="Todoist " + match app-id=r#"^org\.mozilla\.firefox$"# title=r#"^GNOME( \*)? \| "# + match app-id=r#"^org\.mozilla\.firefox$"# title=r#"^Element "# + match app-id=r#"^org\.mozilla\.firefox$"# title=r#"Discord \| "# + + block-out-from "screencast" + // block-out-from "screen-capture" + + border { + // active-color "red" + } +} + +binds { + Mod+Shift+Slash { show-hotkey-overlay; } + + Mod+Shift+C { spawn "sh" "-c" "env DISPLAY=:0 xsel -ob | wl-copy"; } + Mod+Shift+V { spawn "sh" "-c" "wl-paste -n | env DISPLAY=:0 xsel -ib"; } + + // Mod+T { spawn "sh" "-c" "alacritty msg create-window -e fish -C t || alacritty -e fish -C t"; } + Mod+T { spawn "sh" "-c" "alacritty msg create-window || alacritty"; } + Mod+Shift+T { spawn "alacritty"; } + // Mod+Shift+T { spawn "sh" "-c" "alacritty msg create-window || alacritty"; } + // Mod+Return { spawn "~/arst"; } + // Mod+T { spawn "flatpak" "run" "org.gnome.Ptyxis.Devel" "-s"; } + // Mod+T { spawn "~/.local/bin/spawn-ptyxis.sh"; } + // Mod+T { spawn "bash" "-c" "notify-send hello && exec alacritty"; } + // Mod+T { spawn "kgx" "-e" "fish -C t"; } + Mod+D { spawn "fuzzel"; } + Mod+H { spawn "nautilus"; } + Super+Alt+L allow-when-locked=true { spawn "swaylock"; } + + Mod+W { spawn "sh" "-c" "mpv --pause \"$(wl-paste)\""; } + // refresh.sh was a local Bazzite helper; keep these disabled until a Fedora helper exists. + // Super+Alt+H { spawn "refresh.sh" "144"; } + // Super+Alt+Comma { spawn "refresh.sh" "120"; } + + XF86AudioRaiseVolume allow-when-locked=true { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.1+"; } + XF86AudioLowerVolume allow-when-locked=true { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.1-"; } + XF86AudioMute allow-when-locked=true { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SINK@" "toggle"; } + XF86AudioMicMute allow-when-locked=true { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SOURCE@" "toggle"; } + + Mod+Q { close-window; } + Mod+BackSpace { close-window; } + + Mod+M { focus-column-left; } + Mod+N { focus-window-down; } + Mod+E { focus-window-up; } + Mod+I { focus-column-right; } + Mod+Left { focus-column-left; } + Mod+Down { focus-window-down; } + Mod+Up { focus-window-up; } + Mod+Right { focus-column-right; } + + Mod+Ctrl+M { move-column-left; } + Mod+Ctrl+N { move-window-down; } + Mod+Ctrl+E { move-window-up; } + Mod+Ctrl+I { move-column-right; } + Mod+Ctrl+Left { move-column-left; } + Mod+Ctrl+Down { move-window-down; } + Mod+Ctrl+Up { move-window-up; } + Mod+Ctrl+Right { move-column-right; } + + Mod+Home { focus-column-first; } + Mod+End { focus-column-last; } + Mod+Ctrl+Home { move-column-to-first; } + Mod+Ctrl+End { move-column-to-last; } + + Mod+Shift+M { focus-monitor-left; } + Mod+Shift+N { focus-monitor-down; } + Mod+Shift+E { focus-monitor-up; } + Mod+Shift+I { focus-monitor-right; } + Mod+Shift+Left { focus-monitor-left; } + Mod+Shift+Down { focus-monitor-down; } + Mod+Shift+Up { focus-monitor-up; } + Mod+Shift+Right { focus-monitor-right; } + + Mod+Shift+Ctrl+M { move-column-to-monitor-left; } + Mod+Shift+Ctrl+N { move-column-to-monitor-down; } + Mod+Shift+Ctrl+E { move-column-to-monitor-up; } + Mod+Shift+Ctrl+I { move-column-to-monitor-right; } + Mod+Shift+Ctrl+Left { move-column-to-monitor-left; } + Mod+Shift+Ctrl+Down { move-column-to-monitor-down; } + Mod+Shift+Ctrl+Up { move-column-to-monitor-up; } + Mod+Shift+Ctrl+Right { move-column-to-monitor-right; } + + Mod+Shift+Alt+M { move-workspace-to-monitor-left; } + Mod+Shift+Alt+N { move-workspace-to-monitor-down; } + Mod+Shift+Alt+E { move-workspace-to-monitor-up; } + Mod+Shift+Alt+I { move-workspace-to-monitor-right; } + Mod+Shift+Alt+Left { move-workspace-to-monitor-left; } + Mod+Shift+Alt+Down { move-workspace-to-monitor-down; } + Mod+Shift+Alt+Up { move-workspace-to-monitor-up; } + Mod+Shift+Alt+Right { move-workspace-to-monitor-right; } + + Mod+L { focus-workspace-down; } + Mod+U { focus-workspace-up; } + Mod+Page_Down { focus-workspace-down; } + Mod+Page_Up { focus-workspace-up; } + Mod+Ctrl+L { move-column-to-workspace-down; } + Mod+Ctrl+U { move-column-to-workspace-up; } + Mod+Ctrl+Page_Down { move-column-to-workspace-down; } + Mod+Ctrl+Page_Up { move-column-to-workspace-up; } + + Mod+WheelScrollDown cooldown-ms=150 { focus-workspace-down; } + Mod+WheelScrollUp cooldown-ms=150 { focus-workspace-up; } + Mod+Ctrl+WheelScrollDown cooldown-ms=150 { move-column-to-workspace-down; } + Mod+Ctrl+WheelScrollUp cooldown-ms=150 { move-column-to-workspace-up; } + + Mod+WheelScrollRight { focus-column-right; } + Mod+WheelScrollLeft { focus-column-left; } + Mod+Ctrl+WheelScrollRight { move-column-right; } + Mod+Ctrl+WheelScrollLeft { move-column-left; } + + Mod+Shift+WheelScrollDown { focus-column-right; } + Mod+Shift+WheelScrollUp { focus-column-left; } + Mod+Ctrl+Shift+WheelScrollDown { move-column-right; } + Mod+Ctrl+Shift+WheelScrollUp { move-column-left; } + + Mod+TouchpadScrollUp allow-when-locked=true { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.02-"; } + Mod+TouchpadScrollDown allow-when-locked=true { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.02+"; } + // Mod+TouchpadScrollLeft { focus-column-left; } + // Mod+TouchpadScrollRight { focus-column-right; } + + Mod+Shift+L { move-workspace-down; } + Mod+Shift+U { move-workspace-up; } + Mod+Shift+Page_Down { move-workspace-down; } + Mod+Shift+Page_Up { move-workspace-up; } + + Mod+1 { focus-workspace 1; } + Mod+2 { focus-workspace 2; } + Mod+3 { focus-workspace 3; } + Mod+4 { focus-workspace 4; } + Mod+5 { focus-workspace 5; } + Mod+6 { focus-workspace 6; } + Mod+7 { focus-workspace 7; } + Mod+8 { focus-workspace 8; } + Mod+9 { focus-workspace 9; } + Mod+Ctrl+1 { move-column-to-workspace 1; } + Mod+Ctrl+2 { move-column-to-workspace 2; } + Mod+Ctrl+3 { move-column-to-workspace 3; } + Mod+Ctrl+4 { move-column-to-workspace 4; } + Mod+Ctrl+5 { move-column-to-workspace 5; } + Mod+Ctrl+6 { move-column-to-workspace 6; } + Mod+Ctrl+7 { move-column-to-workspace 7; } + Mod+Ctrl+8 { move-column-to-workspace 8; } + Mod+Ctrl+9 { move-column-to-workspace 9; } + + // Mod+Tab { focus-workspace-previous; } + + Mod+Comma { consume-window-into-column; } + Mod+Period { expel-window-from-column; } + + Mod+BracketLeft { consume-or-expel-window-left; } + Mod+BracketRight { consume-or-expel-window-right; } + + Mod+R { switch-preset-column-width; } + Mod+F { maximize-column; } + Mod+Shift+F { fullscreen-window; } + Mod+C { center-column; } + + Mod+Minus { set-column-width "-10%"; } + Mod+Equal { set-column-width "+10%"; } + Mod+Ctrl+Minus { set-column-width "-1"; } + Mod+Ctrl+Equal { set-column-width "+1"; } + + Mod+Shift+Minus { set-window-height "-10%"; } + Mod+Shift+Equal { set-window-height "+10%"; } + Mod+Shift+Ctrl+Minus { set-window-height "-1"; } + Mod+Shift+Ctrl+Equal { set-window-height "+1"; } + + Mod+0 { set-column-width "960"; } + Mod+Shift+0 { set-window-height "540"; } + Mod+Alt+0 { set-column-width "1280"; } + Mod+Shift+Alt+0 { set-window-height "720"; } + Mod+Ctrl+0 { set-column-width "1920"; } + Mod+Shift+Ctrl+0 { set-window-height "1080"; } + + Mod+Space { switch-layout "next"; } + Mod+Shift+Space { switch-layout "prev"; } + + Print { screenshot; } + Ctrl+Print { screenshot-screen; } + Alt+Print { screenshot-window; } + + Mod+P { screenshot; } + + Mod+Shift+Y { quit; } + Mod+Shift+P { power-off-monitors; } + + Mod+Shift+Ctrl+T { toggle-debug-tint; } + Mod+Shift+Ctrl+O { debug-toggle-opaque-regions; } + Mod+Shift+Ctrl+D { debug-toggle-damage; } +} diff --git a/fedora/screenshots/waybar-icons-fixed.png b/fedora/screenshots/waybar-icons-fixed.png new file mode 100644 index 0000000..bbb94e6 Binary files /dev/null and b/fedora/screenshots/waybar-icons-fixed.png differ diff --git a/fedora/wallpapers/raspberry.png b/fedora/wallpapers/raspberry.png new file mode 100644 index 0000000..cf7b725 Binary files /dev/null and b/fedora/wallpapers/raspberry.png differ diff --git a/fedora/waybar/config.jsonc b/fedora/waybar/config.jsonc new file mode 100644 index 0000000..1d309af --- /dev/null +++ b/fedora/waybar/config.jsonc @@ -0,0 +1,179 @@ +// -*- mode: jsonc -*- +{ + "layer": "top", + "height": 32, + "spacing": 8, + + "modules-left": [ + "niri/workspaces" + ], + "modules-center": [ + "niri/window" + ], + "modules-right": [ + "privacy", + "custom/mail", + "custom/weather", + "pulseaudio", + "network", + "niri/language", + "power-profiles-daemon", + "cpu", + "memory", + "temperature", + "custom/gpu", + "disk", + "custom/calendar", + "tray" + ], + + "niri/workspaces": { + "format": "{index}" + }, + + "niri/window": { + "format": "{}", + "max-length": 50, + "align": 0.5, + "justify": "center" + }, + + "niri/language": { + "format": "{short}", + "align": 0.5, + "justify": "center" + }, + + "privacy": { + "icon-spacing": 4, + "icon-size": 16, + "transition-duration": 250, + "modules": [ + { "type": "screenshare" }, + { "type": "audio-in" }, + { "type": "audio-out" } + ] + }, + + "custom/mail": { + "format": "{}", + "return-type": "json", + "exec": "~/.config/waybar/scripts/mail", + "interval": 60, + "tooltip": true, + "on-click": "thunderbird", + "align": 0.5, + "justify": "center" + }, + + "custom/weather": { + "format": "{}", + "return-type": "json", + "exec": "~/.config/waybar/scripts/weather.sh", + "interval": 900, + "tooltip": true, + "align": 0.5, + "justify": "center" + }, + + "custom/gpu": { + "format": "{}", + "return-type": "json", + "exec": "~/.config/waybar/scripts/gpu.sh", + "interval": 5, + "tooltip": true, + "align": 0.5, + "justify": "center" + }, + + "disk": { + "format": " {percentage_used}%", + "path": "/", + "tooltip-format": "{used} / {total} ({percentage_used}%)", + "align": 0.5, + "justify": "center" + }, + + "tray": { + "spacing": 10 + }, + + "custom/calendar": { + "format": " {}", + "return-type": "json", + "exec": "~/.config/waybar/scripts/khal-calendar", + "interval": 30, + "tooltip": true, + "on-click": "alacritty --title khal-popup -e bash -c 'vdirsyncer sync 2>/dev/null; ikhal'", + "align": 0.5, + "justify": "center" + }, + + "cpu": { + "format": " {usage}%", + "tooltip": false, + "align": 0.5, + "justify": "center" + }, + + "memory": { + "format": " {}%", + "align": 0.5, + "justify": "center" + }, + + "temperature": { + "critical-threshold": 80, + "format": " {temperatureC}°C", + "align": 0.5, + "justify": "center" + }, + + "network": { + "format-wifi": "{icon}", + "format-ethernet": "", + "format-icons": ["󰤯", "󰤟", "󰤢", "󰤥", "󰤨"], + "tooltip-format-wifi": "{essid} ({signalStrength}%)\n{ipaddr}/{cidr}", + "tooltip-format-ethernet": "{ifname}\n{ipaddr}/{cidr}", + "format-linked": "{ifname} (No IP) ", + "format-disconnected": "󰤭", + "on-click": "", + "align": 0.5, + "justify": "center" + }, + + "pulseaudio": { + "format": "{icon} {volume}% {format_source}", + "format-bluetooth": "{icon} {volume}% {format_source}", + "format-bluetooth-muted": " {format_source}", + "format-muted": " {format_source}", + "format-source": "{volume}%", + "format-source-muted": "", + "format-icons": { + "headphone": "", + "hands-free": "", + "headset": "", + "phone": "", + "portable": "", + "car": "", + "default": ["", "", ""] + }, + "on-click": "pavucontrol", + "align": 0.5, + "justify": "center" + }, + + "power-profiles-daemon": { + "format": "{icon}", + "tooltip-format": "Power profile: {profile}\nDriver: {driver}", + "tooltip": true, + "align": 0.5, + "justify": "center", + "format-icons": { + "default": "", + "performance": "", + "balanced": "", + "power-saver": "" + } + } +} diff --git a/fedora/waybar/scripts/gpu.sh b/fedora/waybar/scripts/gpu.sh new file mode 100755 index 0000000..fb870d1 --- /dev/null +++ b/fedora/waybar/scripts/gpu.sh @@ -0,0 +1,9 @@ +#!/bin/bash +data=$(nvidia-smi --query-gpu=utilization.gpu,temperature.gpu --format=csv,noheader,nounits 2>/dev/null) +if [ -z "$data" ]; then + echo '{"text": "GPU N/A", "tooltip": "nvidia-smi not available"}' + exit 0 +fi +usage=$(echo "$data" | cut -d',' -f1 | tr -d ' ') +temp=$(echo "$data" | cut -d',' -f2 | tr -d ' ') +echo "{\"text\": \"GPU ${usage}% ${temp}°C\", \"tooltip\": \"GPU: ${usage}%\\nTemp: ${temp}°C\", \"class\": \"\"}" diff --git a/fedora/waybar/scripts/weather.sh b/fedora/waybar/scripts/weather.sh new file mode 100755 index 0000000..e37ecd7 --- /dev/null +++ b/fedora/waybar/scripts/weather.sh @@ -0,0 +1,8 @@ +#!/bin/bash +weather=$(curl -s "wttr.in/?format=%c%t" 2>/dev/null) +if [ -z "$weather" ] || echo "$weather" | grep -q "Unknown"; then + echo '{"text": "N/A", "tooltip": "Weather unavailable"}' + exit 0 +fi +tooltip=$(curl -s "wttr.in/?format=%l:+%C+%t+%w+%h" 2>/dev/null) +echo "{\"text\": \"${weather}\", \"tooltip\": \"${tooltip}\"}" diff --git a/fedora/waybar/style.css b/fedora/waybar/style.css new file mode 100644 index 0000000..4cffa06 --- /dev/null +++ b/fedora/waybar/style.css @@ -0,0 +1,262 @@ +/* Catppuccin Latte */ +@define-color rosewater #dc8a78; +@define-color flamingo #dd7878; +@define-color pink #ea76cb; +@define-color mauve #8839ef; +@define-color red #d20f39; +@define-color maroon #e64553; +@define-color peach #fe640b; +@define-color yellow #df8e1d; +@define-color green #40a02b; +@define-color teal #179299; +@define-color sky #04a5e5; +@define-color sapphire #209fb5; +@define-color blue #1e66f5; +@define-color lavender #7287fd; +@define-color text #4c4f69; +@define-color subtext1 #5c5f77; +@define-color subtext0 #6c6f85; +@define-color overlay2 #7c7f93; +@define-color overlay1 #8c8fa1; +@define-color overlay0 #9ca0b0; +@define-color surface2 #acb0be; +@define-color surface1 #bcc0cc; +@define-color surface0 #ccd0da; +@define-color base #eff1f5; +@define-color mantle #e6e9ef; +@define-color crust #dce0e8; + +* { + font-family: 'Noto Sans Mono', 'Symbols Nerd Font Mono', 'Symbols Nerd Font', 'Font Awesome 6 Free', monospace; + font-size: 15px; + font-weight: bold; +} + +window#waybar { + background-color: rgba(30, 30, 46, 0.72); + color: @text; + transition-property: background-color; + transition-duration: .5s; +} + +window#waybar.hidden { + opacity: 0.2; +} + +button { + box-shadow: inset 0 -3px transparent; + border: none; + border-radius: 16px; +} + +button:hover { + background: inherit; + box-shadow: inset 0 -2px @blue; +} + +/* Workspaces */ +#workspaces button { + min-height: 24px; + padding: 4px 8px; + background-color: #000000; + color: @overlay2; + border-radius: 16px; +} + +#workspaces button:hover { + background: #000000; +} + +#workspaces button.active { + color: @blue; + box-shadow: inset 0 -2px @blue; +} + +#workspaces button.urgent { + background-color: #000000; + color: @base; +} + +/* Common module styling */ +#window, +#custom-calendar, +#cpu, +#memory, +#disk, +#temperature, +#network, +#pulseaudio, +#wireplumber, +#tray, +#power-profiles-daemon, +#language, +#custom-gpu, +#custom-weather, +#privacy { + min-height: 24px; + padding: 4px 8px; + background-color: #000000; + border-radius: 16px; + color: @text; +} + +#workspaces button label, +#window label, +#custom-calendar label, +#cpu label, +#memory label, +#disk label, +#temperature label, +#network label, +#pulseaudio label, +#wireplumber label, +#tray label, +#power-profiles-daemon label, +#language label, +#custom-gpu label, +#custom-weather label, +#privacy label { + min-height: 24px; + padding: 0; + margin: 0; +} + +#window, +#workspaces { + margin: 0 4px; +} + +#window { + color: @text; +} + +/* Calendar/Clock */ +#custom-calendar { + color: @blue; + font-weight: bold; +} + +/* CPU */ +#cpu { + color: @green; +} + +/* Memory */ +#memory { + color: @mauve; +} + +/* Temperature */ +#temperature { + color: @peach; +} + +#temperature.critical { + color: @red; +} + +/* Network */ +#network { + color: @sapphire; + font-size: 20px; +} + +#network.disconnected { + color: @red; +} + +/* Pulseaudio */ +#pulseaudio { + color: @yellow; +} + +#pulseaudio.muted { + color: @overlay1; +} + +/* Language */ +#language { + color: @teal; + font-weight: bold; +} + +/* Power profiles */ +#power-profiles-daemon { + color: @subtext0; +} + +#power-profiles-daemon.performance { + color: @red; +} + +#power-profiles-daemon.balanced { + color: @blue; +} + +#power-profiles-daemon.power-saver { + color: @green; +} + +/* Mail */ +#custom-mail { + color: @lavender; +} + +#custom-mail.unread { + color: @red; +} + +/* Disk */ +#disk { + color: @flamingo; +} + +/* GPU */ +#custom-gpu { + color: @green; +} + +/* Weather */ +#custom-weather { + color: @sky; +} + +/* Privacy */ +#privacy { + background-color: transparent; + padding: 0; +} + +#privacy-item { + min-height: 24px; + padding: 4px 8px; + color: @base; + background-color: #000000; + border-radius: 16px; +} + +#privacy-item.screenshare { + background-color: #000000; +} + +#privacy-item.audio-in { + background-color: #000000; +} + +#privacy-item.audio-out { + background-color: #000000; +} + +/* Tray */ +#tray { + color: @text; +} + +#tray > .passive { + -gtk-icon-effect: dim; +} + +#tray > .needs-attention { + -gtk-icon-effect: highlight; + color: @red; +} diff --git a/fedora/xsettingsd/xsettingsd.conf b/fedora/xsettingsd/xsettingsd.conf new file mode 100644 index 0000000..327b162 --- /dev/null +++ b/fedora/xsettingsd/xsettingsd.conf @@ -0,0 +1,17 @@ +Gdk/UnscaledDPI 98304 +Gdk/WindowScalingFactor 1 +Gtk/EnableAnimations 1 +Gtk/DecorationLayout "icon:minimize,maximize,close" +Net/ThemeName "" +Gtk/PrimaryButtonWarpsSlider 1 +Gtk/ToolbarStyle 3 +Gtk/MenuImages 1 +Gtk/ButtonImages 1 +Net/CursorBlinkTime 1000 +Net/CursorBlink 1 +Gtk/CursorThemeSize 24 +Gtk/CursorThemeName "breeze_cursors" +Net/SoundThemeName "ocean" +Net/IconThemeName "breeze" +Gtk/FontName "Noto Sans, 10" + diff --git a/nvim/lua/config/keymaps.lua b/nvim/lua/config/keymaps.lua index a3f79cb..dfd3c80 100644 --- a/nvim/lua/config/keymaps.lua +++ b/nvim/lua/config/keymaps.lua @@ -18,7 +18,16 @@ map("n", "", ":vertical resize +2", { desc = "Increase window width -- Buffers map("n", "", ":bnext", { desc = "Next buffer" }) map("n", "", ":bprevious", { desc = "Previous buffer" }) -map("n", "bd", ":bdelete", { desc = "Delete buffer" }) +map("n", "bd", function() + local current = vim.api.nvim_get_current_buf() + local bufs = vim.tbl_filter(function(b) + return b.bufnr ~= current and vim.bo[b.bufnr].filetype ~= "neo-tree" + end, vim.fn.getbufinfo({ buflisted = 1 })) + if #bufs > 0 then + vim.api.nvim_set_current_buf(bufs[1].bufnr) + end + vim.cmd("bdelete " .. current) +end, { desc = "Delete buffer" }) -- Move lines up/down in visual mode map("v", "J", ":m '>+1gv=gv", { desc = "Move selection down" }) diff --git a/nvim/lua/plugins/editor.lua b/nvim/lua/plugins/editor.lua index 8a1d8d1..752a872 100644 --- a/nvim/lua/plugins/editor.lua +++ b/nvim/lua/plugins/editor.lua @@ -47,6 +47,14 @@ return { { "n", "Neotree toggle", desc = "Toggle file explorer" }, }, opts = { + window = { + mappings = { + ["l"] = "open", + ["h"] = "close_node", + [""] = "open", + [""] = "close_node", + }, + }, filesystem = { filtered_items = { visible = true, diff --git a/nvim/lua/plugins/lualine.lua b/nvim/lua/plugins/lualine.lua index 5570bf7..d525762 100644 --- a/nvim/lua/plugins/lualine.lua +++ b/nvim/lua/plugins/lualine.lua @@ -1,24 +1,23 @@ +local layout = "EN" + +local function update_layout() + vim.system({ "im-select" }, { text = true }, function(result) + if result.stdout then + layout = result.stdout:find("Russian") and "RU" or "EN" + end + end) +end + +-- Update layout in background every second, never blocks UI +local timer = vim.loop.new_timer() +timer:start(0, 1000, vim.schedule_wrap(update_layout)) + local function keyboard_layout() - local handle = io.popen("im-select") - if not handle then - return "" - end - local result = handle:read("*a") - handle:close() - if result:find("Russian") then - return "RU" - end - return "EN" + return layout end local function keyboard_layout_color() - local handle = io.popen("im-select") - if not handle then - return { bg = "#a6e3a1", fg = "#1e1e2e", gui = "bold" } - end - local result = handle:read("*a") - handle:close() - if result:find("Russian") then + if layout == "RU" then return { bg = "#f38ba8", fg = "#1e1e2e", gui = "bold" } end return { bg = "#a6e3a1", fg = "#1e1e2e", gui = "bold" }