Compare commits
8 Commits
codex/hypr
...
hyprland-l
| Author | SHA1 | Date | |
|---|---|---|---|
| 75c29eb12b | |||
| c194e91dc1 | |||
| b52b2bcb57 | |||
| ddb77a5a52 | |||
| edbe9603f0 | |||
| 8d1d78dc25 | |||
| 3eb05515a1 | |||
| 016e0aadfe |
18
.github/workflows/cachix.yml
vendored
18
.github/workflows/cachix.yml
vendored
@@ -1,20 +1,22 @@
|
|||||||
name: Build and Push Cachix (imalison-taffybar)
|
name: Build and Push Cachix (NixOS)
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [master]
|
branches: [master]
|
||||||
paths:
|
paths:
|
||||||
- "dotfiles/config/taffybar/**"
|
- "nixos/**"
|
||||||
|
- "org-agenda-api/**"
|
||||||
- ".github/workflows/cachix.yml"
|
- ".github/workflows/cachix.yml"
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [master]
|
branches: [master]
|
||||||
paths:
|
paths:
|
||||||
- "dotfiles/config/taffybar/**"
|
- "nixos/**"
|
||||||
|
- "org-agenda-api/**"
|
||||||
- ".github/workflows/cachix.yml"
|
- ".github/workflows/cachix.yml"
|
||||||
workflow_dispatch: {}
|
workflow_dispatch: {}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
imalison-taffybar:
|
nixos-strixi-minaj:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
@@ -49,6 +51,9 @@ jobs:
|
|||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: DeterminateSystems/nix-installer-action@v16
|
uses: DeterminateSystems/nix-installer-action@v16
|
||||||
|
|
||||||
|
- name: Use GitHub Actions Cache for /nix/store
|
||||||
|
uses: DeterminateSystems/magic-nix-cache-action@v7
|
||||||
|
|
||||||
- name: Require Cachix config (push only)
|
- name: Require Cachix config (push only)
|
||||||
if: github.event_name == 'push'
|
if: github.event_name == 'push'
|
||||||
env:
|
env:
|
||||||
@@ -80,10 +85,11 @@ jobs:
|
|||||||
name: ${{ vars.CACHIX_CACHE_NAME }}
|
name: ${{ vars.CACHIX_CACHE_NAME }}
|
||||||
skipPush: true
|
skipPush: true
|
||||||
|
|
||||||
- name: Build imalison-taffybar
|
- name: Build NixOS system (strixi-minaj)
|
||||||
run: |
|
run: |
|
||||||
set -euxo pipefail
|
set -euxo pipefail
|
||||||
nix build \
|
nix build \
|
||||||
--no-link \
|
--no-link \
|
||||||
--print-build-logs \
|
--print-build-logs \
|
||||||
./dotfiles/config/taffybar#defaultPackage.x86_64-linux
|
./nixos#nixosConfigurations.strixi-minaj.config.system.build.toplevel \
|
||||||
|
--override-input railbird-secrets ./nixos/ci/railbird-secrets-stub
|
||||||
|
|||||||
228
README.org
228
README.org
@@ -1,228 +0,0 @@
|
|||||||
# -*- mode: org; -*-
|
|
||||||
#+TITLE: colonelpanic8's Dotfiles
|
|
||||||
|
|
||||||
This repository is the source of truth for my machines, user environment, and a
|
|
||||||
large set of day-to-day workflow scripts. It started as an Emacs configuration,
|
|
||||||
and that is still here, but the repo is now mostly a Nix-managed personal
|
|
||||||
systems repo: NixOS hosts, a nix-darwin host, Home Manager link management,
|
|
||||||
desktop/window-manager configuration, shell tooling, agent configuration, and
|
|
||||||
org-agenda-api deployment glue.
|
|
||||||
|
|
||||||
The old literate Emacs README lives at [[file:dotfiles/emacs.d/README.org][dotfiles/emacs.d/README.org]]. The
|
|
||||||
published GitHub Pages site is still generated from that document.
|
|
||||||
|
|
||||||
* What This Manages
|
|
||||||
|
|
||||||
- NixOS systems under [[file:nixos/][nixos/]], with one flake configuration per file in
|
|
||||||
[[file:nixos/machines/][nixos/machines/]].
|
|
||||||
- A nix-darwin configuration under [[file:nix-darwin/][nix-darwin/]] for the macOS machine.
|
|
||||||
- Shared Nix modules and overlays under [[file:nix-shared/][nix-shared/]].
|
|
||||||
- Home Manager placement of files from [[file:dotfiles/][dotfiles/]] into =$HOME= and
|
|
||||||
=$XDG_CONFIG_HOME=.
|
|
||||||
- Shell functions and executable helpers in [[file:dotfiles/lib/][dotfiles/lib/]], added to
|
|
||||||
=PATH= and =fpath= by the NixOS environment module.
|
|
||||||
- Desktop environment and tiling-window-manager configuration for Hyprland,
|
|
||||||
XMonad, River/XMonad experiments, Taffybar, Waybar, Rofi, Alacritty,
|
|
||||||
autorandr, and related utilities.
|
|
||||||
- Emacs and org-mode configuration, including the tangled org configuration used
|
|
||||||
by the org-agenda-api container.
|
|
||||||
- Agent and tool configuration for Codex, Claude, project guides, and local
|
|
||||||
task-specific skills.
|
|
||||||
- Container and deployment configuration for personal org-agenda-api instances.
|
|
||||||
|
|
||||||
This is not intended to be a generic starter dotfiles repo. Many modules assume
|
|
||||||
my users, hostnames, hardware, SSH keys, secrets layout, and local checkout path
|
|
||||||
=(~/dotfiles=). It is still useful as a reference for how the pieces fit
|
|
||||||
together.
|
|
||||||
|
|
||||||
* Layout
|
|
||||||
|
|
||||||
| Path | Purpose |
|
|
||||||
|------+---------|
|
|
||||||
| [[file:nixos/][nixos/]] | Main NixOS flake. Imports feature modules, host files, agenix secrets, Home Manager, overlays, and package checks. |
|
|
||||||
| [[file:nixos/machines/][nixos/machines/]] | Per-host NixOS entrypoints such as =strixi-minaj=, =ryzen-shine=, =railbird-sf=, WSL hosts, and Raspberry Pi hosts. |
|
|
||||||
| [[file:nix-darwin/][nix-darwin/]] | macOS system flake using nix-darwin, nix-homebrew, Home Manager, agenix, and shared packages. |
|
|
||||||
| [[file:nix-shared/][nix-shared/]] | Shared package lists, overlays, Home Manager modules, and Syncthing fragments used by Linux and macOS. |
|
|
||||||
| [[file:dotfiles/][dotfiles/]] | Files that are linked into the home directory. Top-level entries become dotfiles; =dotfiles/config/*= becomes XDG config. |
|
|
||||||
| [[file:dotfiles/lib/bin/][dotfiles/lib/bin/]] | User commands and desktop helpers, including Rofi scripts, Hyprland helpers, audio controls, and Syncthing utilities. |
|
|
||||||
| [[file:dotfiles/lib/functions/][dotfiles/lib/functions/]] | Zsh autoload functions and shell helpers. |
|
|
||||||
| [[file:dotfiles/config/hypr/][dotfiles/config/hypr/]] | Hyprland Lua config, lock/idle config, workspace files, scripts, and plugin state. |
|
|
||||||
| [[file:dotfiles/config/xmonad/][dotfiles/config/xmonad/]] | XMonad configuration, local Cabal package, flake, and upstream submodules. |
|
|
||||||
| [[file:dotfiles/config/taffybar/][dotfiles/config/taffybar/]] | Personal Taffybar package/configuration, CSS themes, scripts, and local upstream checkout. |
|
|
||||||
| [[file:dotfiles/emacs.d/][dotfiles/emacs.d/]] | Emacs configuration, literate org config, org-mode setup, snippets, and generated/tangled Elisp. |
|
|
||||||
| [[file:dotfiles/agents/][dotfiles/agents/]] | Agent instructions, project constellation guides, and local Codex skills. |
|
|
||||||
| [[file:org-agenda-api/][org-agenda-api/]] | Instance-specific config and container/deploy glue for org-agenda-api. |
|
|
||||||
| [[file:docs/][docs/]] | Design notes for Cachix, tiling WM behavior, River evaluation, and org-agenda-api consolidation. |
|
|
||||||
| [[file:gen-gh-pages/][gen-gh-pages/]] | Legacy/publication pipeline that exports the Emacs README to GitHub Pages. |
|
|
||||||
|
|
||||||
* NixOS
|
|
||||||
|
|
||||||
The NixOS flake is [[file:nixos/flake.nix][nixos/flake.nix]]. It discovers host configurations from
|
|
||||||
[[file:nixos/machines/][nixos/machines/]] and exposes them as =nixosConfigurations.<hostname>=.
|
|
||||||
The broad feature set is assembled by [[file:nixos/configuration.nix][nixos/configuration.nix]], where
|
|
||||||
=features.full.enable= expands into the normal desktop/profile modules.
|
|
||||||
|
|
||||||
Common workflow:
|
|
||||||
|
|
||||||
#+begin_src sh
|
|
||||||
cd ~/dotfiles/nixos
|
|
||||||
just switch
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
The local =just switch= recipe wraps =nixos-rebuild switch --flake ".#"=, waits
|
|
||||||
for an already-running switch to finish, and overrides the Taffybar inputs to
|
|
||||||
the live checkout under this repo. Use it instead of running =nixos-rebuild=
|
|
||||||
directly.
|
|
||||||
|
|
||||||
Useful variants:
|
|
||||||
|
|
||||||
#+begin_src sh
|
|
||||||
cd ~/dotfiles/nixos
|
|
||||||
just switch-remote
|
|
||||||
just switch-local-taffybar
|
|
||||||
just remote-switch <host>
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
Build/check examples:
|
|
||||||
|
|
||||||
#+begin_src sh
|
|
||||||
nix flake check ~/dotfiles/nixos
|
|
||||||
nix build ~/dotfiles/nixos#nixosConfigurations.strixi-minaj.config.system.build.toplevel
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
The flake also exposes package/check outputs for Hyprland plugins and a
|
|
||||||
Hyprland Lua config syntax/verification check.
|
|
||||||
|
|
||||||
* nix-darwin
|
|
||||||
|
|
||||||
The macOS configuration lives in [[file:nix-darwin/flake.nix][nix-darwin/flake.nix]]. It uses
|
|
||||||
nix-darwin, nix-homebrew, Home Manager, agenix, and the shared package list in
|
|
||||||
[[file:nix-shared/system/essential.nix][nix-shared/system/essential.nix]].
|
|
||||||
|
|
||||||
Common workflow:
|
|
||||||
|
|
||||||
#+begin_src sh
|
|
||||||
cd ~/dotfiles/nix-darwin
|
|
||||||
just switch
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
The active host configuration is =mac-demarco-mini=. There is also a
|
|
||||||
=mac-demarco-mini-imalison= target used while migrating the primary macOS user.
|
|
||||||
|
|
||||||
* Home File Linking
|
|
||||||
|
|
||||||
The NixOS Home Manager module [[file:nixos/dotfiles-links.nix][nixos/dotfiles-links.nix]] reproduces the useful
|
|
||||||
part of =rcm/rcup=:
|
|
||||||
|
|
||||||
- files under [[file:dotfiles/][dotfiles/]] are linked into =$HOME= with a leading dot;
|
|
||||||
- directories under [[file:dotfiles/config/][dotfiles/config/]] are linked into =$XDG_CONFIG_HOME=;
|
|
||||||
- links are out-of-store symlinks, so editing the checkout updates runtime
|
|
||||||
config immediately;
|
|
||||||
- generated or special directories such as =codex=, =lib=, =config=, and
|
|
||||||
=emacs.d= are handled separately.
|
|
||||||
|
|
||||||
On NixOS, shell scripts belong in [[file:dotfiles/lib/bin/][dotfiles/lib/bin/]] and autoloaded shell
|
|
||||||
functions belong in [[file:dotfiles/lib/functions/][dotfiles/lib/functions/]]. [[file:nixos/environment.nix][nixos/environment.nix]] adds
|
|
||||||
those paths to the shell environment.
|
|
||||||
|
|
||||||
The nix-darwin Home Manager module in [[file:nix-darwin/home/common.nix][nix-darwin/home/common.nix]] uses the
|
|
||||||
same basic idea for macOS, with extra launchd, GPG, Raycast, Homebrew, and agent
|
|
||||||
setup.
|
|
||||||
|
|
||||||
* Desktop Stack
|
|
||||||
|
|
||||||
The desktop setup is modular. [[file:nixos/desktop.nix][nixos/desktop.nix]] enables the common desktop
|
|
||||||
surface, while individual modules layer in window managers, panels, launchers,
|
|
||||||
notifications, SNI/tray support, fonts, and app defaults.
|
|
||||||
|
|
||||||
The currently important pieces are:
|
|
||||||
|
|
||||||
- Hyprland configuration in [[file:dotfiles/config/hypr/hyprland.lua][dotfiles/config/hypr/hyprland.lua]], with imported Lua
|
|
||||||
modules under [[file:dotfiles/config/hypr/hyprland/][dotfiles/config/hypr/hyprland/]], backed by custom plugin inputs in
|
|
||||||
the NixOS flake.
|
|
||||||
- XMonad configuration in [[file:dotfiles/config/xmonad/xmonad.hs][dotfiles/config/xmonad/xmonad.hs]], with upstream
|
|
||||||
=xmonad= and =xmonad-contrib= available as submodules/checkouts.
|
|
||||||
- Taffybar configuration in [[file:dotfiles/config/taffybar/taffybar.hs][dotfiles/config/taffybar/taffybar.hs]], plus a local
|
|
||||||
flake and scripts for restart, screenshots, and SNI debugging.
|
|
||||||
- Waybar, Rofi, autorandr, Alacritty, Zellij, and miscellaneous app configs
|
|
||||||
under [[file:dotfiles/config/][dotfiles/config/]].
|
|
||||||
|
|
||||||
The intended tiling-WM behavior is documented in
|
|
||||||
[[file:docs/tiling-wm-experience.md][docs/tiling-wm-experience.md]].
|
|
||||||
|
|
||||||
* Emacs And Org
|
|
||||||
|
|
||||||
Emacs is still a major part of the repo, just no longer the only thing here.
|
|
||||||
The main files are:
|
|
||||||
|
|
||||||
- [[file:dotfiles/emacs.d/README.org][dotfiles/emacs.d/README.org]]: the original literate Emacs README.
|
|
||||||
- [[file:dotfiles/emacs.d/init.el][dotfiles/emacs.d/init.el]] and [[file:dotfiles/emacs.d/early-init.el][early-init.el]]: runtime entrypoints.
|
|
||||||
- [[file:dotfiles/emacs.d/org-config.org][dotfiles/emacs.d/org-config.org]]: the org-mode configuration that is tangled
|
|
||||||
for normal Emacs and for org-agenda-api.
|
|
||||||
- [[file:gen-gh-pages/][gen-gh-pages/]] and [[file:.github/workflows/gh-pages.yml][.github/workflows/gh-pages.yml]]: export the Emacs README
|
|
||||||
to the public GitHub Pages site.
|
|
||||||
|
|
||||||
* org-agenda-api
|
|
||||||
|
|
||||||
The repo carries the personal integration layer for
|
|
||||||
[[https://github.com/colonelpanic8/org-agenda-api][org-agenda-api]].
|
|
||||||
[[file:nixos/org-agenda-api.nix][nixos/org-agenda-api.nix]] tangles the org-mode configuration from
|
|
||||||
[[file:dotfiles/emacs.d/org-config.org][dotfiles/emacs.d/org-config.org]]. [[file:org-agenda-api/container.nix][org-agenda-api/container.nix]] combines that
|
|
||||||
tangled config with per-instance loaders under [[file:org-agenda-api/configs/][org-agenda-api/configs/]] and
|
|
||||||
builds OCI containers exposed by the NixOS flake.
|
|
||||||
|
|
||||||
The host-side NixOS module [[file:nixos/org-agenda-api-host.nix][nixos/org-agenda-api-host.nix]] runs the container
|
|
||||||
behind nginx with ACME certificates and Podman.
|
|
||||||
|
|
||||||
To enter the deployment shell:
|
|
||||||
|
|
||||||
#+begin_src sh
|
|
||||||
nix develop ~/dotfiles/nixos#org-agenda-api
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
* Secrets
|
|
||||||
|
|
||||||
Secrets are intentionally not stored as plaintext in the repo. Nix-managed
|
|
||||||
secrets use agenix files under [[file:nixos/secrets/][nixos/secrets/]]. Runtime credentials and
|
|
||||||
personal service passwords live in =pass=. Modules and scripts should consume
|
|
||||||
secrets from those sources at runtime rather than checking derived values into
|
|
||||||
git.
|
|
||||||
|
|
||||||
* Submodules And Local Checkouts
|
|
||||||
|
|
||||||
Some third-party or upstream projects are tracked as submodules:
|
|
||||||
|
|
||||||
- =dotfiles/config/taffybar/taffybar=
|
|
||||||
- =dotfiles/config/xmonad/xmonad=
|
|
||||||
- =dotfiles/config/xmonad/xmonad-contrib=
|
|
||||||
- =dotfiles/config/alacritty/themes=
|
|
||||||
- =nixos/railbird.ai=
|
|
||||||
|
|
||||||
Clone with submodules when bootstrapping a new checkout:
|
|
||||||
|
|
||||||
#+begin_src sh
|
|
||||||
git clone --recurse-submodules git@github.com:IvanMalison/dotfiles.git ~/dotfiles
|
|
||||||
#+end_src
|
|
||||||
|
|
||||||
This repo also contains project-local git worktrees under =.worktrees/= during
|
|
||||||
active development. Those are machine-local working state and are ignored.
|
|
||||||
|
|
||||||
* CI And Caches
|
|
||||||
|
|
||||||
[[file:.github/workflows/cachix.yml][.github/workflows/cachix.yml]] can build the =strixi-minaj= NixOS closure and
|
|
||||||
push paths to Cachix.
|
|
||||||
|
|
||||||
The top-level [[file:justfile][justfile]] contains helper commands for populating the
|
|
||||||
=colonelpanic8-dotfiles= Cachix cache from a local machine.
|
|
||||||
|
|
||||||
* Working In This Repo
|
|
||||||
|
|
||||||
- Prefer Nix modules for system-level behavior and Home Manager modules for
|
|
||||||
user-level placement and services.
|
|
||||||
- Put user commands in [[file:dotfiles/lib/bin/][dotfiles/lib/bin/]] and shell functions in
|
|
||||||
[[file:dotfiles/lib/functions/][dotfiles/lib/functions/]].
|
|
||||||
- Run NixOS switches from [[file:nixos/][nixos/]] with =just switch=.
|
|
||||||
- Run macOS switches from [[file:nix-darwin/][nix-darwin/]] with =just switch=.
|
|
||||||
- Keep host-specific behavior in [[file:nixos/machines/][nixos/machines/]] where possible.
|
|
||||||
- Do not commit secrets or generated local state; use agenix, =pass=, or ignored
|
|
||||||
machine-local files.
|
|
||||||
1
README.org
Symbolic link
1
README.org
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
dotfiles/emacs.d/README.org
|
||||||
37
docs/cachix.md
Normal file
37
docs/cachix.md
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
# Cachix for this repo
|
||||||
|
|
||||||
|
This repo's NixOS flake lives under `nixos/`.
|
||||||
|
|
||||||
|
The workflow in `.github/workflows/cachix.yml` can build the `strixi-minaj`
|
||||||
|
system closure on GitHub Actions and push the results to a Cachix cache.
|
||||||
|
|
||||||
|
## One-time setup
|
||||||
|
|
||||||
|
1. Create a Cachix cache (on cachix.org).
|
||||||
|
2. Create a Cachix auth token with write access to that cache.
|
||||||
|
3. In the GitHub repo settings:
|
||||||
|
- Add a repo variable `CACHIX_CACHE_NAME` (the cache name).
|
||||||
|
- Add a repo secret `CACHIX_AUTH_TOKEN` (the write token).
|
||||||
|
|
||||||
|
After that, pushes to `master` will populate the cache.
|
||||||
|
|
||||||
|
## Using the cache locally
|
||||||
|
|
||||||
|
Option A: ad-hoc (non-declarative)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cachix use <your-cache-name>
|
||||||
|
```
|
||||||
|
|
||||||
|
Option B: declarative via flake `nixConfig` (recommended for NixOS)
|
||||||
|
|
||||||
|
1. Get the cache public key from the Cachix UI:
|
||||||
|
|
||||||
|
- Open `https://app.cachix.org/cache/<your-cache-name>#pull`
|
||||||
|
- Copy the `Public Key` value shown there.
|
||||||
|
|
||||||
|
2. Add it to `nixos/flake.nix` under `nixConfig.extra-substituters` and
|
||||||
|
`nixConfig.extra-trusted-public-keys`.
|
||||||
|
|
||||||
|
Note: `nixos/nix.nix` sets `nix.settings.accept-flake-config = true`, so the
|
||||||
|
flake `nixConfig` is honored during rebuilds.
|
||||||
218
docs/hyprland-lua-migration-checklist.md
Normal file
218
docs/hyprland-lua-migration-checklist.md
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
# Hyprland Lua Migration Checklist
|
||||||
|
|
||||||
|
This checklist tracks the migration described in `docs/tiling-wm-experience.md`.
|
||||||
|
|
||||||
|
Guiding rule for shelling out:
|
||||||
|
|
||||||
|
- Prefer Lua for compositor/window/workspace state changes.
|
||||||
|
- Avoid `hyprctl` for window manipulation unless there is no usable Lua API.
|
||||||
|
- `hyprctl` remains acceptable for non-window-control escape hatches such as
|
||||||
|
`hyprctl reload`.
|
||||||
|
- External utilities remain acceptable where they are the real tool being
|
||||||
|
launched, for example rofi, cliphist, grim/slurp/swappy, playerctl, hyprlock,
|
||||||
|
and systemd commands.
|
||||||
|
|
||||||
|
## 0. Version And Build Base
|
||||||
|
|
||||||
|
- [x] Update/confirm Hyprland Lua input at latest usable upstream target.
|
||||||
|
- [x] Keep stable Hyprland path intact until Lua path is proven.
|
||||||
|
- [x] Keep hy3 out of the Lua branch.
|
||||||
|
- [x] Keep hyprNStack following the Lua Hyprland input.
|
||||||
|
- [x] Rebuild hyprNStack against the Lua Hyprland branch.
|
||||||
|
- [x] Add a forked hyprexpo input for the Lua Hyprland branch.
|
||||||
|
- [x] Keep a cheap Lua check: parse config, execute against stub, reject
|
||||||
|
`hyprctl` in the Lua config's window/workspace manipulation path.
|
||||||
|
- [x] Add a real Hyprland Lua verifier check for the config parser path.
|
||||||
|
|
||||||
|
Current upstream note: latest Hyprland release observed during this migration is
|
||||||
|
`v0.54.3`; the Lua config input tracks PR 13817 and was already at the current
|
||||||
|
PR head `c35a8a5` dated 2026-04-26. The non-Lua fallback remains pinned to the older
|
||||||
|
hy3/hyprexpo-compatible stack; the Lua branch uses forked hyprexpo branch
|
||||||
|
`colonelpanic8/hyprland-plugins:hyprexpo-lua-hyprland`.
|
||||||
|
|
||||||
|
## 1. Core Layout
|
||||||
|
|
||||||
|
- [x] Primary layout is equal-width columns.
|
||||||
|
- [x] No scrolling layout.
|
||||||
|
- [x] No hy3 in Lua path.
|
||||||
|
- [x] Dynamic redistribution on open/close via Lua-managed nStack count.
|
||||||
|
- [x] Monocle/tabbed-style layout available.
|
||||||
|
- [x] Direct jump to columns layout.
|
||||||
|
- [x] Direct jump to monocle layout.
|
||||||
|
- [x] Directional focus cycles in monocle.
|
||||||
|
- [x] Visual indication of hidden monocle windows, currently notification.
|
||||||
|
- [x] Make layout state per workspace instead of one global current layout.
|
||||||
|
- [x] Preserve one-window smart gaps in the live config path.
|
||||||
|
- [x] Use a persistent monocle indicator instead of a transient notification.
|
||||||
|
|
||||||
|
Smart-gaps note: nStack uses `no_gaps_when_only = true`; Hyprland workspace
|
||||||
|
rules are still applied at runtime for broader parity, but skipped during
|
||||||
|
`--verify-config` because the current Lua PR segfaults when rule bindings run in
|
||||||
|
verifier mode.
|
||||||
|
|
||||||
|
## 2. Workspace Behavior
|
||||||
|
|
||||||
|
- [x] `Super+1..9` focuses bounded workspaces.
|
||||||
|
- [x] `Super+Shift+1..9` sends window without following.
|
||||||
|
- [x] `Super+Ctrl+1..9` sends and follows.
|
||||||
|
- [x] Previous workspace per monitor uses Lua-tracked history.
|
||||||
|
- [x] Implement next empty workspace focus in Lua.
|
||||||
|
- [x] Implement move focused window to next empty workspace without following.
|
||||||
|
- [x] Implement move focused window to next empty workspace and follow.
|
||||||
|
- [x] Implement bounded workspace cycling `1..9` in Lua, replacing
|
||||||
|
`workspace-scroll.sh`.
|
||||||
|
- [x] Implement workspace swap or decide whether native dispatcher is enough.
|
||||||
|
- [x] Track current monitor workspace history explicitly, with native
|
||||||
|
`previous_per_monitor` as fallback.
|
||||||
|
|
||||||
|
## 3. Directional Navigation
|
||||||
|
|
||||||
|
- [x] `Super+w/a/s/d` focuses windows.
|
||||||
|
- [x] `Super+Shift+w/a/s/d` swaps windows.
|
||||||
|
- [x] `Hyper+w/a/s/d` focuses monitors.
|
||||||
|
- [x] `Hyper+Shift+w/a/s/d` moves windows to monitors.
|
||||||
|
- [x] `Super+z` next monitor.
|
||||||
|
- [x] `Super+Shift+z` move to next monitor.
|
||||||
|
- [x] Replace any old cursor-follow/move scripts fully.
|
||||||
|
- [x] Add required `Super+Ctrl+w/a/s/d` move-to-monitor behavior preserving
|
||||||
|
useful focus.
|
||||||
|
- [x] Add "move to empty workspace on monitor in direction" without requiring
|
||||||
|
`Hyper+Ctrl`.
|
||||||
|
- [x] Route directional focus in monocle through deterministic Lua cycling.
|
||||||
|
- [ ] Live-verify directional focus in monocle behaves predictably.
|
||||||
|
|
||||||
|
## 4. Script Elimination Priority
|
||||||
|
|
||||||
|
- [x] Core layout switching no longer uses scripts.
|
||||||
|
- [x] Core column count logic no longer uses scripts or `hyprctl`.
|
||||||
|
- [x] Replace `find-empty-workspace.sh`.
|
||||||
|
- [x] Replace `workspace-goto-empty.sh`.
|
||||||
|
- [x] Replace `workspace-move-to-empty.sh`.
|
||||||
|
- [x] Replace `workspace-scroll.sh`.
|
||||||
|
- [x] Replace `cycle-layout.sh`.
|
||||||
|
- [x] Replace `movewindow-follow-cursor.sh`.
|
||||||
|
- [x] Replace `gather-class.sh`.
|
||||||
|
- [x] Replace `focus-next-class.sh`.
|
||||||
|
- [x] Replace `raise-or-run.sh`.
|
||||||
|
- [x] Replace minimize scripts if Lua can maintain hidden workspace state.
|
||||||
|
- [x] Replace `swap-workspaces.sh`.
|
||||||
|
- [x] Decide whether rofi-backed pickers remain scripts or become
|
||||||
|
Lua-generated command pipes. Rofi itself remains external.
|
||||||
|
|
||||||
|
## 5. Overview And Window Discovery
|
||||||
|
|
||||||
|
- [x] Restore visual hyprexpo for `Super+Tab` overview.
|
||||||
|
- [x] Restore visual hyprexpo `bring` mode for `Super+Shift+Tab`.
|
||||||
|
- [x] Keep first-pass Lua numbered window picker on secondary bindings.
|
||||||
|
- [x] Implement first-pass Lua-native go-to-window picker.
|
||||||
|
- [x] Implement first-pass Lua-native bring-window picker.
|
||||||
|
- [x] Implement first-pass Lua-native replace-window picker.
|
||||||
|
- [ ] Picker entries include icons.
|
||||||
|
- [x] Picker entries include title/workspace.
|
||||||
|
- [x] Hide scratchpad/minimized/internal windows from normal pickers.
|
||||||
|
- [x] Decide whether picker data generation can be Lua-native with rofi as only
|
||||||
|
external process.
|
||||||
|
|
||||||
|
Picker decision: current Lua API can query and manipulate windows directly, but
|
||||||
|
does not expose a synchronous way to run rofi and consume its selected output.
|
||||||
|
The first pass therefore uses Lua-native numbered submaps and notifications.
|
||||||
|
A final rofi/icon picker would need either a small IPC bridge or an upstream Lua
|
||||||
|
process-output/callback primitive.
|
||||||
|
|
||||||
|
Hyprexpo decision: hyprexpo is kept as the visual overview. The forked Lua
|
||||||
|
branch exposes `hl.plugin.hyprexpo.expo(...)`, so the Lua config can invoke
|
||||||
|
`toggle` and `bring` directly without shelling out to `hyprctl`.
|
||||||
|
|
||||||
|
## 6. Scratchpads
|
||||||
|
|
||||||
|
- [x] Preserve named scratchpads: element, gmail, htop, messages, slack,
|
||||||
|
spotify, transmission, volume.
|
||||||
|
- [x] Preserve dropdown terminal scratchpad.
|
||||||
|
- [x] Scratchpads near-fullscreen and centered.
|
||||||
|
- [x] Scratchpads hidden from normal listings/status bar.
|
||||||
|
- [x] Toggling scratchpad exits fullscreen/monocle state first.
|
||||||
|
- [x] Decide hyprscratch daemon is not needed in the Lua branch.
|
||||||
|
- [x] Replace `hyprscratch toggle` with Lua-managed scratchpad toggles.
|
||||||
|
- [x] Disable hyprscratch service on the Lua branch.
|
||||||
|
- [x] Handle delayed class/title assignment with window class/title event adoption.
|
||||||
|
- [x] Handle already-running app.
|
||||||
|
- [x] Handle minimized app.
|
||||||
|
- [x] Handle app on another workspace.
|
||||||
|
|
||||||
|
## 7. Minimization
|
||||||
|
|
||||||
|
- [x] Implement minimize active window.
|
||||||
|
- [x] Implement restore last minimized window.
|
||||||
|
- [x] Exclude minimized windows from layout.
|
||||||
|
- [x] Exclude minimized windows from normal go/bring lists.
|
||||||
|
- [x] Implement minimized picker.
|
||||||
|
- [x] Implement restore all minimized.
|
||||||
|
- [x] Implement minimize other windows of current workspace class.
|
||||||
|
- [x] Implement restore windows of focused class.
|
||||||
|
- [x] Decide hidden workspace naming/state model for minimized windows.
|
||||||
|
- [x] Hydrate minimized-window state from the hidden workspace on restore/picker
|
||||||
|
paths.
|
||||||
|
|
||||||
|
## 8. Class-Aware Workflows
|
||||||
|
|
||||||
|
- [x] Gather all windows of focused class onto current workspace.
|
||||||
|
- [x] Focus next window of different/same class as desired parity.
|
||||||
|
- [x] Browser raise-or-spawn.
|
||||||
|
- [x] Window info command exposes class/title/workspace/address/pid.
|
||||||
|
- [ ] Window menus expose real window icons.
|
||||||
|
- [x] Prefer Lua window queries over `hyprctl clients`.
|
||||||
|
|
||||||
|
## 9. Status Bar Contract
|
||||||
|
|
||||||
|
- [ ] Confirm taffybar can still list normal workspaces.
|
||||||
|
- [ ] Confirm special scratchpad/minimize workspaces are filtered.
|
||||||
|
- [ ] Confirm active workspace per monitor remains visible.
|
||||||
|
- [ ] Confirm class/title/active/minimized/urgent metadata is available.
|
||||||
|
- [x] Expose layout name/state if practical.
|
||||||
|
- [ ] Confirm workspace/window positioning remains enough for icon strips.
|
||||||
|
|
||||||
|
Layout state note: Lua writes `$XDG_RUNTIME_DIR/hyprland-layout-state` with the
|
||||||
|
active workspace, active layout, and per-workspace layout map. Taffybar still
|
||||||
|
needs a live readback check.
|
||||||
|
|
||||||
|
## 10. Session And Utilities
|
||||||
|
|
||||||
|
- [x] Terminal binding preserved.
|
||||||
|
- [x] Launcher/run menu preserved.
|
||||||
|
- [x] Media keys preserved.
|
||||||
|
- [x] Clipboard history binding preserved.
|
||||||
|
- [x] Screenshot binding preserved.
|
||||||
|
- [x] Lock binding preserved.
|
||||||
|
- [x] Session startup target integration preserved.
|
||||||
|
- [x] `hyprctl reload` may remain available as a non-window-manipulation escape
|
||||||
|
hatch.
|
||||||
|
- [x] Resolve `Hyper+w` conflict: monitor focus must win; wallpaper picker
|
||||||
|
needs another key.
|
||||||
|
- [x] Keep rofi utility commands as external commands unless there is a
|
||||||
|
meaningful Lua replacement.
|
||||||
|
- [x] Decide which shell utilities are acceptable because they are not Hyprland
|
||||||
|
control scripts.
|
||||||
|
|
||||||
|
## 11. Validation
|
||||||
|
|
||||||
|
- [x] Lua syntax check.
|
||||||
|
- [x] Lua stub execution check.
|
||||||
|
- [x] `hyprctl` rejection in Lua config for window/workspace manipulation.
|
||||||
|
- [x] Real `Hyprland --verify-config` check.
|
||||||
|
- [x] hyprNStack flake build check.
|
||||||
|
- [x] hyprexpo Lua-branch flake build check.
|
||||||
|
- [x] `ryzen-shine` system dry-run.
|
||||||
|
- [x] `just switch` activates successfully and deploys branch-owned
|
||||||
|
`~/.config/hypr/hyprland.lua`.
|
||||||
|
- [x] Re-run checks after Hyprland/Lua input confirmation.
|
||||||
|
- [ ] Try live compositor smoke test again after version bump.
|
||||||
|
- [x] Document `--verify-config` caveats for Lua rule/plugin-specific config.
|
||||||
|
- [x] Eventually run `just switch` only when the branch is coherent enough for a
|
||||||
|
live test.
|
||||||
|
|
||||||
|
Live-smoke note: this Hyprland binary exposes `--verify-config` but no
|
||||||
|
`--headless` CLI flag. `just switch` now installs the Lua branch binary and
|
||||||
|
deploys `hyprland.lua`, but the currently running compositor remains the old
|
||||||
|
0.53 process until the Hyprland session is restarted. A true compositor smoke
|
||||||
|
test still needs a session restart or a nested Wayland session that avoids
|
||||||
|
startup side effects.
|
||||||
152
docs/plans/2026-01-28-org-agenda-api-consolidation-design.md
Normal file
152
docs/plans/2026-01-28-org-agenda-api-consolidation-design.md
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
# Org-Agenda-API Consolidation Design
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Consolidate org-agenda-api container builds and fly.io deployment into the dotfiles repository. This eliminates the separate `colonelpanic-org-agenda-api` repo and provides:
|
||||||
|
|
||||||
|
- Container outputs available to NixOS machines directly
|
||||||
|
- Fly.io deployment from the same repo
|
||||||
|
- Fewer repos to maintain
|
||||||
|
- Cachix integration for faster builds
|
||||||
|
|
||||||
|
## Directory Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
/home/imalison/dotfiles/
|
||||||
|
├── nixos/
|
||||||
|
│ ├── flake.nix # Main flake, adds container output
|
||||||
|
│ ├── org-agenda-api.nix # Existing tangling module (stays here)
|
||||||
|
│ └── ...
|
||||||
|
├── org-agenda-api/
|
||||||
|
│ ├── container.nix # Container build logic (mkContainer, etc.)
|
||||||
|
│ ├── configs/
|
||||||
|
│ │ ├── colonelpanic/
|
||||||
|
│ │ │ ├── custom-config.el
|
||||||
|
│ │ │ └── overrides.el (optional)
|
||||||
|
│ │ └── kat/
|
||||||
|
│ │ └── custom-config.el
|
||||||
|
│ ├── fly/
|
||||||
|
│ │ ├── fly.toml
|
||||||
|
│ │ ├── deploy.sh
|
||||||
|
│ │ └── config-{instance}.env
|
||||||
|
│ └── secrets/
|
||||||
|
│ ├── secrets.nix # agenix declarations
|
||||||
|
│ └── *.age # encrypted secrets
|
||||||
|
└── dotfiles/emacs.d/
|
||||||
|
└── org-config.org # Source of truth for org config
|
||||||
|
```
|
||||||
|
|
||||||
|
## Flake Integration
|
||||||
|
|
||||||
|
The main dotfiles flake at `/home/imalison/dotfiles/nixos/flake.nix` exposes container outputs:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
outputs = inputs @ { self, nixpkgs, flake-utils, ... }:
|
||||||
|
{
|
||||||
|
nixosConfigurations = { ... }; # existing
|
||||||
|
} // flake-utils.lib.eachDefaultSystem (system:
|
||||||
|
let
|
||||||
|
pkgs = import nixpkgs { inherit system; };
|
||||||
|
containerLib = import ../org-agenda-api/container.nix {
|
||||||
|
inherit pkgs system;
|
||||||
|
tangledConfig = (import ./org-agenda-api.nix {
|
||||||
|
inherit pkgs system;
|
||||||
|
inputs = inputs;
|
||||||
|
}).org-agenda-custom-config;
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
packages = {
|
||||||
|
container-colonelpanic = containerLib.mkInstanceContainer "colonelpanic";
|
||||||
|
container-kat = containerLib.mkInstanceContainer "kat";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
Build with: `nix build .#container-colonelpanic`
|
||||||
|
|
||||||
|
## Custom Elisp & Tangling
|
||||||
|
|
||||||
|
Single source of truth: `org-config.org` tangles to elisp files loaded by containers.
|
||||||
|
|
||||||
|
**What stays in custom-config.el (container-specific glue):**
|
||||||
|
- Path overrides (`/data/org` instead of `~/org`)
|
||||||
|
- Stubs for unavailable packages (`org-bullets-mode` no-op)
|
||||||
|
- Customize-to-setq format conversion
|
||||||
|
- Template conversion for org-agenda-api format
|
||||||
|
- Instance-specific settings
|
||||||
|
|
||||||
|
**Audit:** During implementation, verify no actual org logic is duplicated in custom-config.el.
|
||||||
|
|
||||||
|
## Cachix Integration
|
||||||
|
|
||||||
|
### Phase 1: Use upstream cache as substituter
|
||||||
|
|
||||||
|
Add to dotfiles flake's `nixConfig`:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
nixConfig = {
|
||||||
|
extra-substituters = [
|
||||||
|
"https://org-agenda-api.cachix.org"
|
||||||
|
];
|
||||||
|
extra-trusted-public-keys = [
|
||||||
|
"org-agenda-api.cachix.org-1:PUBLIC_KEY_HERE"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Benefits:
|
||||||
|
- `container-base` (~500MB+ dependencies) fetched from cache
|
||||||
|
- Rebuilds only process the small custom config layer
|
||||||
|
|
||||||
|
### Phase 2 (future): Push custom builds
|
||||||
|
|
||||||
|
Set up GitHub Action or local push for colonelpanic-specific container builds.
|
||||||
|
|
||||||
|
## Fly.io Deployment
|
||||||
|
|
||||||
|
**What moves:**
|
||||||
|
- `fly.toml` → `dotfiles/org-agenda-api/fly/fly.toml`
|
||||||
|
- `deploy.sh` → `dotfiles/org-agenda-api/fly/deploy.sh`
|
||||||
|
- `configs/*/config.env` → `dotfiles/org-agenda-api/fly/config-{instance}.env`
|
||||||
|
- Agenix secrets → `dotfiles/org-agenda-api/secrets/`
|
||||||
|
|
||||||
|
**Deploy script changes:**
|
||||||
|
- Build path: `nix build "../nixos#container-${INSTANCE}"`
|
||||||
|
- Secrets path adjusts to new location
|
||||||
|
- Otherwise same logic
|
||||||
|
|
||||||
|
## Implementation Phases
|
||||||
|
|
||||||
|
### Phase 1: Pull latest & verify current state
|
||||||
|
- Pull latest changes in org-agenda-api and colonelpanic-org-agenda-api
|
||||||
|
- Build container, verify it works
|
||||||
|
- Fix any issues before restructuring
|
||||||
|
|
||||||
|
### Phase 2: Create dotfiles structure
|
||||||
|
- Create `/home/imalison/dotfiles/org-agenda-api/` directory
|
||||||
|
- Move container.nix logic (adapted from current colonelpanic-org-agenda-api flake)
|
||||||
|
- Move instance configs (colonelpanic/, kat/)
|
||||||
|
- Move fly.io deployment files
|
||||||
|
- Move agenix secrets
|
||||||
|
|
||||||
|
### Phase 3: Integrate with dotfiles flake
|
||||||
|
- Update `/home/imalison/dotfiles/nixos/flake.nix` to expose container outputs
|
||||||
|
- Add cachix substituter configuration
|
||||||
|
- Test build from dotfiles: `nix build .#container-colonelpanic`
|
||||||
|
|
||||||
|
### Phase 4: Verify deployment
|
||||||
|
- Test deploy.sh from new location
|
||||||
|
- Verify fly.io deployment works
|
||||||
|
- Run the container locally on a NixOS machine
|
||||||
|
|
||||||
|
### Phase 5: Audit & cleanup
|
||||||
|
- Review custom-config.el for any duplicated org logic
|
||||||
|
- Archive colonelpanic-org-agenda-api repo
|
||||||
|
- Update any references/documentation
|
||||||
|
|
||||||
|
## Repos Affected
|
||||||
|
|
||||||
|
- **dotfiles** - Receives container build + fly.io deployment
|
||||||
|
- **colonelpanic-org-agenda-api** - Becomes obsolete after migration
|
||||||
|
- **org-agenda-api** (upstream) - No changes, used as flake input
|
||||||
@@ -43,13 +43,13 @@ Required behavior:
|
|||||||
- Moving the focused window to the next empty workspace and following it is a
|
- Moving the focused window to the next empty workspace and following it is a
|
||||||
first-class operation.
|
first-class operation.
|
||||||
- Normal workspaces are bounded to `1..9`.
|
- Normal workspaces are bounded to `1..9`.
|
||||||
|
- Workspace history is tracked per monitor.
|
||||||
|
- Last-workspace toggle uses the current monitor's workspace history.
|
||||||
|
- Workspace cycling works on the current monitor within the bounded workspace
|
||||||
|
set.
|
||||||
|
|
||||||
Important behavior:
|
Important behavior:
|
||||||
|
|
||||||
- Workspace history is tracked per monitor.
|
|
||||||
- Last-workspace toggle uses the current monitor's workspace history.
|
|
||||||
- Workspace history cycling works on the current monitor within the bounded
|
|
||||||
workspace set.
|
|
||||||
- Swapping the current workspace contents with another workspace is available.
|
- Swapping the current workspace contents with another workspace is available.
|
||||||
- Moving a window to an empty workspace on another monitor is available.
|
- Moving a window to an empty workspace on another monitor is available.
|
||||||
- Moving the focused window to another monitor without following keeps keyboard
|
- Moving the focused window to another monitor without following keeps keyboard
|
||||||
@@ -62,30 +62,6 @@ Important behavior:
|
|||||||
- Hidden/special workspaces are excluded from the status bar's normal workspace
|
- Hidden/special workspaces are excluded from the status bar's normal workspace
|
||||||
list.
|
list.
|
||||||
|
|
||||||
### Workspace History Cycling
|
|
||||||
|
|
||||||
Important behavior:
|
|
||||||
|
|
||||||
- The model is most-recently-used workspace switching, scoped to the monitor
|
|
||||||
where the action starts.
|
|
||||||
- Each monitor has its own ordered workspace history. The focused monitor's
|
|
||||||
history is not shared with other monitors.
|
|
||||||
- Only ordinary bounded workspaces are candidates. Special, scratchpad,
|
|
||||||
minimized, hidden, and out-of-range workspaces are excluded.
|
|
||||||
- Starting a cycle freezes the candidate list for that cycle. Previewing
|
|
||||||
workspaces while the cycle is active must not rewrite the history order.
|
|
||||||
- Starting a cycle previews the previous workspace for the current monitor.
|
|
||||||
- Repeating the forward cycle action continues farther back through that
|
|
||||||
monitor's frozen history.
|
|
||||||
- A reverse cycle action moves through the same frozen history in the opposite
|
|
||||||
direction.
|
|
||||||
- Releasing the initiating modifier key commits the currently previewed
|
|
||||||
workspace and updates history exactly once.
|
|
||||||
- A cancel path may return to the workspace where the cycle started.
|
|
||||||
|
|
||||||
This behavior is important for workflow continuity, but it is not a hard
|
|
||||||
requirement for a minimal daily-driver window manager.
|
|
||||||
|
|
||||||
## Directional Navigation
|
## Directional Navigation
|
||||||
|
|
||||||
Required behavior:
|
Required behavior:
|
||||||
@@ -114,16 +90,6 @@ Important behavior:
|
|||||||
- Keyboard resize remains available, but it should not displace the directional
|
- Keyboard resize remains available, but it should not displace the directional
|
||||||
move-to-monitor binding.
|
move-to-monitor binding.
|
||||||
|
|
||||||
## Pointer Focus
|
|
||||||
|
|
||||||
Required behavior:
|
|
||||||
|
|
||||||
- Focus-follows-mouse, or an equivalent pointer-driven focus model, is enabled.
|
|
||||||
- Moving the pointer over a managed window focuses that window without requiring
|
|
||||||
a click.
|
|
||||||
- Mouse-follows-focus is also enabled: keyboard or programmatic focus changes
|
|
||||||
move the pointer into the newly focused window.
|
|
||||||
|
|
||||||
## Layouts
|
## Layouts
|
||||||
|
|
||||||
Required behavior:
|
Required behavior:
|
||||||
@@ -133,8 +99,6 @@ Required behavior:
|
|||||||
- Scrolling layouts are not acceptable.
|
- Scrolling layouts are not acceptable.
|
||||||
- All ordinary splits are vertical.
|
- All ordinary splits are vertical.
|
||||||
- Adding windows dynamically redistributes all tiled windows evenly.
|
- Adding windows dynamically redistributes all tiled windows evenly.
|
||||||
- Newly tiled windows are inserted near the currently focused tile, not
|
|
||||||
appended to the far end of the workspace.
|
|
||||||
- Removing windows dynamically redistributes all tiled windows evenly.
|
- Removing windows dynamically redistributes all tiled windows evenly.
|
||||||
- Ordinary use should not require manually managing a split tree.
|
- Ordinary use should not require manually managing a split tree.
|
||||||
- Tabbed/fullscreen-style monocle layout is available.
|
- Tabbed/fullscreen-style monocle layout is available.
|
||||||
@@ -145,9 +109,6 @@ Required behavior:
|
|||||||
- Dialogs are centered.
|
- Dialogs are centered.
|
||||||
- There is a command to jump directly to the columns layout and one to jump
|
- There is a command to jump directly to the columns layout and one to jump
|
||||||
directly to the tabbed/fullscreen layout.
|
directly to the tabbed/fullscreen layout.
|
||||||
- `Super+Ctrl+Space` jumps directly to the tabbed/fullscreen layout.
|
|
||||||
- Direct fullscreen or floating-fullscreen behavior should not have a
|
|
||||||
keybinding.
|
|
||||||
- Layout state is per workspace when the compositor supports it.
|
- Layout state is per workspace when the compositor supports it.
|
||||||
|
|
||||||
Important behavior:
|
Important behavior:
|
||||||
@@ -157,6 +118,7 @@ Important behavior:
|
|||||||
Nice behavior:
|
Nice behavior:
|
||||||
|
|
||||||
- Gaps can be toggled.
|
- Gaps can be toggled.
|
||||||
|
- Fullscreen can be toggled.
|
||||||
- Smart borders can be toggled.
|
- Smart borders can be toggled.
|
||||||
- Layout-related modifiers remain available for experiments.
|
- Layout-related modifiers remain available for experiments.
|
||||||
- Inactive windows are slightly dimmed when supported.
|
- Inactive windows are slightly dimmed when supported.
|
||||||
@@ -165,8 +127,7 @@ Nice behavior:
|
|||||||
|
|
||||||
Required behavior:
|
Required behavior:
|
||||||
|
|
||||||
- There is a visual window overview for inspecting open windows before jumping.
|
- There is an expose-style way to inspect open windows or workspaces before
|
||||||
- There is a visual workspace expose for inspecting normal workspaces before
|
|
||||||
jumping.
|
jumping.
|
||||||
- There is a rofi-style window picker.
|
- There is a rofi-style window picker.
|
||||||
- Window picker entries show icons.
|
- Window picker entries show icons.
|
||||||
@@ -180,15 +141,6 @@ Required behavior:
|
|||||||
Important behavior:
|
Important behavior:
|
||||||
|
|
||||||
- Overview supports both "go" and "bring" workflows.
|
- Overview supports both "go" and "bring" workflows.
|
||||||
- Window overview and workspace expose are distinct surfaces, because window
|
|
||||||
selection and workspace selection are different navigation tasks.
|
|
||||||
- Window overview supports directional keyboard selection with the same
|
|
||||||
`w/a/s/d` spatial model as ordinary window focus.
|
|
||||||
- Window overview supports direct go, bring, and replace-window actions from the
|
|
||||||
selection UI.
|
|
||||||
- Workspace expose shows bounded normal workspaces, including empty workspaces,
|
|
||||||
with visible workspace numbers.
|
|
||||||
- Workspace expose can be opened in a bring-window-oriented mode when supported.
|
|
||||||
- Window switchers hide scratchpad windows unless the user is explicitly using a
|
- Window switchers hide scratchpad windows unless the user is explicitly using a
|
||||||
scratchpad picker.
|
scratchpad picker.
|
||||||
- Window switchers hide minimized windows unless the user is explicitly using a
|
- Window switchers hide minimized windows unless the user is explicitly using a
|
||||||
@@ -200,9 +152,10 @@ Important behavior:
|
|||||||
|
|
||||||
Required behavior:
|
Required behavior:
|
||||||
|
|
||||||
- A named scratchpad exists for codex.
|
|
||||||
- A named scratchpad exists for element.
|
- A named scratchpad exists for element.
|
||||||
|
- A named scratchpad exists for gmail.
|
||||||
- A named scratchpad exists for htop.
|
- A named scratchpad exists for htop.
|
||||||
|
- A named scratchpad exists for messages.
|
||||||
- A named scratchpad exists for slack.
|
- A named scratchpad exists for slack.
|
||||||
- A named scratchpad exists for spotify.
|
- A named scratchpad exists for spotify.
|
||||||
- A named scratchpad exists for transmission.
|
- A named scratchpad exists for transmission.
|
||||||
@@ -321,43 +274,19 @@ Required behavior:
|
|||||||
- `Super+p` opens the application launcher.
|
- `Super+p` opens the application launcher.
|
||||||
- `Super+Shift+p` opens the run menu.
|
- `Super+Shift+p` opens the run menu.
|
||||||
- `Super+Shift+Return` opens a terminal.
|
- `Super+Shift+Return` opens a terminal.
|
||||||
- `Super+q` reloads the window manager config.
|
- `Super+Tab` opens the overview.
|
||||||
- `Super+Shift+c` closes the focused window.
|
- `Super+Shift+Tab` opens the overview in bring-window mode when supported.
|
||||||
- `Super+Shift+q` exits the window manager session.
|
|
||||||
- `Super+x` opens the command picker with `rofi_command.sh`.
|
|
||||||
- `Super+g` opens the go-to-window picker.
|
- `Super+g` opens the go-to-window picker.
|
||||||
- `Super+b` opens the bring-window picker.
|
- `Super+b` opens the bring-window picker.
|
||||||
- `Super+Shift+b` opens the replace-window picker.
|
- `Super+Shift+b` opens the replace-window picker.
|
||||||
|
- `Super+\` toggles to the previous workspace on the current monitor.
|
||||||
- `Super+Shift+e` moves the focused window to the next empty workspace and
|
- `Super+Shift+e` moves the focused window to the next empty workspace and
|
||||||
follows it. This is the target replacement for the older `Super+Shift+h`
|
follows it. This is the target replacement for the older `Super+Shift+h`
|
||||||
binding.
|
binding.
|
||||||
- `Hyper+e` focuses the next empty workspace.
|
- `Hyper+e` focuses the next empty workspace.
|
||||||
- `Hyper+1` toggles inactive-window opacity reduction for the focused window.
|
|
||||||
- `Hyper+5` swaps the current workspace with a selected workspace.
|
- `Hyper+5` swaps the current workspace with a selected workspace.
|
||||||
- `Hyper+g` gathers windows of the focused class onto the current workspace.
|
- `Hyper+g` gathers windows of the focused class onto the current workspace.
|
||||||
|
|
||||||
Important behavior:
|
|
||||||
|
|
||||||
- `Super+Tab` opens the visual window overview.
|
|
||||||
- `Super+Shift+Tab` opens the visual window overview scoped to non-visible
|
|
||||||
windows or bring-window mode when supported.
|
|
||||||
- `Alt+Tab` opens the visual workspace expose.
|
|
||||||
- `Alt+Shift+Tab` opens the visual workspace expose in bring-window mode when
|
|
||||||
supported.
|
|
||||||
- Within visual window overview, `w/a/s/d`, `h/j/k/l`, and arrow keys move the
|
|
||||||
selection directionally.
|
|
||||||
- Within visual window overview, `Return`, `Space`, `g`, or `f` activates the
|
|
||||||
selected window.
|
|
||||||
- Within visual window overview, `b`, `Shift+Return`, or `Shift+Space` brings
|
|
||||||
the selected window to the current workspace.
|
|
||||||
- Within visual window overview, `Shift+b` replaces the focused window with the
|
|
||||||
selected window when supported.
|
|
||||||
- Within visual window overview, `Escape` or `q` closes the overview.
|
|
||||||
- `Super+\` starts or advances current-monitor workspace history cycling.
|
|
||||||
- `Super+/` reverses current-monitor workspace history cycling while the
|
|
||||||
initiating `Super` key is held.
|
|
||||||
- Releasing the initiating `Super` key commits the workspace history cycle.
|
|
||||||
|
|
||||||
### Directional Navigation Bindings
|
### Directional Navigation Bindings
|
||||||
|
|
||||||
Required behavior:
|
Required behavior:
|
||||||
@@ -387,9 +316,10 @@ Required behavior:
|
|||||||
|
|
||||||
Required behavior:
|
Required behavior:
|
||||||
|
|
||||||
- `Super+Alt+c` toggles the codex scratchpad.
|
|
||||||
- `Super+Alt+e` toggles the element scratchpad.
|
- `Super+Alt+e` toggles the element scratchpad.
|
||||||
|
- `Super+Alt+g` toggles the gmail scratchpad.
|
||||||
- `Super+Alt+h` toggles the htop scratchpad.
|
- `Super+Alt+h` toggles the htop scratchpad.
|
||||||
|
- `Super+Alt+m` toggles the messages scratchpad.
|
||||||
- `Super+Alt+k` toggles the slack scratchpad.
|
- `Super+Alt+k` toggles the slack scratchpad.
|
||||||
- `Super+Alt+s` toggles the spotify scratchpad.
|
- `Super+Alt+s` toggles the spotify scratchpad.
|
||||||
- `Super+Alt+t` toggles the transmission scratchpad.
|
- `Super+Alt+t` toggles the transmission scratchpad.
|
||||||
@@ -398,6 +328,7 @@ Required behavior:
|
|||||||
Important behavior:
|
Important behavior:
|
||||||
|
|
||||||
- `Super+Alt+grave` toggles the dropdown terminal scratchpad.
|
- `Super+Alt+grave` toggles the dropdown terminal scratchpad.
|
||||||
|
- `Super+Alt+c` raises or starts the browser.
|
||||||
- `Super+Alt+Return` enters the minimized-window picker or restores minimized
|
- `Super+Alt+Return` enters the minimized-window picker or restores minimized
|
||||||
windows, depending on environment support.
|
windows, depending on environment support.
|
||||||
- `Super+Alt` is reserved for app-specific raise/spawn, scratchpad, and
|
- `Super+Alt` is reserved for app-specific raise/spawn, scratchpad, and
|
||||||
@@ -412,8 +343,8 @@ Required behavior:
|
|||||||
- `Hyper+p` opens the password picker with `rofi-pass`.
|
- `Hyper+p` opens the password picker with `rofi-pass`.
|
||||||
- `Hyper+h` opens the screenshot tool with the compositor/session-appropriate
|
- `Hyper+h` opens the screenshot tool with the compositor/session-appropriate
|
||||||
screenshot command.
|
screenshot command.
|
||||||
- `Hyper+c` opens the Codex launcher with `rofi_tmcodex.sh`.
|
- `Hyper+c` opens a shell command prompt with `shell_command.sh`.
|
||||||
- `Hyper+Shift+c` opens the Codex launcher with `tmcodex resume`.
|
- `Hyper+x` opens the command picker with `rofi_command.sh`.
|
||||||
- `Hyper+k` opens the process killer with `rofi_kill_process.sh`.
|
- `Hyper+k` opens the process killer with `rofi_kill_process.sh`.
|
||||||
- `Hyper+Shift+k` opens the kill-all/process-tree killer with
|
- `Hyper+Shift+k` opens the kill-all/process-tree killer with
|
||||||
`rofi_kill_all.sh`.
|
`rofi_kill_all.sh`.
|
||||||
|
|||||||
@@ -3,18 +3,16 @@
|
|||||||
## Multiplexer session titling
|
## Multiplexer session titling
|
||||||
- If the `TMUX` or `ZELLIJ` environment variable is set, treat this chat as the controller for the current tmux or zellij session.
|
- If the `TMUX` or `ZELLIJ` environment variable is set, treat this chat as the controller for the current tmux or zellij session.
|
||||||
- Use `set_multiplexer_title '<project> - <task>'` to update the title. The command detects tmux vs. zellij internally, prefers tmux when both are present, and no-ops outside a multiplexer.
|
- Use `set_multiplexer_title '<project> - <task>'` to update the title. The command detects tmux vs. zellij internally, prefers tmux when both are present, and no-ops outside a multiplexer.
|
||||||
- Maintain a session/window/pane title that describes the durable purpose of the overall exchange.
|
- Maintain a session/window/pane title that updates when the task focus changes substantially.
|
||||||
- Prefer automatic titling: infer a concise <task> from the current user request and the existing chat context without asking.
|
- Prefer automatic titling: infer a concise <task> from the current user request and context without asking.
|
||||||
- Choose holistic titles over granular turn summaries. The title should answer "what has this chat been for?" rather than describe the latest command, substep, clarification, or follow-up message.
|
|
||||||
- Preserve the existing <task> when the new user turn is a continuation, status check, refinement, or implementation detail within the same broader objective.
|
|
||||||
- Title format: "<project> - <task>".
|
- Title format: "<project> - <task>".
|
||||||
- <project> is the basename of the current project directory.
|
- <project> is the basename of the current project directory.
|
||||||
- Prefer git repo root basename if available; otherwise use basename of the current working directory.
|
- Prefer git repo root basename if available; otherwise use basename of the current working directory.
|
||||||
- <task> is a short, user-friendly description of what we are doing.
|
- <task> is a short, user-friendly description of what we are doing.
|
||||||
- Ask for a short descriptive <task> only when the task is ambiguous or you are not confident in an inferred title.
|
- Ask for a short descriptive <task> only when the task is ambiguous or you are not confident in an inferred title.
|
||||||
- When the broader objective changes substantially, update the <task> automatically if clear; otherwise ask for an updated <task>.
|
- When the task changes substantially, update the <task> automatically if clear; otherwise ask for an updated <task>.
|
||||||
- When a title is provided or updated, immediately run `set_multiplexer_title '<project> - <task>'`; do not call raw tmux or zellij rename commands unless debugging the helper itself.
|
- When a title is provided or updated, immediately run `set_multiplexer_title '<project> - <task>'`; do not call raw tmux or zellij rename commands unless debugging the helper itself.
|
||||||
- For Claude Code sessions, a UserPromptSubmit hook may initialize titles automatically from the first substantive prompt, but it should not keep overwriting an established same-project title with the latest prompt.
|
- For Claude Code sessions, a UserPromptSubmit hook will also update titles automatically based on the latest prompt.
|
||||||
|
|
||||||
## Pane usage
|
## Pane usage
|
||||||
- Do not create extra panes or windows unless the user asks.
|
- Do not create extra panes or windows unless the user asks.
|
||||||
|
|||||||
@@ -16,13 +16,10 @@ sys.stdout.write(cwd)
|
|||||||
sys.stdout.write("\0")
|
sys.stdout.write("\0")
|
||||||
sys.stdout.write(prompt)
|
sys.stdout.write(prompt)
|
||||||
sys.stdout.write("\0")
|
sys.stdout.write("\0")
|
||||||
sys.stdout.write(str(data.get("session_id") or ""))
|
|
||||||
sys.stdout.write("\0")
|
|
||||||
PY
|
PY
|
||||||
)
|
)
|
||||||
cwd="${parsed[0]:-}"
|
cwd="${parsed[0]:-}"
|
||||||
prompt="${parsed[1]:-}"
|
prompt="${parsed[1]:-}"
|
||||||
session_id="${parsed[2]:-}"
|
|
||||||
|
|
||||||
if [[ -z "${cwd}" ]]; then
|
if [[ -z "${cwd}" ]]; then
|
||||||
cwd="$PWD"
|
cwd="$PWD"
|
||||||
@@ -49,17 +46,6 @@ if [[ -z "$task" ]]; then
|
|||||||
task="work"
|
task="work"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
explicit_retitle=false
|
|
||||||
case "$lower" in
|
|
||||||
"new task:"*|"new topic:"*|"switch topic:"*|"switch context:"*|"rename title:"*|"title:"*)
|
|
||||||
explicit_retitle=true
|
|
||||||
task=$(printf '%s' "$prompt_first_line" | sed -E 's/^[^:]+:[[:space:]]*//')
|
|
||||||
if [[ -z "$task" ]]; then
|
|
||||||
task="work"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# Trim to a reasonable length for multiplexer UI labels.
|
# Trim to a reasonable length for multiplexer UI labels.
|
||||||
if [[ ${#task} -gt 60 ]]; then
|
if [[ ${#task} -gt 60 ]]; then
|
||||||
task="${task:0:57}..."
|
task="${task:0:57}..."
|
||||||
@@ -67,42 +53,9 @@ fi
|
|||||||
|
|
||||||
title="$project - $task"
|
title="$project - $task"
|
||||||
|
|
||||||
# The hook only sees the newest prompt, not the full conversation. Avoid
|
|
||||||
# degrading a useful same-project title into a granular follow-up summary.
|
|
||||||
if [[ -n "${TMUX:-}" ]]; then
|
|
||||||
multiplexer="tmux"
|
|
||||||
elif [[ -n "${ZELLIJ:-}" ]]; then
|
|
||||||
multiplexer="zellij"
|
|
||||||
else
|
|
||||||
multiplexer=""
|
|
||||||
fi
|
|
||||||
|
|
||||||
hook_state_file=""
|
|
||||||
if [[ -n "$multiplexer" ]]; then
|
|
||||||
state_dir="${HOME}/.agents/state"
|
|
||||||
if [[ -n "$session_id" ]]; then
|
|
||||||
safe_session_id=$(printf '%s' "$session_id" | tr -c '[:alnum:]_.-' '_')
|
|
||||||
hook_state_file="${state_dir}/${multiplexer}-title-hook-${safe_session_id}"
|
|
||||||
else
|
|
||||||
hook_state_file="${state_dir}/${multiplexer}-title"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -f "$hook_state_file" ]]; then
|
|
||||||
established_title=$(cat "$hook_state_file" 2>/dev/null || true)
|
|
||||||
if [[ "$established_title" == "$project - "* && "$established_title" != "$title" && "$explicit_retitle" != true ]]; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if command -v set_multiplexer_title >/dev/null 2>&1; then
|
if command -v set_multiplexer_title >/dev/null 2>&1; then
|
||||||
set_multiplexer_title "$title"
|
set_multiplexer_title "$title"
|
||||||
else
|
else
|
||||||
hook_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
hook_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
||||||
"$hook_dir/../../lib/functions/set_multiplexer_title" "$title"
|
"$hook_dir/../../lib/functions/set_multiplexer_title" "$title"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n "$hook_state_file" ]]; then
|
|
||||||
mkdir -p "$(dirname "$hook_state_file")"
|
|
||||||
printf '%s' "$title" > "$hook_state_file"
|
|
||||||
fi
|
|
||||||
|
|||||||
@@ -112,8 +112,6 @@ Recommended sequence:
|
|||||||
Machine-specific note:
|
Machine-specific note:
|
||||||
|
|
||||||
- Project-local `.worktrees/*/target` directories are common cleanup wins on this machine and are easy to miss with the old hard-coded workflow.
|
- Project-local `.worktrees/*/target` directories are common cleanup wins on this machine and are easy to miss with the old hard-coded workflow.
|
||||||
- `cargo-sweep` is installed through the NixOS `code.nix` package set, but stale manually-installed binaries under `~/.cargo/bin` can shadow `/run/current-system/sw/bin/cargo-sweep`. If `cargo sweep` fails with a missing loader or `No such file or directory`, run `type -a cargo-sweep` and remove the stale `~/.cargo/bin/cargo-sweep` entry.
|
|
||||||
- `nixos/imalison.nix` defines a daily user timer, `cargo-sweep-rust-targets.timer`, that runs `cargo-sweep sweep -r --hidden --maxsize 15GB` across `/home/imalison/Projects`, `/home/imalison/org`, and `/home/imalison/dotfiles`.
|
|
||||||
|
|
||||||
## Step 4: Investigation with `ncdu` and `du`
|
## Step 4: Investigation with `ncdu` and `du`
|
||||||
|
|
||||||
|
|||||||
@@ -9,28 +9,25 @@ How the taffybar ecosystem packages are consumed by the NixOS configuration thro
|
|||||||
|
|
||||||
See also: `taffybar-ecosystem-release` for the package dependency graph, release workflow, and Hackage publishing.
|
See also: `taffybar-ecosystem-release` for the package dependency graph, release workflow, and Hackage publishing.
|
||||||
|
|
||||||
## The Flake Chain
|
## The Three-Layer Flake Chain
|
||||||
|
|
||||||
The NixOS system build pulls in taffybar through the personal
|
The NixOS system build pulls in taffybar through three nested flake.nix files:
|
||||||
`imalison-taffybar` config flake. The top-level NixOS flake should not declare
|
|
||||||
or override a direct `taffybar` input; the config flake owns its taffybar
|
|
||||||
version.
|
|
||||||
|
|
||||||
```
|
```
|
||||||
nixos/flake.nix (top - `just switch` reads this)
|
nixos/flake.nix (top — `just switch` reads this)
|
||||||
│ └── imalison-taffybar path:../dotfiles/config/taffybar
|
│ ├── taffybar path:.../taffybar/taffybar
|
||||||
|
│ ├── imalison-taffybar path:../dotfiles/config/taffybar
|
||||||
|
│ └── gtk-sni-tray, gtk-strut, etc. (GitHub inputs)
|
||||||
│
|
│
|
||||||
dotfiles/config/taffybar/flake.nix (middle - imalison-taffybar config)
|
dotfiles/config/taffybar/flake.nix (middle — imalison-taffybar config)
|
||||||
│ ├── taffybar path:.../taffybar/taffybar
|
│ ├── taffybar path:.../taffybar/taffybar
|
||||||
│ └── gtk-sni-tray, gtk-strut, etc. (GitHub inputs)
|
│ └── gtk-sni-tray, gtk-strut, etc. (GitHub inputs)
|
||||||
│
|
│
|
||||||
dotfiles/config/taffybar/taffybar/flake.nix (bottom - taffybar library)
|
dotfiles/config/taffybar/taffybar/flake.nix (bottom — taffybar library)
|
||||||
│ └── gtk-sni-tray, gtk-strut, etc. (flake = false GitHub inputs)
|
│ └── gtk-sni-tray, gtk-strut, etc. (flake = false GitHub inputs)
|
||||||
```
|
```
|
||||||
|
|
||||||
The NixOS layer may make `imalison-taffybar` follow shared inputs such as
|
All three flakes declare their own top-level inputs for the ecosystem packages and use `follows` to keep versions consistent within each layer.
|
||||||
`nixpkgs`, `flake-utils`, and `xmonad`, but it should not set
|
|
||||||
`imalison-taffybar.inputs.taffybar.follows`.
|
|
||||||
|
|
||||||
## Why Bottom-Up Updates Matter
|
## Why Bottom-Up Updates Matter
|
||||||
|
|
||||||
@@ -46,14 +43,14 @@ cd ~/.config/taffybar/taffybar && nix flake update <pkg>
|
|||||||
cd ~/.config/taffybar && nix flake update <pkg> taffybar
|
cd ~/.config/taffybar && nix flake update <pkg> taffybar
|
||||||
|
|
||||||
# Top:
|
# Top:
|
||||||
cd ~/dotfiles/nixos && nix flake update imalison-taffybar
|
cd ~/dotfiles/nixos && nix flake update <pkg> imalison-taffybar taffybar
|
||||||
```
|
```
|
||||||
|
|
||||||
Not every change requires touching all three layers. Think about which flake.lock files actually contain stale references:
|
Not every change requires touching all three layers. Think about which flake.lock files actually contain stale references:
|
||||||
|
|
||||||
- Changed **taffybar itself** — it's owned by the config flake, so start at the middle (`nix flake update taffybar`) then update `imalison-taffybar` at the top.
|
- Changed **taffybar itself** — it's the bottom layer, so start at the middle (`nix flake update taffybar`) then the top.
|
||||||
- Changed a **leaf ecosystem package** (e.g. gtk-strut) — start at the bottom since taffybar's flake.lock references it, then cascade up.
|
- Changed a **leaf ecosystem package** (e.g. gtk-strut) — start at the bottom since taffybar's flake.lock references it, then cascade up.
|
||||||
- The nixos flake can still have unrelated direct inputs such as `kanshi-sni`. Do not add a top-level `taffybar` input just to control the config flake's taffybar source.
|
- The nixos flake also has **direct GitHub inputs** for ecosystem packages with `follows` overrides. Updating those at the top level may be sufficient if nothing changed in the middle/bottom flake.lock files themselves.
|
||||||
|
|
||||||
## Rebuilding
|
## Rebuilding
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,5 @@
|
|||||||
"agent-browser@agent-browser": true
|
"agent-browser@agent-browser": true
|
||||||
},
|
},
|
||||||
"effortLevel": "high",
|
"effortLevel": "high",
|
||||||
"skipDangerousModePermissionPrompt": true,
|
"skipDangerousModePermissionPrompt": true
|
||||||
"remoteControlAtStartup": true
|
|
||||||
}
|
}
|
||||||
|
|||||||
3
dotfiles/codex/.gitignore
vendored
3
dotfiles/codex/.gitignore
vendored
@@ -3,6 +3,3 @@
|
|||||||
!AGENTS.md
|
!AGENTS.md
|
||||||
!config.toml
|
!config.toml
|
||||||
!skills
|
!skills
|
||||||
|
|
||||||
# Legacy generated/local Codex state under this repo stays ignored. Active
|
|
||||||
# host-local Codex fragments now live under ~/.codex.
|
|
||||||
|
|||||||
@@ -1,12 +1,148 @@
|
|||||||
model = "gpt-5.5"
|
model = "gpt-5.5"
|
||||||
model_reasoning_effort = "high"
|
model_reasoning_effort = "high"
|
||||||
service_tier = "fast"
|
|
||||||
personality = "pragmatic"
|
personality = "pragmatic"
|
||||||
suppress_unstable_features_warning = true
|
|
||||||
|
|
||||||
# Portable Codex defaults. Home Manager regenerates ~/.codex/config.toml from
|
|
||||||
# this file, ~/.codex/config.local.toml, and Codex-owned sections preserved in
|
notify = ["/Users/kat/dotfiles/dotfiles/codex/plugins/cache/openai-bundled/computer-use/1.0.755/Codex Computer Use.app/Contents/SharedSupport/SkyComputerUseClient.app/Contents/MacOS/SkyComputerUseClient", "turn-ended"]
|
||||||
# ~/.codex/config.local-state.toml.
|
|
||||||
|
[projects."/home/imalison/Projects/nixpkgs"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/dotfiles"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/Projects/railbird"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/Projects/subtr-actor"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/Projects/google-messages-api"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/Projects/scrobble-scrubber"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/temp"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/Projects/org-agenda-api"]
|
||||||
|
trust_level = "untrusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/org"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/dotfiles/.git/modules/dotfiles/config/taffybar"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/Projects/notifications-tray-icon"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/Projects/hyprland"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/Projects/git-sync-rs"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/Projects/keepbook"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/Projects/boxcars"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/Projects/rumno"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/Projects/git-blame-rank"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/Projects/hatchet"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/dotfiles/dotfiles/emacs.d/elpaca/sources/org-project-capture"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/dotfiles/dotfiles/config/taffybar/taffybar/packages"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/Projects/scrobble-tools"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/.password-store"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/Projects/subtr-actor-mechanics"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/Projects/lastfm-edit"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/Projects/mova"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/dotfiles/dotfiles/config/taffybar/taffybar"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/Projects"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/Projects/rofi-systemd"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/Projects/map-quiz"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/run/media/imalison/NETDEBUGUSB"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/Projects/coqui-tts-streamer"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/Downloads"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/keysmith_generated"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/run/media/imalison/NIXOS_SD"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/Users/kat/dotfiles"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/Users/kat"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/Users/kat/org"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/Users/kat/Documents/Codex/2026-04-25/do-you-see-the-sandisk-external"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/Volumes/Extreme SSD/Projects/keepbook"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/Users/kat/Documents/Codex/2026-04-25/it-seems-like-maybe-we-dont"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/Users/kat/Documents/Codex/2026-04-25/what-is-the-state-of-tiling"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/Pictures/ai/2026/celeb"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/imalison/.local/share/keepbook"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[notice]
|
||||||
|
hide_gpt5_1_migration_prompt = true
|
||||||
|
"hide_gpt-5.1-codex-max_migration_prompt" = true
|
||||||
|
|
||||||
|
[notice.model_migrations]
|
||||||
|
"gpt-5.2" = "gpt-5.2-codex"
|
||||||
|
|
||||||
[mcp_servers.chrome-devtools]
|
[mcp_servers.chrome-devtools]
|
||||||
command = "npx"
|
command = "npx"
|
||||||
@@ -23,9 +159,16 @@ url = "https://developers.openai.com/mcp"
|
|||||||
unified_exec = true
|
unified_exec = true
|
||||||
apps = true
|
apps = true
|
||||||
steer = true
|
steer = true
|
||||||
goals = true
|
|
||||||
fast_mode = true
|
[marketplaces.openai-bundled]
|
||||||
remote_control = true
|
last_updated = "2026-04-21T17:43:57Z"
|
||||||
|
source_type = "local"
|
||||||
|
source = "/Users/kat/.codex/.tmp/bundled-marketplaces/openai-bundled"
|
||||||
|
|
||||||
|
[marketplaces.openai-primary-runtime]
|
||||||
|
last_updated = "2026-04-25T23:49:36Z"
|
||||||
|
source_type = "local"
|
||||||
|
source = "/Users/kat/.cache/codex-runtimes/codex-primary-runtime/plugins/openai-primary-runtime"
|
||||||
|
|
||||||
[plugins."google-calendar@openai-curated"]
|
[plugins."google-calendar@openai-curated"]
|
||||||
enabled = true
|
enabled = true
|
||||||
@@ -53,3 +196,6 @@ enabled = true
|
|||||||
|
|
||||||
[plugins."browser-use@openai-bundled"]
|
[plugins."browser-use@openai-bundled"]
|
||||||
enabled = true
|
enabled = true
|
||||||
|
|
||||||
|
[tui.model_availability_nux]
|
||||||
|
"gpt-5.5" = 4
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
general {
|
general {
|
||||||
lock_cmd = pidof hyprlock || hyprlock
|
lock_cmd = pidof hyprlock || hyprlock
|
||||||
before_sleep_cmd = loginctl lock-session
|
before_sleep_cmd = loginctl lock-session
|
||||||
|
after_sleep_cmd = hyprctl dispatch dpms on
|
||||||
}
|
}
|
||||||
|
|
||||||
listener {
|
listener {
|
||||||
timeout = 300
|
timeout = 900
|
||||||
on-timeout = /home/imalison/dotfiles/dotfiles/lib/bin/hypr-screensaver start
|
on-timeout = hypr-screensaver stop && hyprctl dispatch dpms off
|
||||||
on-resume = /home/imalison/dotfiles/dotfiles/lib/bin/hypr-screensaver stop
|
on-resume = hyprctl dispatch dpms on
|
||||||
}
|
}
|
||||||
|
|||||||
562
dotfiles/config/hypr/hyprland.conf
Normal file
562
dotfiles/config/hypr/hyprland.conf
Normal file
@@ -0,0 +1,562 @@
|
|||||||
|
# Hyprland Configuration
|
||||||
|
# XMonad-like dynamic tiling using hy3 plugin
|
||||||
|
# Based on XMonad configuration from xmonad.hs
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# PLUGINS (Hyprland pinned to 0.53.0 to match hy3)
|
||||||
|
# =============================================================================
|
||||||
|
# Load the plugin before parsing keybinds/layouts that depend on it
|
||||||
|
plugin = /run/current-system/sw/lib/libhy3.so
|
||||||
|
plugin = /run/current-system/sw/lib/libhyprexpo.so
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# MONITORS
|
||||||
|
# =============================================================================
|
||||||
|
monitor=,preferred,auto,1
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# PROGRAMS
|
||||||
|
# =============================================================================
|
||||||
|
$terminal = ghostty --gtk-single-instance=false
|
||||||
|
$fileManager = dolphin
|
||||||
|
$menu = rofi -show drun -show-icons
|
||||||
|
$runMenu = rofi -show run
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# ENVIRONMENT VARIABLES
|
||||||
|
# =============================================================================
|
||||||
|
env = XCURSOR_SIZE,24
|
||||||
|
env = QT_QPA_PLATFORMTHEME,qt5ct
|
||||||
|
# Used by ~/.config/hypr/scripts/* to keep workspace IDs bounded.
|
||||||
|
env = HYPR_MAX_WORKSPACE,9
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# INPUT CONFIGURATION
|
||||||
|
# =============================================================================
|
||||||
|
input {
|
||||||
|
kb_layout = us
|
||||||
|
kb_variant =
|
||||||
|
kb_model =
|
||||||
|
kb_options =
|
||||||
|
kb_rules =
|
||||||
|
|
||||||
|
follow_mouse = 1
|
||||||
|
|
||||||
|
touchpad {
|
||||||
|
natural_scroll = no
|
||||||
|
}
|
||||||
|
|
||||||
|
sensitivity = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cursor warping behavior
|
||||||
|
cursor {
|
||||||
|
persistent_warps = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# GENERAL SETTINGS
|
||||||
|
# =============================================================================
|
||||||
|
general {
|
||||||
|
gaps_in = 5
|
||||||
|
gaps_out = 10
|
||||||
|
border_size = 0
|
||||||
|
col.active_border = rgba(edb443ee) rgba(33ccffee) 45deg
|
||||||
|
col.inactive_border = rgba(595959aa)
|
||||||
|
|
||||||
|
# Use hy3 layout for XMonad-like dynamic tiling
|
||||||
|
layout = hy3
|
||||||
|
|
||||||
|
allow_tearing = false
|
||||||
|
}
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# DECORATION
|
||||||
|
# =============================================================================
|
||||||
|
decoration {
|
||||||
|
rounding = 5
|
||||||
|
|
||||||
|
blur {
|
||||||
|
enabled = true
|
||||||
|
size = 3
|
||||||
|
passes = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Fade inactive windows (like XMonad's fadeInactive)
|
||||||
|
active_opacity = 1.0
|
||||||
|
inactive_opacity = 0.9
|
||||||
|
}
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# ANIMATIONS
|
||||||
|
# =============================================================================
|
||||||
|
animations {
|
||||||
|
enabled = yes
|
||||||
|
|
||||||
|
# Hyprland supports bezier curves, not true spring physics.
|
||||||
|
# Use a mild overshoot plus GNOME-like window animation style.
|
||||||
|
bezier = overshoot, 0.05, 0.9, 0.1, 1.1
|
||||||
|
bezier = smoothOut, 0.36, 1, 0.3, 1
|
||||||
|
bezier = smoothInOut, 0.42, 0, 0.58, 1
|
||||||
|
bezier = linear, 0, 0, 1, 1
|
||||||
|
|
||||||
|
# SPEED is in deciseconds (e.g. 6 == 600ms).
|
||||||
|
animation = windows, 1, 6, overshoot, gnomed
|
||||||
|
animation = windowsIn, 1, 6, overshoot, gnomed
|
||||||
|
animation = windowsOut, 1, 5, smoothInOut, gnomed
|
||||||
|
animation = windowsMove, 1, 6, smoothOut
|
||||||
|
animation = border, 0
|
||||||
|
animation = borderangle, 0
|
||||||
|
animation = fade, 1, 5, smoothOut
|
||||||
|
animation = workspaces, 1, 6, smoothOut, slidefade 15%
|
||||||
|
animation = specialWorkspace, 1, 6, smoothOut, slidevert
|
||||||
|
}
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# MASTER LAYOUT CONFIGURATION
|
||||||
|
# =============================================================================
|
||||||
|
master {
|
||||||
|
new_status = slave
|
||||||
|
mfact = 0.5
|
||||||
|
orientation = left
|
||||||
|
}
|
||||||
|
|
||||||
|
# Dwindle layout (alternative - binary tree like i3)
|
||||||
|
dwindle {
|
||||||
|
pseudotile = yes
|
||||||
|
preserve_split = yes
|
||||||
|
}
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# WORKSPACE RULES (SMART GAPS)
|
||||||
|
# =============================================================================
|
||||||
|
# Replace no_gaps_when_only (removed in newer Hyprland)
|
||||||
|
# Remove gaps when there's only one visible tiled window (ignore special workspaces)
|
||||||
|
workspace = w[tv1]s[false], gapsout:0, gapsin:0
|
||||||
|
workspace = f[1]s[false], gapsout:0, gapsin:0
|
||||||
|
|
||||||
|
# Group/tabbed window configuration (built-in alternative to hy3 tabs)
|
||||||
|
group {
|
||||||
|
col.border_active = rgba(edb443ff)
|
||||||
|
col.border_inactive = rgba(091f2eff)
|
||||||
|
|
||||||
|
groupbar {
|
||||||
|
enabled = true
|
||||||
|
font_size = 12
|
||||||
|
height = 22
|
||||||
|
col.active = rgba(edb443ff)
|
||||||
|
col.inactive = rgba(091f2eff)
|
||||||
|
text_color = rgba(091f2eff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# HY3/HYPREXPO PLUGIN CONFIG
|
||||||
|
# =============================================================================
|
||||||
|
plugin {
|
||||||
|
hy3 {
|
||||||
|
# Disable autotile to get XMonad-like manual control
|
||||||
|
autotile {
|
||||||
|
enable = false
|
||||||
|
}
|
||||||
|
|
||||||
|
# Tab configuration
|
||||||
|
tabs {
|
||||||
|
height = 22
|
||||||
|
padding = 6
|
||||||
|
render_text = true
|
||||||
|
text_font = "Sans"
|
||||||
|
text_height = 10
|
||||||
|
text_padding = 3
|
||||||
|
col.active = rgba(edb443ff)
|
||||||
|
col.inactive = rgba(091f2eff)
|
||||||
|
col.urgent = rgba(ff0000ff)
|
||||||
|
col.text.active = rgba(091f2eff)
|
||||||
|
col.text.inactive = rgba(ffffffff)
|
||||||
|
col.text.urgent = rgba(ffffffff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hyprexpo {
|
||||||
|
# Always include workspace 1 in the overview grid
|
||||||
|
workspace_method = first 1
|
||||||
|
# Only show workspaces with windows
|
||||||
|
skip_empty = true
|
||||||
|
# Show numeric workspace labels in the expo grid
|
||||||
|
show_workspace_numbers = true
|
||||||
|
# 3 columns -> 3x3 grid when 9 workspaces are visible
|
||||||
|
columns = 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# MISC
|
||||||
|
# =============================================================================
|
||||||
|
misc {
|
||||||
|
force_default_wallpaper = 0
|
||||||
|
disable_hyprland_logo = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# BINDS OPTIONS
|
||||||
|
# =============================================================================
|
||||||
|
binds {
|
||||||
|
# Keep workspace history so "previous" can toggle back reliably.
|
||||||
|
allow_workspace_cycles = true
|
||||||
|
workspace_back_and_forth = true
|
||||||
|
}
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# WINDOW RULES
|
||||||
|
# =============================================================================
|
||||||
|
# Float dialogs
|
||||||
|
windowrule = match:class ^()$, match:title ^()$, float on
|
||||||
|
windowrule = match:title ^(Picture-in-Picture)$, float on
|
||||||
|
windowrule = match:title ^(Open File)$, float on
|
||||||
|
windowrule = match:title ^(Save File)$, float on
|
||||||
|
windowrule = match:title ^(Confirm)$, float on
|
||||||
|
|
||||||
|
# Rumno OSD/notifications: treat as an overlay, not a "real" managed window.
|
||||||
|
# (Matches both class and title because rumno may set either depending on backend.)
|
||||||
|
windowrule = match:class ^(.*[Rr]umno.*)$, float on
|
||||||
|
windowrule = match:class ^(.*[Rr]umno.*)$, pin on
|
||||||
|
windowrule = match:class ^(.*[Rr]umno.*)$, center on
|
||||||
|
windowrule = match:class ^(.*[Rr]umno.*)$, decorate off
|
||||||
|
windowrule = match:class ^(.*[Rr]umno.*)$, no_shadow on
|
||||||
|
windowrule = match:title ^(.*[Rr]umno.*)$, float on
|
||||||
|
windowrule = match:title ^(.*[Rr]umno.*)$, pin on
|
||||||
|
windowrule = match:title ^(.*[Rr]umno.*)$, center on
|
||||||
|
windowrule = match:title ^(.*[Rr]umno.*)$, decorate off
|
||||||
|
windowrule = match:title ^(.*[Rr]umno.*)$, no_shadow on
|
||||||
|
|
||||||
|
# Scratchpad sizing handled by hyprscratch exec rules (see hyprland.nix)
|
||||||
|
# Using hyprscratch rules instead of windowrule to avoid affecting child windows (e.g. Slack meets)
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# KEY BINDINGS
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# Modifier keys
|
||||||
|
$mainMod = SUPER
|
||||||
|
$modAlt = SUPER ALT
|
||||||
|
$hyper = SUPER CTRL ALT
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Program Launching
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
bind = $mainMod, P, exec, $menu
|
||||||
|
bind = $mainMod SHIFT, P, exec, $runMenu
|
||||||
|
bind = $mainMod SHIFT, Return, exec, $terminal
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Overview (Hyprexpo)
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
bind = $mainMod, TAB, hyprexpo:expo, toggle
|
||||||
|
bind = $mainMod SHIFT, TAB, hyprexpo:expo, bring
|
||||||
|
bind = $mainMod, Q, killactive,
|
||||||
|
bind = $mainMod SHIFT, C, killactive,
|
||||||
|
bind = $mainMod SHIFT, Q, exit,
|
||||||
|
# Emacs-everywhere (like XMonad's emacs-everywhere)
|
||||||
|
bind = $mainMod, E, exec, emacsclient --eval '(emacs-everywhere)'
|
||||||
|
bind = $mainMod, V, exec, wl-paste | xdotool type --file -
|
||||||
|
|
||||||
|
# Chrome/Browser (raise or spawn like XMonad's bindBringAndRaise)
|
||||||
|
bind = $modAlt, C, exec, ~/.config/hypr/scripts/raise-or-run.sh google-chrome google-chrome-stable
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# SCRATCHPADS (managed by hyprscratch daemon with auto-dismiss)
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
bind = $modAlt, E, exec, hyprscratch toggle element
|
||||||
|
bind = $modAlt, G, exec, hyprscratch toggle gmail
|
||||||
|
bind = $modAlt, H, exec, hyprscratch toggle htop
|
||||||
|
bind = $modAlt, M, exec, hyprscratch toggle messages
|
||||||
|
bind = $modAlt, K, exec, hyprscratch toggle slack
|
||||||
|
bind = $modAlt, S, exec, hyprscratch toggle spotify
|
||||||
|
bind = $modAlt, T, exec, hyprscratch toggle transmission
|
||||||
|
bind = $modAlt, V, exec, hyprscratch toggle volume
|
||||||
|
bind = $modAlt, grave, exec, hyprscratch toggle dropdown
|
||||||
|
|
||||||
|
# Hidden workspace (like XMonad's NSP)
|
||||||
|
bind = $mainMod, X, movetoworkspace, special:NSP
|
||||||
|
bind = $mainMod SHIFT, X, togglespecialworkspace, NSP
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# DIRECTIONAL NAVIGATION (WASD - like XMonad Navigation2D)
|
||||||
|
# Using hy3 dispatchers for proper tree-based navigation
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Focus movement (Mod + WASD) - hy3:movefocus navigates the tree
|
||||||
|
bind = $mainMod, W, hy3:movefocus, u
|
||||||
|
bind = $mainMod, S, hy3:movefocus, d
|
||||||
|
bind = $mainMod, A, hy3:movefocus, l
|
||||||
|
bind = $mainMod, D, hy3:movefocus, r
|
||||||
|
|
||||||
|
# Move windows (Mod + Shift + WASD) - hy3:movewindow with once=true for swapping
|
||||||
|
bind = $mainMod SHIFT, W, exec, ~/.config/hypr/scripts/movewindow-follow-cursor.sh u once
|
||||||
|
bind = $mainMod SHIFT, S, exec, ~/.config/hypr/scripts/movewindow-follow-cursor.sh d once
|
||||||
|
bind = $mainMod SHIFT, A, exec, ~/.config/hypr/scripts/movewindow-follow-cursor.sh l once
|
||||||
|
bind = $mainMod SHIFT, D, exec, ~/.config/hypr/scripts/movewindow-follow-cursor.sh r once
|
||||||
|
|
||||||
|
# Resize windows (Mod + Ctrl + WASD)
|
||||||
|
binde = $mainMod CTRL, W, resizeactive, 0 -50
|
||||||
|
binde = $mainMod CTRL, S, resizeactive, 0 50
|
||||||
|
binde = $mainMod CTRL, A, resizeactive, -50 0
|
||||||
|
binde = $mainMod CTRL, D, resizeactive, 50 0
|
||||||
|
|
||||||
|
# Screen/Monitor focus (Hyper + WASD)
|
||||||
|
bind = $hyper, W, focusmonitor, u
|
||||||
|
bind = $hyper, S, focusmonitor, d
|
||||||
|
bind = $hyper, A, focusmonitor, l
|
||||||
|
bind = $hyper, D, focusmonitor, r
|
||||||
|
|
||||||
|
# Move window to monitor and follow (Hyper + Shift + WASD)
|
||||||
|
bind = $hyper SHIFT, W, movewindow, mon:u
|
||||||
|
bind = $hyper SHIFT, S, movewindow, mon:d
|
||||||
|
bind = $hyper SHIFT, A, movewindow, mon:l
|
||||||
|
bind = $hyper SHIFT, D, movewindow, mon:r
|
||||||
|
|
||||||
|
# Shift to empty workspace on screen direction (Super + Ctrl + Shift + WASD)
|
||||||
|
# Like XMonad's shiftToEmptyOnScreen
|
||||||
|
bind = $mainMod CTRL SHIFT, W, exec, ~/.config/hypr/scripts/shift-to-empty-on-screen.sh u
|
||||||
|
bind = $mainMod CTRL SHIFT, S, exec, ~/.config/hypr/scripts/shift-to-empty-on-screen.sh d
|
||||||
|
bind = $mainMod CTRL SHIFT, A, exec, ~/.config/hypr/scripts/shift-to-empty-on-screen.sh l
|
||||||
|
bind = $mainMod CTRL SHIFT, D, exec, ~/.config/hypr/scripts/shift-to-empty-on-screen.sh r
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# LAYOUT CONTROL (XMonad-like with hy3)
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Create groups with different orientations (like XMonad layouts)
|
||||||
|
# hy3:makegroup creates a split/tab group from focused window
|
||||||
|
bind = $mainMod, Space, hy3:changegroup, toggletab
|
||||||
|
bind = $mainMod SHIFT, Space, hy3:changegroup, opposite
|
||||||
|
|
||||||
|
# Create specific group types
|
||||||
|
bind = $mainMod, H, hy3:makegroup, h
|
||||||
|
bind = $mainMod SHIFT, V, hy3:makegroup, v
|
||||||
|
# Mod+Ctrl+Space mirrors Mod+Space (tabs instead of fullscreen)
|
||||||
|
bind = $mainMod CTRL, Space, hy3:changegroup, toggletab
|
||||||
|
|
||||||
|
# Change group type (cycle h -> v -> tab)
|
||||||
|
bind = $mainMod, slash, hy3:changegroup, h
|
||||||
|
bind = $mainMod SHIFT, slash, hy3:changegroup, v
|
||||||
|
|
||||||
|
# Tab navigation (like XMonad's focus next/prev in tabbed)
|
||||||
|
bind = $mainMod, bracketright, hy3:focustab, r, wrap
|
||||||
|
bind = $mainMod, bracketleft, hy3:focustab, l, wrap
|
||||||
|
|
||||||
|
# Move window within tab group (hy3 has no movetab dispatcher)
|
||||||
|
bind = $mainMod SHIFT, bracketright, hy3:movewindow, r, visible
|
||||||
|
bind = $mainMod SHIFT, bracketleft, hy3:movewindow, l, visible
|
||||||
|
|
||||||
|
# Expand focus to parent group (like XMonad's focus parent)
|
||||||
|
bind = $mainMod, grave, hy3:expand, expand
|
||||||
|
bind = $mainMod SHIFT, grave, hy3:expand, base
|
||||||
|
|
||||||
|
# Fullscreen (like XMonad's NBFULL toggle)
|
||||||
|
bind = $mainMod, F, fullscreen, 0
|
||||||
|
bind = $mainMod SHIFT, F, fullscreen, 1
|
||||||
|
|
||||||
|
# Toggle floating
|
||||||
|
bind = $mainMod, T, togglefloating,
|
||||||
|
|
||||||
|
# Resize split ratio (hy3 uses resizeactive for splits)
|
||||||
|
binde = $mainMod, comma, resizeactive, -50 0
|
||||||
|
binde = $mainMod, period, resizeactive, 50 0
|
||||||
|
|
||||||
|
# Equalize window sizes on workspace (hy3)
|
||||||
|
bind = $mainMod SHIFT, equal, hy3:equalize, workspace
|
||||||
|
|
||||||
|
# Kill group - removes the focused window from its group
|
||||||
|
bind = $mainMod, N, hy3:killactive
|
||||||
|
|
||||||
|
# hy3:setswallow - set a window to swallow newly spawned windows
|
||||||
|
bind = $mainMod CTRL, M, hy3:setswallow, toggle
|
||||||
|
|
||||||
|
# Minimize/unminimize (via special workspace)
|
||||||
|
bind = $mainMod, M, exec, ~/.config/hypr/scripts/minimize-active.sh minimized
|
||||||
|
bind = $mainMod SHIFT, M, exec, ~/.config/hypr/scripts/unminimize-last.sh minimized
|
||||||
|
|
||||||
|
# Minimized "picker" mode:
|
||||||
|
# Open the minimized special workspace, focus a window, press Enter to restore it.
|
||||||
|
bind = $modAlt, Return, exec, ~/.config/hypr/scripts/minimized-mode.sh minimized
|
||||||
|
|
||||||
|
submap = minimized
|
||||||
|
bind = , Return, exec, ~/.config/hypr/scripts/unminimize-last.sh minimized; hyprctl dispatch submap reset
|
||||||
|
bind = , Escape, exec, ~/.config/hypr/scripts/minimized-cancel.sh minimized
|
||||||
|
bind = $modAlt, Return, exec, ~/.config/hypr/scripts/minimized-cancel.sh minimized
|
||||||
|
|
||||||
|
# Optional: basic focus navigation inside the picker.
|
||||||
|
bind = , H, movefocus, l
|
||||||
|
bind = , J, movefocus, d
|
||||||
|
bind = , K, movefocus, u
|
||||||
|
bind = , L, movefocus, r
|
||||||
|
bind = , left, movefocus, l
|
||||||
|
bind = , down, movefocus, d
|
||||||
|
bind = , up, movefocus, u
|
||||||
|
bind = , right, movefocus, r
|
||||||
|
|
||||||
|
submap = reset
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# WORKSPACE CONTROL
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Switch workspaces (1-9 only) on the currently focused monitor.
|
||||||
|
bind = $mainMod, 1, focusworkspaceoncurrentmonitor, 1
|
||||||
|
bind = $mainMod, 2, focusworkspaceoncurrentmonitor, 2
|
||||||
|
bind = $mainMod, 3, focusworkspaceoncurrentmonitor, 3
|
||||||
|
bind = $mainMod, 4, focusworkspaceoncurrentmonitor, 4
|
||||||
|
bind = $mainMod, 5, focusworkspaceoncurrentmonitor, 5
|
||||||
|
bind = $mainMod, 6, focusworkspaceoncurrentmonitor, 6
|
||||||
|
bind = $mainMod, 7, focusworkspaceoncurrentmonitor, 7
|
||||||
|
bind = $mainMod, 8, focusworkspaceoncurrentmonitor, 8
|
||||||
|
bind = $mainMod, 9, focusworkspaceoncurrentmonitor, 9
|
||||||
|
|
||||||
|
# Move window to workspace
|
||||||
|
bind = $mainMod SHIFT, 1, movetoworkspace, 1
|
||||||
|
bind = $mainMod SHIFT, 2, movetoworkspace, 2
|
||||||
|
bind = $mainMod SHIFT, 3, movetoworkspace, 3
|
||||||
|
bind = $mainMod SHIFT, 4, movetoworkspace, 4
|
||||||
|
bind = $mainMod SHIFT, 5, movetoworkspace, 5
|
||||||
|
bind = $mainMod SHIFT, 6, movetoworkspace, 6
|
||||||
|
bind = $mainMod SHIFT, 7, movetoworkspace, 7
|
||||||
|
bind = $mainMod SHIFT, 8, movetoworkspace, 8
|
||||||
|
bind = $mainMod SHIFT, 9, movetoworkspace, 9
|
||||||
|
|
||||||
|
# Move and follow to workspace (like XMonad's shiftThenView)
|
||||||
|
bind = $mainMod CTRL, 1, movetoworkspacesilent, 1
|
||||||
|
bind = $mainMod CTRL, 1, focusworkspaceoncurrentmonitor, 1
|
||||||
|
bind = $mainMod CTRL, 2, movetoworkspacesilent, 2
|
||||||
|
bind = $mainMod CTRL, 2, focusworkspaceoncurrentmonitor, 2
|
||||||
|
bind = $mainMod CTRL, 3, movetoworkspacesilent, 3
|
||||||
|
bind = $mainMod CTRL, 3, focusworkspaceoncurrentmonitor, 3
|
||||||
|
bind = $mainMod CTRL, 4, movetoworkspacesilent, 4
|
||||||
|
bind = $mainMod CTRL, 4, focusworkspaceoncurrentmonitor, 4
|
||||||
|
bind = $mainMod CTRL, 5, movetoworkspacesilent, 5
|
||||||
|
bind = $mainMod CTRL, 5, focusworkspaceoncurrentmonitor, 5
|
||||||
|
bind = $mainMod CTRL, 6, movetoworkspacesilent, 6
|
||||||
|
bind = $mainMod CTRL, 6, focusworkspaceoncurrentmonitor, 6
|
||||||
|
bind = $mainMod CTRL, 7, movetoworkspacesilent, 7
|
||||||
|
bind = $mainMod CTRL, 7, focusworkspaceoncurrentmonitor, 7
|
||||||
|
bind = $mainMod CTRL, 8, movetoworkspacesilent, 8
|
||||||
|
bind = $mainMod CTRL, 8, focusworkspaceoncurrentmonitor, 8
|
||||||
|
bind = $mainMod CTRL, 9, movetoworkspacesilent, 9
|
||||||
|
bind = $mainMod CTRL, 9, focusworkspaceoncurrentmonitor, 9
|
||||||
|
|
||||||
|
# Toggle to the previous workspace on the current monitor using Hyprland's
|
||||||
|
# built-in per-monitor workspace history.
|
||||||
|
bind = $mainMod, backslash, workspace, previous_per_monitor
|
||||||
|
|
||||||
|
# Swap current workspace with another (like XMonad's swapWithCurrent)
|
||||||
|
bind = $hyper, 5, exec, ~/.config/hypr/scripts/swap-workspaces.sh
|
||||||
|
|
||||||
|
# Go to next empty workspace (like XMonad's moveTo Next emptyWS)
|
||||||
|
bind = $hyper, E, exec, ~/.config/hypr/scripts/workspace-goto-empty.sh
|
||||||
|
|
||||||
|
# Move to next screen (like XMonad's shiftToNextScreenX)
|
||||||
|
bind = $mainMod, Z, focusmonitor, +1
|
||||||
|
bind = $mainMod SHIFT, Z, movewindow, mon:+1
|
||||||
|
|
||||||
|
# Shift to empty workspace and view (like XMonad's shiftToEmptyAndView)
|
||||||
|
bind = $mainMod SHIFT, H, exec, ~/.config/hypr/scripts/workspace-move-to-empty.sh
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# WINDOW MANAGEMENT
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Go to window (rofi window switcher with icons)
|
||||||
|
bind = $mainMod, G, exec, ~/.config/hypr/scripts/go-to-window.sh
|
||||||
|
|
||||||
|
# Bring window (move to current workspace)
|
||||||
|
bind = $mainMod, B, exec, ~/.config/hypr/scripts/bring-window.sh
|
||||||
|
|
||||||
|
# Replace window (swap focused with selected - like XMonad's myReplaceWindow)
|
||||||
|
bind = $mainMod SHIFT, B, exec, ~/.config/hypr/scripts/replace-window.sh
|
||||||
|
|
||||||
|
# Gather windows of same class (like XMonad's gatherThisClass)
|
||||||
|
bind = $hyper, G, exec, ~/.config/hypr/scripts/gather-class.sh
|
||||||
|
|
||||||
|
# Focus next window of different class (like XMonad's focusNextClass)
|
||||||
|
bind = $mainMod, apostrophe, exec, ~/.config/hypr/scripts/focus-next-class.sh
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# MEDIA KEYS
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Volume control (matching XMonad: Mod+I=up, Mod+K=down, Mod+U=mute)
|
||||||
|
binde = , XF86AudioRaiseVolume, exec, set_volume --unmute --change-volume +5
|
||||||
|
binde = , XF86AudioLowerVolume, exec, set_volume --unmute --change-volume -5
|
||||||
|
bind = , XF86AudioMute, exec, set_volume --toggle-mute
|
||||||
|
binde = $mainMod, I, exec, set_volume --unmute --change-volume +5
|
||||||
|
binde = $mainMod, K, exec, set_volume --unmute --change-volume -5
|
||||||
|
bind = $mainMod, U, exec, set_volume --toggle-mute
|
||||||
|
|
||||||
|
# Media player controls (matching XMonad: Mod+;=play, Mod+L=next, Mod+J=prev)
|
||||||
|
bind = $mainMod, semicolon, exec, playerctl play-pause
|
||||||
|
bind = , XF86AudioPlay, exec, playerctl play-pause
|
||||||
|
bind = , XF86AudioPause, exec, playerctl play-pause
|
||||||
|
bind = $mainMod, L, exec, playerctl next
|
||||||
|
bind = , XF86AudioNext, exec, playerctl next
|
||||||
|
bind = $mainMod, J, exec, playerctl previous
|
||||||
|
bind = , XF86AudioPrev, exec, playerctl previous
|
||||||
|
|
||||||
|
# Mute current window (like XMonad's toggle_mute_current_window)
|
||||||
|
bind = $hyper SHIFT, Q, exec, toggle_mute_current_window.sh
|
||||||
|
bind = $hyper CTRL, Q, exec, toggle_mute_current_window.sh only
|
||||||
|
|
||||||
|
# Brightness control
|
||||||
|
binde = , XF86MonBrightnessUp, exec, brightness.sh up
|
||||||
|
binde = , XF86MonBrightnessDown, exec, brightness.sh down
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# UTILITY BINDINGS
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
bind = $hyper, V, exec, cliphist list | rofi -dmenu -p "Clipboard" | cliphist decode | wl-copy
|
||||||
|
bind = $hyper, P, exec, rofi-pass
|
||||||
|
bind = $hyper, H, exec, grim -g "$(slurp)" - | swappy -f -
|
||||||
|
bind = $hyper, C, exec, shell_command.sh
|
||||||
|
bind = $hyper, X, exec, rofi_command.sh
|
||||||
|
bind = $hyper SHIFT, L, exec, hyprlock
|
||||||
|
bind = $hyper, K, exec, rofi_kill_process.sh
|
||||||
|
bind = $hyper SHIFT, K, exec, rofi_kill_all.sh
|
||||||
|
bind = $hyper, R, exec, rofi-systemd
|
||||||
|
bind = $hyper, slash, exec, toggle_taffybar
|
||||||
|
bind = $hyper, 9, exec, start_synergy.sh
|
||||||
|
bind = $hyper, I, exec, rofi_select_input.hs
|
||||||
|
bind = $hyper, backslash, exec, /home/imalison/dotfiles/dotfiles/lib/functions/mpg341cx_input toggle
|
||||||
|
bind = $hyper, O, exec, rofi_paswitch
|
||||||
|
bind = $hyper, comma, exec, rofi_wallpaper.sh
|
||||||
|
bind = $hyper, Y, exec, rofi_agentic_skill
|
||||||
|
|
||||||
|
# Reload config
|
||||||
|
bind = $mainMod, R, exec, hyprctl reload
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# MOUSE BINDINGS
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
bindm = $mainMod, mouse:272, movewindow
|
||||||
|
bindm = $mainMod, mouse:273, resizewindow
|
||||||
|
|
||||||
|
# Scroll through workspaces
|
||||||
|
bind = $mainMod, mouse_down, exec, ~/.config/hypr/scripts/workspace-scroll.sh +1
|
||||||
|
bind = $mainMod, mouse_up, exec, ~/.config/hypr/scripts/workspace-scroll.sh -1
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# AUTOSTART
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
# Wire Hyprland into Home Manager's standard user-session targets.
|
||||||
|
# `graphical-session.target` pulls in most tray/SNI applets (which in turn pull in `tray.target`).
|
||||||
|
# Keep the systemd user manager in sync with the current Hyprland session before
|
||||||
|
# starting any session-bound units. Separate `exec-once` commands race.
|
||||||
|
exec-once = sh -lc 'export IMALISON_SESSION_TYPE=wayland; dbus-update-activation-environment --systemd WAYLAND_DISPLAY DISPLAY XAUTHORITY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP XDG_SESSION_TYPE IMALISON_SESSION_TYPE; systemctl --user start graphical-session.target hyprland-session.target'
|
||||||
|
# Force a fresh daemon after compositor restarts so hyprscratch doesn't keep a stale socket.
|
||||||
|
exec-once = systemctl --user restart hyprscratch.service
|
||||||
|
exec-once = hypridle
|
||||||
|
|
||||||
|
# Clipboard history daemon
|
||||||
|
exec-once = wl-paste --type text --watch cliphist store
|
||||||
|
exec-once = wl-paste --type image --watch cliphist store
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,332 +0,0 @@
|
|||||||
local M = {}
|
|
||||||
|
|
||||||
function M.setup(ctx)
|
|
||||||
local _ENV = ctx
|
|
||||||
|
|
||||||
local function desc(description, opts)
|
|
||||||
local bind_opts = {}
|
|
||||||
for key, value in pairs(opts or {}) do
|
|
||||||
bind_opts[key] = value
|
|
||||||
end
|
|
||||||
bind_opts.description = description
|
|
||||||
return bind_opts
|
|
||||||
end
|
|
||||||
|
|
||||||
local function setup_launcher_and_app_bindings()
|
|
||||||
bind(main_mod .. " + P", exec(launcher_command), desc("Open application launcher"))
|
|
||||||
bind(main_mod .. " + SHIFT + P", exec(run_menu), desc("Open command runner"))
|
|
||||||
bind(main_mod .. " + SHIFT + Return", exec(terminal), desc("Open terminal"))
|
|
||||||
bind(main_mod .. " + E", exec("emacsclient --eval '(emacs-everywhere)'"), desc("Open Emacs Everywhere"))
|
|
||||||
bind(main_mod .. " + V", exec("wl-paste --no-newline | ydotool type --file -"), desc("Type clipboard contents"))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function setup_shell_and_session_bindings()
|
|
||||||
bind(hyper .. " + SHIFT + N", exec(shell_ui_command .. " control-center"), desc("Open control center"))
|
|
||||||
bind(hyper .. " + CTRL + N", exec(shell_ui_command .. " settings"), desc("Open system settings"))
|
|
||||||
bind(main_mod .. " + Q", exec("hyprctl reload"), desc("Reload Hyprland"))
|
|
||||||
bind(main_mod .. " + R", exec("hyprctl reload"), desc("Reload Hyprland"))
|
|
||||||
bind(hyper .. " + SHIFT + L", exec("hyprlock"), desc("Lock screen"))
|
|
||||||
bind(hyper .. " + SHIFT + V", toggle_visual_performance_mode, desc("Toggle Hyprland performance mode"))
|
|
||||||
bind(hyper .. " + slash", function()
|
|
||||||
hl.exec_cmd("toggle_taffybar")
|
|
||||||
refresh_monitor_reserved_cache(0.25)
|
|
||||||
refresh_active_scratchpad_geometries_later(600)
|
|
||||||
end, desc("Toggle taffybar"))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function setup_audio_media_bindings()
|
|
||||||
bind(main_mod .. " + I", exec("set_volume --unmute --change-volume +5"), desc("Raise volume", { repeating = true }))
|
|
||||||
bind(main_mod .. " + K", exec("set_volume --unmute --change-volume -5"), desc("Lower volume", { repeating = true }))
|
|
||||||
bind(main_mod .. " + U", exec("set_volume --toggle-mute"), desc("Toggle mute"))
|
|
||||||
bind(main_mod .. " + semicolon", exec("playerctl play-pause"), desc("Play or pause media"))
|
|
||||||
bind(main_mod .. " + L", exec("playerctl next"), desc("Skip to next media track"))
|
|
||||||
bind(main_mod .. " + J", exec("playerctl previous"), desc("Skip to previous media track"))
|
|
||||||
|
|
||||||
bind("XF86AudioPlay", exec("playerctl play-pause"), desc("Play or pause media"))
|
|
||||||
bind("XF86AudioPause", exec("playerctl play-pause"), desc("Play or pause media"))
|
|
||||||
bind("XF86AudioNext", exec("playerctl next"), desc("Skip to next media track"))
|
|
||||||
bind("XF86AudioPrev", exec("playerctl previous"), desc("Skip to previous media track"))
|
|
||||||
bind("XF86AudioRaiseVolume", exec("set_volume --unmute --change-volume +5"), desc("Raise volume", { repeating = true }))
|
|
||||||
bind("XF86AudioLowerVolume", exec("set_volume --unmute --change-volume -5"), desc("Lower volume", { repeating = true }))
|
|
||||||
bind("XF86AudioMute", exec("set_volume --toggle-mute"), desc("Toggle mute"))
|
|
||||||
bind(hyper .. " + O", exec("/home/imalison/dotfiles/dotfiles/lib/functions/rofi_paswitch"), desc("Open PulseAudio output switcher"))
|
|
||||||
bind(hyper .. " + SHIFT + O", exec("/home/imalison/dotfiles/dotfiles/lib/bin/kef-optical"), desc("Switch KEF speakers to optical input"))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function setup_display_wallpaper_and_capture_bindings()
|
|
||||||
bind("XF86MonBrightnessUp", exec("brightness.sh up"), desc("Raise display brightness", { repeating = true }))
|
|
||||||
bind("XF86MonBrightnessDown", exec("brightness.sh down"), desc("Lower display brightness", { repeating = true }))
|
|
||||||
bind("Print", exec("flameshot gui"), desc("Take screenshot"))
|
|
||||||
bind(hyper .. " + H", exec("flameshot gui"), desc("Take screenshot"))
|
|
||||||
bind(hyper .. " + backslash", exec("/home/imalison/dotfiles/dotfiles/lib/functions/mpg341cx_input toggle"), desc("Toggle monitor input"))
|
|
||||||
bind(hyper .. " + comma", exec("rofi_wallpaper.sh"), desc("Open wallpaper menu"))
|
|
||||||
bind(hyper .. " + SHIFT + comma", exec("/home/imalison/dotfiles/dotfiles/lib/bin/neowall-wallpaper toggle"), desc("Toggle neowall wallpaper"))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function setup_rofi_and_tool_bindings()
|
|
||||||
bind(main_mod .. " + X", exec("rofi_command.sh"), desc("Open command menu"))
|
|
||||||
bind(hyper .. " + V", exec([[cliphist list | rofi -dmenu -p "Clipboard" | cliphist decode | wl-copy]]), desc("Open clipboard history"))
|
|
||||||
bind(hyper .. " + P", exec("rofi-pass"), desc("Open password menu"))
|
|
||||||
bind(hyper .. " + C", exec("rofi_tmcodex.sh"), desc("Open Codex session menu"))
|
|
||||||
bind(hyper .. " + SHIFT + C", exec("rofi_tmcodex.sh resume"), desc("Resume Codex session"))
|
|
||||||
bind(hyper .. " + L", exec("hypr_rofi_layout"), desc("Open Hyprland layout menu"))
|
|
||||||
bind(hyper .. " + K", exec("rofi_kill_process.sh"), desc("Open process kill menu"))
|
|
||||||
bind(hyper .. " + SHIFT + K", exec("rofi_kill_all.sh"), desc("Open kill-all menu"))
|
|
||||||
bind(hyper .. " + R", exec("rofi_systemd_mono"), desc("Open systemd unit menu"))
|
|
||||||
bind(hyper .. " + X", exec("hypr_rofi_action"), desc("Open Hyprland action menu"))
|
|
||||||
bind(hyper .. " + I", exec("rofi_select_input.hs"), desc("Open input selection menu"))
|
|
||||||
bind(hyper .. " + Y", exec("rofi_agentic_skill"), desc("Open agentic skill menu"))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function setup_external_command_bindings()
|
|
||||||
setup_launcher_and_app_bindings()
|
|
||||||
setup_shell_and_session_bindings()
|
|
||||||
setup_audio_media_bindings()
|
|
||||||
setup_display_wallpaper_and_capture_bindings()
|
|
||||||
setup_rofi_and_tool_bindings()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function setup_window_overview_bindings()
|
|
||||||
bind(main_mod .. " + SHIFT + C", hl.dsp.window.close(), desc("Close active window"))
|
|
||||||
bind(main_mod .. " + SHIFT + Q", hl.dsp.exit(), desc("Exit Hyprland"))
|
|
||||||
bind(main_mod .. " + Tab", hyprexpo("toggle"), desc("Toggle hyprexpo workspace overview", overview_bind_opts))
|
|
||||||
bind(main_mod .. " + SHIFT + Tab", hyprwinview({
|
|
||||||
action = "show",
|
|
||||||
include_current_workspace = false,
|
|
||||||
start_in_filter_mode = true,
|
|
||||||
default_action = "bring",
|
|
||||||
}), desc("Show all-workspace window overview", overview_bind_opts))
|
|
||||||
bind(main_mod .. " + SHIFT + slash", hyprwinview({ action = "toggle-filter" }), desc("Toggle window overview filter", overview_bind_opts))
|
|
||||||
bind("ALT + Tab", hyprexpo("toggle"), desc("Toggle hyprexpo workspace overview", overview_bind_opts))
|
|
||||||
bind("ALT + SHIFT + Tab", hyprexpo("on"), desc("Open hyprexpo workspace overview", overview_bind_opts))
|
|
||||||
bind(main_mod .. " + G", hyprwinview({
|
|
||||||
action = "show",
|
|
||||||
start_in_filter_mode = true,
|
|
||||||
default_action = "select",
|
|
||||||
}), desc("Show window overview", overview_bind_opts))
|
|
||||||
bind(main_mod .. " + B", hyprwinview({
|
|
||||||
action = "show",
|
|
||||||
start_in_filter_mode = true,
|
|
||||||
default_action = "bring",
|
|
||||||
}), desc("Bring window from overview", overview_bind_opts))
|
|
||||||
bind(main_mod .. " + SHIFT + B", hyprwinview({
|
|
||||||
action = "show",
|
|
||||||
start_in_filter_mode = true,
|
|
||||||
default_action = "bring-replace",
|
|
||||||
}), desc("Replace active window from overview", overview_bind_opts))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function setup_window_focus_and_move_bindings()
|
|
||||||
bind(main_mod .. " + W", function()
|
|
||||||
focus_direction("up")
|
|
||||||
end, desc("Focus window above"))
|
|
||||||
bind(main_mod .. " + S", function()
|
|
||||||
focus_direction("down")
|
|
||||||
end, desc("Focus window below"))
|
|
||||||
bind(main_mod .. " + A", function()
|
|
||||||
focus_direction("left")
|
|
||||||
end, desc("Focus window to the left"))
|
|
||||||
bind(main_mod .. " + D", function()
|
|
||||||
focus_direction("right")
|
|
||||||
end, desc("Focus window to the right"))
|
|
||||||
|
|
||||||
bind(main_mod .. " + SHIFT + W", function()
|
|
||||||
swap_direction("up")
|
|
||||||
end, desc("Swap active window upward"))
|
|
||||||
bind(main_mod .. " + SHIFT + S", function()
|
|
||||||
swap_direction("down")
|
|
||||||
end, desc("Swap active window downward"))
|
|
||||||
bind(main_mod .. " + SHIFT + A", function()
|
|
||||||
swap_direction("left")
|
|
||||||
end, desc("Swap active window left"))
|
|
||||||
bind(main_mod .. " + SHIFT + D", function()
|
|
||||||
swap_direction("right")
|
|
||||||
end, desc("Swap active window right"))
|
|
||||||
|
|
||||||
bind(main_mod .. " + CTRL + W", function()
|
|
||||||
move_window_to_monitor("u", false)
|
|
||||||
end, desc("Move window to monitor above"))
|
|
||||||
bind(main_mod .. " + CTRL + S", function()
|
|
||||||
move_window_to_monitor("d", false)
|
|
||||||
end, desc("Move window to monitor below"))
|
|
||||||
bind(main_mod .. " + CTRL + A", function()
|
|
||||||
move_window_to_monitor("l", false)
|
|
||||||
end, desc("Move window to monitor on the left"))
|
|
||||||
bind(main_mod .. " + CTRL + D", function()
|
|
||||||
move_window_to_monitor("r", false)
|
|
||||||
end, desc("Move window to monitor on the right"))
|
|
||||||
bind(main_mod .. " + CTRL + SHIFT + W", function()
|
|
||||||
move_window_to_empty_workspace_on_monitor("u")
|
|
||||||
end, desc("Move window to empty workspace on monitor above"))
|
|
||||||
bind(main_mod .. " + CTRL + SHIFT + S", function()
|
|
||||||
move_window_to_empty_workspace_on_monitor("d")
|
|
||||||
end, desc("Move window to empty workspace on monitor below"))
|
|
||||||
bind(main_mod .. " + CTRL + SHIFT + A", function()
|
|
||||||
move_window_to_empty_workspace_on_monitor("l")
|
|
||||||
end, desc("Move window to empty workspace on left monitor"))
|
|
||||||
bind(main_mod .. " + CTRL + SHIFT + D", function()
|
|
||||||
move_window_to_empty_workspace_on_monitor("r")
|
|
||||||
end, desc("Move window to empty workspace on right monitor"))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function setup_submap_bindings()
|
|
||||||
hl.define_submap("swap-workspace", function()
|
|
||||||
for i = 1, 9 do
|
|
||||||
local workspace_id = i
|
|
||||||
bind(tostring(i), function()
|
|
||||||
swap_current_workspace_with(workspace_id)
|
|
||||||
dispatch(hl.dsp.submap("reset"))
|
|
||||||
end, desc("Swap current workspace with workspace " .. workspace_id))
|
|
||||||
end
|
|
||||||
|
|
||||||
bind("Escape", hl.dsp.submap("reset"), desc("Exit workspace swap mode"))
|
|
||||||
bind("catchall", hl.dsp.submap("reset"), desc("Exit workspace swap mode"))
|
|
||||||
end)
|
|
||||||
|
|
||||||
hl.define_submap("window-picker", function()
|
|
||||||
for i = 1, 9 do
|
|
||||||
local index = i
|
|
||||||
bind(tostring(i), function()
|
|
||||||
activate_window_picker_candidate(index)
|
|
||||||
end, desc("Activate window picker candidate " .. index))
|
|
||||||
end
|
|
||||||
|
|
||||||
bind("Escape", hl.dsp.submap("reset"), desc("Exit window picker"))
|
|
||||||
bind("catchall", hl.dsp.submap("reset"), desc("Exit window picker"))
|
|
||||||
end)
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
local function setup_window_resize_and_monitor_bindings()
|
|
||||||
bind(mod_alt .. " + SHIFT + W", hl.dsp.window.resize({ x = 0, y = -50, relative = true }), desc("Shrink window height upward", { repeating = true }))
|
|
||||||
bind(mod_alt .. " + SHIFT + S", hl.dsp.window.resize({ x = 0, y = 50, relative = true }), desc("Grow window height downward", { repeating = true }))
|
|
||||||
bind(mod_alt .. " + SHIFT + A", hl.dsp.window.resize({ x = -50, y = 0, relative = true }), desc("Shrink window width leftward", { repeating = true }))
|
|
||||||
bind(mod_alt .. " + SHIFT + D", hl.dsp.window.resize({ x = 50, y = 0, relative = true }), desc("Grow window width rightward", { repeating = true }))
|
|
||||||
|
|
||||||
bind(hyper .. " + W", hl.dsp.focus({ monitor = "u" }), desc("Focus monitor above"))
|
|
||||||
bind(hyper .. " + S", hl.dsp.focus({ monitor = "d" }), desc("Focus monitor below"))
|
|
||||||
bind(hyper .. " + A", hl.dsp.focus({ monitor = "l" }), desc("Focus monitor on the left"))
|
|
||||||
bind(hyper .. " + D", hl.dsp.focus({ monitor = "r" }), desc("Focus monitor on the right"))
|
|
||||||
bind(hyper .. " + SHIFT + W", function()
|
|
||||||
move_window_to_monitor("u", true)
|
|
||||||
end, desc("Move window to monitor above and follow"))
|
|
||||||
bind(hyper .. " + SHIFT + S", function()
|
|
||||||
move_window_to_monitor("d", true)
|
|
||||||
end, desc("Move window to monitor below and follow"))
|
|
||||||
bind(hyper .. " + SHIFT + A", function()
|
|
||||||
move_window_to_monitor("l", true)
|
|
||||||
end, desc("Move window to left monitor and follow"))
|
|
||||||
bind(hyper .. " + SHIFT + D", function()
|
|
||||||
move_window_to_monitor("r", true)
|
|
||||||
end, desc("Move window to right monitor and follow"))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function setup_layout_and_window_state_bindings()
|
|
||||||
bind(main_mod .. " + Space", cycle_layout_or_restore_tabbed_group, desc("Cycle workspace layout"))
|
|
||||||
bind(main_mod .. " + SHIFT + Space", force_columns_layout, desc("Force columns layout"))
|
|
||||||
bind(main_mod .. " + CTRL + Space", gather_workspace_into_tabbed_group, desc("Gather workspace into tabbed group"))
|
|
||||||
bind(main_mod .. " + bracketright", monocle_next, desc("Focus next monocle window"))
|
|
||||||
bind(main_mod .. " + bracketleft", monocle_prev, desc("Focus previous monocle window"))
|
|
||||||
bind(main_mod .. " + T", hl.dsp.window.float({ action = "disable" }), desc("Tile active window"))
|
|
||||||
bind(main_mod .. " + O", toggle_pinned_active_window, desc("Toggle pinned active window"))
|
|
||||||
bind(main_mod .. " + M", minimize_active_window, desc("Minimize active window"))
|
|
||||||
bind(main_mod .. " + SHIFT + M", restore_last_minimized, desc("Restore last minimized window"))
|
|
||||||
bind(main_mod .. " + CTRL + SHIFT + M", function()
|
|
||||||
enter_window_picker("minimized")
|
|
||||||
end, desc("Pick minimized window to restore"))
|
|
||||||
bind(main_mod .. " + SHIFT + equal", schedule_nstack_count_update, desc("Update nstack window count"))
|
|
||||||
bind(main_mod .. " + CTRL + M", hl.dsp.window.toggle_swallow(), desc("Toggle window swallowing"))
|
|
||||||
bind(main_mod .. " + SHIFT + E", function()
|
|
||||||
move_to_next_empty_workspace(true)
|
|
||||||
end, desc("Move to next empty workspace"))
|
|
||||||
bind(main_mod .. " + CTRL + E", function()
|
|
||||||
move_to_next_empty_workspace(false)
|
|
||||||
end, desc("Move window to next empty workspace"))
|
|
||||||
bind(main_mod .. " + apostrophe", focus_next_class, desc("Focus next window class"))
|
|
||||||
bind(hyper .. " + 1", toggle_inactive_opacity_for_active_window, desc("Toggle inactive opacity reduction for active window"))
|
|
||||||
bind(mod_alt .. " + W", show_active_window_info, desc("Show active window info"))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function setup_scratchpad_bindings()
|
|
||||||
bind(main_mod .. " + SHIFT + X", hl.dsp.workspace.toggle_special("NSP"), desc("Toggle NSP special workspace"))
|
|
||||||
bind(mod_alt .. " + C", function()
|
|
||||||
toggle_scratchpad("codex")
|
|
||||||
end, desc("Toggle Codex scratchpad"))
|
|
||||||
bind(mod_alt .. " + E", function()
|
|
||||||
toggle_scratchpad("element")
|
|
||||||
end, desc("Toggle Element scratchpad"))
|
|
||||||
bind(mod_alt .. " + H", function()
|
|
||||||
toggle_scratchpad("htop")
|
|
||||||
end, desc("Toggle htop scratchpad"))
|
|
||||||
bind(mod_alt .. " + K", function()
|
|
||||||
toggle_scratchpad("slack")
|
|
||||||
end, desc("Toggle Slack scratchpad"))
|
|
||||||
bind(mod_alt .. " + M", function()
|
|
||||||
toggle_scratchpad("messages")
|
|
||||||
end, desc("Toggle Messages scratchpad"))
|
|
||||||
bind(mod_alt .. " + S", function()
|
|
||||||
toggle_scratchpad("spotify")
|
|
||||||
end, desc("Toggle Spotify scratchpad"))
|
|
||||||
bind(mod_alt .. " + T", function()
|
|
||||||
toggle_scratchpad("transmission")
|
|
||||||
end, desc("Toggle Transmission scratchpad"))
|
|
||||||
bind(mod_alt .. " + V", function()
|
|
||||||
toggle_scratchpad("volume")
|
|
||||||
end, desc("Toggle volume scratchpad"))
|
|
||||||
bind(mod_alt .. " + grave", function()
|
|
||||||
toggle_scratchpad("dropdown")
|
|
||||||
end, desc("Toggle dropdown scratchpad"))
|
|
||||||
bind(mod_alt .. " + Space", minimize_other_classes, desc("Minimize other window classes"))
|
|
||||||
bind(mod_alt .. " + SHIFT + Space", restore_focused_class, desc("Restore focused window class"))
|
|
||||||
bind(mod_alt .. " + Return", restore_all_minimized, desc("Restore all minimized windows"))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function setup_workspace_bindings()
|
|
||||||
for i = 1, 9 do
|
|
||||||
local workspace = tostring(i)
|
|
||||||
bind(main_mod .. " + " .. workspace, hl.dsp.focus({ workspace = workspace, on_current_monitor = true }), desc("Focus workspace " .. workspace))
|
|
||||||
bind(main_mod .. " + SHIFT + " .. workspace, hl.dsp.window.move({ workspace = workspace, follow = false }), desc("Move window to workspace " .. workspace))
|
|
||||||
bind(main_mod .. " + CTRL + " .. workspace, function()
|
|
||||||
dispatch(hl.dsp.window.move({ workspace = workspace, follow = false }))
|
|
||||||
dispatch(hl.dsp.focus({ workspace = workspace, on_current_monitor = true }))
|
|
||||||
end, desc("Move window to workspace " .. workspace .. " and follow"))
|
|
||||||
end
|
|
||||||
|
|
||||||
bind(main_mod .. " + backslash", workspacehistory("cycle", 1), desc("Cycle to next workspace in history"))
|
|
||||||
bind(main_mod .. " + slash", workspacehistory("cycle", -1), desc("Cycle to previous workspace in history"))
|
|
||||||
bind(main_mod .. " + Escape", workspacehistory("cancel"), desc("Cancel workspace history cycle"))
|
|
||||||
bind(main_mod .. " + Z", hl.dsp.focus({ monitor = "+1" }), desc("Focus next monitor"))
|
|
||||||
bind(main_mod .. " + SHIFT + Z", hl.dsp.window.move({ monitor = "+1" }), desc("Move window to next monitor"))
|
|
||||||
bind(main_mod .. " + mouse_down", function()
|
|
||||||
cycle_workspace(1)
|
|
||||||
end, desc("Cycle to next workspace"))
|
|
||||||
bind(main_mod .. " + mouse_up", function()
|
|
||||||
cycle_workspace(-1)
|
|
||||||
end, desc("Cycle to previous workspace"))
|
|
||||||
bind(hyper .. " + E", focus_next_empty_workspace, desc("Focus next empty workspace"))
|
|
||||||
bind(hyper .. " + 5", enter_workspace_swap_mode, desc("Enter workspace swap mode"))
|
|
||||||
bind(hyper .. " + G", gather_focused_class, desc("Gather focused window class"))
|
|
||||||
bind(hyper .. " + SHIFT + backslash", workspacehistory("debug"), desc("Show workspace history debug info"))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function setup_mouse_bindings()
|
|
||||||
bind(main_mod .. " + mouse:272", float_and_drag_active_window, desc("Float and drag active window"))
|
|
||||||
bind(main_mod .. " + mouse:273", float_and_resize_active_window, desc("Float and resize active window"))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function setup_internal_window_manager_bindings()
|
|
||||||
setup_window_overview_bindings()
|
|
||||||
setup_window_focus_and_move_bindings()
|
|
||||||
setup_submap_bindings()
|
|
||||||
setup_window_resize_and_monitor_bindings()
|
|
||||||
setup_layout_and_window_state_bindings()
|
|
||||||
setup_scratchpad_bindings()
|
|
||||||
setup_workspace_bindings()
|
|
||||||
setup_mouse_bindings()
|
|
||||||
end
|
|
||||||
|
|
||||||
setup_external_command_bindings()
|
|
||||||
setup_internal_window_manager_bindings()
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
|
||||||
@@ -1,615 +0,0 @@
|
|||||||
local M = {}
|
|
||||||
|
|
||||||
function M.setup(ctx)
|
|
||||||
local _ENV = ctx
|
|
||||||
local function command_line_contains(needle)
|
|
||||||
local command_line = io.open("/proc/self/cmdline", "rb")
|
|
||||||
if not command_line then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local contents = command_line:read("*a") or ""
|
|
||||||
command_line:close()
|
|
||||||
return contents:find(needle, 1, true) ~= nil
|
|
||||||
end
|
|
||||||
|
|
||||||
verify_config = command_line_contains("--verify-config")
|
|
||||||
dev_session = os.getenv("IMALISON_HYPRLAND_DEV_SESSION") == "1"
|
|
||||||
|
|
||||||
local function exec(command)
|
|
||||||
return hl.dsp.exec_cmd(command)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function dispatch(dispatcher)
|
|
||||||
return hl.dispatch(dispatcher)
|
|
||||||
end
|
|
||||||
|
|
||||||
local action_registry = {}
|
|
||||||
|
|
||||||
local function action_text(value)
|
|
||||||
return tostring(value or ""):gsub("[\t\r\n]", " "):gsub(" +", " "):match("^%s*(.-)%s*$")
|
|
||||||
end
|
|
||||||
|
|
||||||
local function action_registry_path()
|
|
||||||
local runtime_dir = os.getenv("XDG_RUNTIME_DIR") or "/tmp"
|
|
||||||
return runtime_dir .. "/hyprland-actions.tsv"
|
|
||||||
end
|
|
||||||
|
|
||||||
local function register_action(keys, dispatcher, opts)
|
|
||||||
local description = opts and opts.description
|
|
||||||
if not description or description == "" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local id = tostring(#action_registry + 1)
|
|
||||||
action_registry[#action_registry + 1] = {
|
|
||||||
id = id,
|
|
||||||
keys = action_text(keys),
|
|
||||||
description = action_text(description),
|
|
||||||
dispatcher = dispatcher,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local function bind(keys, dispatcher, opts)
|
|
||||||
hl.bind(keys, dispatcher, opts)
|
|
||||||
register_action(keys, dispatcher, opts)
|
|
||||||
end
|
|
||||||
|
|
||||||
_G.im_hyprland_write_actions = function()
|
|
||||||
local actions_file = io.open(action_registry_path(), "w")
|
|
||||||
if not actions_file then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, action in ipairs(action_registry) do
|
|
||||||
actions_file:write(action.id, "\t", action.description, "\t", action.keys, "\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
actions_file:close()
|
|
||||||
end
|
|
||||||
|
|
||||||
_G.im_hyprland_run_action = function(id)
|
|
||||||
local action = action_registry[tonumber(id)]
|
|
||||||
if not action then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if type(action.dispatcher) == "function" then
|
|
||||||
action.dispatcher()
|
|
||||||
else
|
|
||||||
dispatch(action.dispatcher)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function shell_quote(value)
|
|
||||||
return "'" .. tostring(value):gsub("'", "'\\''") .. "'"
|
|
||||||
end
|
|
||||||
|
|
||||||
local function overview_trace(label)
|
|
||||||
local enabled = io.open(overview_trace_enabled_path, "r")
|
|
||||||
if not enabled then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
enabled:close()
|
|
||||||
|
|
||||||
local trace = io.open(overview_trace_path, "a")
|
|
||||||
if trace then
|
|
||||||
trace:write(os.date("%Y-%m-%d %H:%M:%S "), label, "\n")
|
|
||||||
trace:close()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function window_selector(window)
|
|
||||||
if not window or not window.address then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
return "address:" .. tostring(window.address)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function hyprexpo_call(method, arg)
|
|
||||||
return function()
|
|
||||||
overview_trace("hyprexpo:" .. method .. (arg and (" " .. tostring(arg)) or ""))
|
|
||||||
if hl.plugin and hl.plugin.hyprexpo and hl.plugin.hyprexpo[method] then
|
|
||||||
hl.plugin.hyprexpo[method](arg)
|
|
||||||
else
|
|
||||||
hl.notification.create({
|
|
||||||
text = "hyprexpo is not loaded",
|
|
||||||
duration = 1800,
|
|
||||||
icon = notification_icons.warning,
|
|
||||||
color = "rgba(edb443ff)",
|
|
||||||
font_size = 13,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function hyprexpo(action)
|
|
||||||
return hyprexpo_call("expo", action or "toggle")
|
|
||||||
end
|
|
||||||
|
|
||||||
local function hyprwinview(action)
|
|
||||||
return function()
|
|
||||||
local label = "hyprwinview"
|
|
||||||
if type(action) == "table" and action.action then
|
|
||||||
label = label .. " " .. tostring(action.action)
|
|
||||||
elseif type(action) ~= "table" and action ~= nil then
|
|
||||||
label = label .. " " .. tostring(action)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function invoke()
|
|
||||||
overview_trace(label)
|
|
||||||
if hl.plugin and hl.plugin.hyprwinview and hl.plugin.hyprwinview.overview then
|
|
||||||
hl.plugin.hyprwinview.overview(action)
|
|
||||||
else
|
|
||||||
hl.notification.create({
|
|
||||||
text = "hyprwinview is not loaded",
|
|
||||||
duration = 1800,
|
|
||||||
icon = notification_icons.warning,
|
|
||||||
color = "rgba(edb443ff)",
|
|
||||||
font_size = 13,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
invoke()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function workspacehistory(action, arg)
|
|
||||||
return function()
|
|
||||||
if hl.plugin and hl.plugin.workspacehistory and hl.plugin.workspacehistory[action] then
|
|
||||||
hl.plugin.workspacehistory[action](arg)
|
|
||||||
else
|
|
||||||
hl.notification.create({
|
|
||||||
text = "workspacehistory is not loaded",
|
|
||||||
duration = 1800,
|
|
||||||
icon = notification_icons.warning,
|
|
||||||
color = "rgba(edb443ff)",
|
|
||||||
font_size = 13,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function apply_nstack_config()
|
|
||||||
if verify_config or not enable_nstack or not configure_nstack_plugin_from_lua then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
hl.config({
|
|
||||||
plugin = {
|
|
||||||
nstack = {
|
|
||||||
layout = {
|
|
||||||
orientation = "left",
|
|
||||||
new_on_top = false,
|
|
||||||
new_near_focused = true,
|
|
||||||
new_is_master = false,
|
|
||||||
no_gaps_when_only = true,
|
|
||||||
special_scale_factor = 0.8,
|
|
||||||
inherit_fullscreen = true,
|
|
||||||
stacks = 1,
|
|
||||||
center_single_master = false,
|
|
||||||
mfact = 0.0,
|
|
||||||
single_mfact = 1.0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
local function apply_hyprexpo_config()
|
|
||||||
if verify_config or not enable_hyprexpo then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
hl.config({
|
|
||||||
plugin = {
|
|
||||||
hyprexpo = {
|
|
||||||
columns = 3,
|
|
||||||
gap_size = 5,
|
|
||||||
gap_size_outer = 0,
|
|
||||||
bg_col = 0xff111111,
|
|
||||||
workspace_method = "first 1",
|
|
||||||
skip_empty = false,
|
|
||||||
max_workspace = max_workspace,
|
|
||||||
gesture_distance = 200,
|
|
||||||
keynav_wrap_h = 1,
|
|
||||||
keynav_wrap_v = 1,
|
|
||||||
keynav_reading_order = 0,
|
|
||||||
border_width = 2,
|
|
||||||
border_color_current = "rgb(66ccff)",
|
|
||||||
border_color_focus = "rgb(edb443)",
|
|
||||||
border_color_hover = "rgb(aabbcc)",
|
|
||||||
tile_rounding = 5,
|
|
||||||
tile_rounding_power = 2.0,
|
|
||||||
label_enable = 1,
|
|
||||||
label_font_size = 28,
|
|
||||||
label_text_mode = "id",
|
|
||||||
label_position = "center",
|
|
||||||
label_offset_x = 6,
|
|
||||||
label_offset_y = 6,
|
|
||||||
selection_label_enable = 0,
|
|
||||||
label_show = "always",
|
|
||||||
label_color_default = 0xffffffff,
|
|
||||||
label_color_hover = 0xffeeeeee,
|
|
||||||
label_color_focus = 0xffedb443,
|
|
||||||
label_color_current = 0xff66ccff,
|
|
||||||
label_bg_enable = 1,
|
|
||||||
label_bg_color = 0xcc000000,
|
|
||||||
label_bg_rounding = 10,
|
|
||||||
label_padding = 12,
|
|
||||||
label_font_bold = 1,
|
|
||||||
label_pixel_snap = 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
local function apply_hyprwinview_config()
|
|
||||||
if verify_config or not enable_hyprwinview then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
hl.config({
|
|
||||||
plugin = {
|
|
||||||
hyprwinview = {
|
|
||||||
gap_size = 24,
|
|
||||||
margin = 48,
|
|
||||||
background = "rgba(10101400)",
|
|
||||||
background_blur = 1,
|
|
||||||
border_col = "rgba(ffffff33)",
|
|
||||||
hover_border_col = "rgba(66ccffee)",
|
|
||||||
border_size = 3,
|
|
||||||
window_order = "application",
|
|
||||||
keys_default_action = "return,enter,space,g,f",
|
|
||||||
keys_filter_toggle = "/",
|
|
||||||
show_app_icon = 1,
|
|
||||||
app_icon_size = 48,
|
|
||||||
app_icon_theme_source = "auto",
|
|
||||||
app_icon_position = "bottom right",
|
|
||||||
app_icon_margin_x = 12,
|
|
||||||
app_icon_margin_y = 12,
|
|
||||||
app_icon_margin_relative_x = 0.0,
|
|
||||||
app_icon_margin_relative_y = 0.0,
|
|
||||||
app_icon_offset_x = 0,
|
|
||||||
app_icon_offset_y = 0,
|
|
||||||
app_icon_backplate_col = "rgba(00000066)",
|
|
||||||
app_icon_backplate_padding = 6,
|
|
||||||
show_window_text = 1,
|
|
||||||
window_text_font = "Sans",
|
|
||||||
window_text_size = 14,
|
|
||||||
window_text_color = "rgba(ffffffff)",
|
|
||||||
window_text_backplate_col = "rgba(00000099)",
|
|
||||||
window_text_padding = 6,
|
|
||||||
filter_animation_ms = 140,
|
|
||||||
animation = "workspace_zoom",
|
|
||||||
animation_in_ms = 280,
|
|
||||||
animation_out_ms = 220,
|
|
||||||
animation_speed = 1.0,
|
|
||||||
animation_scale = 0.94,
|
|
||||||
animation_stagger_ms = 16,
|
|
||||||
animation_stagger_max_ms = 120,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
if hl.plugin and hl.plugin.hyprwinview and hl.plugin.hyprwinview.configure then
|
|
||||||
hl.plugin.hyprwinview.configure({
|
|
||||||
keys = {
|
|
||||||
left = { "a", "h", "left" },
|
|
||||||
right = { "d", "l", "right" },
|
|
||||||
up = { "w", "k", "up" },
|
|
||||||
down = { "s", "j", "down" },
|
|
||||||
default_action = { "return", "enter", "space", "g", "f" },
|
|
||||||
bring = { "b", "shift+return", "shift+space" },
|
|
||||||
bring_replace = { "shift + b" },
|
|
||||||
close = { "escape", "q" },
|
|
||||||
filter_toggle = { "/" },
|
|
||||||
},
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function active_workspace()
|
|
||||||
return hl.get_active_workspace()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function active_workspace_id()
|
|
||||||
local workspace = active_workspace()
|
|
||||||
if workspace and type(workspace.id) == "number" and workspace.id >= 1 then
|
|
||||||
return math.min(max_workspace, math.max(1, workspace.id))
|
|
||||||
end
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
|
|
||||||
local function workspace_key(workspace)
|
|
||||||
workspace = workspace or active_workspace()
|
|
||||||
if workspace and workspace.id then
|
|
||||||
return tostring(workspace.id)
|
|
||||||
end
|
|
||||||
return tostring(active_workspace_id())
|
|
||||||
end
|
|
||||||
|
|
||||||
local function current_workspace_layout()
|
|
||||||
return workspace_layouts[workspace_key()] or columns_layout
|
|
||||||
end
|
|
||||||
|
|
||||||
local function write_layout_state()
|
|
||||||
local runtime_dir = os.getenv("XDG_RUNTIME_DIR")
|
|
||||||
if not runtime_dir then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local file = io.open(runtime_dir .. "/hyprland-layout-state", "w")
|
|
||||||
if not file then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local workspace = active_workspace()
|
|
||||||
file:write("workspace=", workspace_key(workspace), "\n")
|
|
||||||
file:write("layout=", current_layout, "\n")
|
|
||||||
for key, layout in pairs(workspace_layouts) do
|
|
||||||
file:write("workspace.", tostring(key), "=", tostring(layout), "\n")
|
|
||||||
end
|
|
||||||
file:close()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function is_normal_workspace(workspace)
|
|
||||||
return workspace and not workspace.special and workspace.id and workspace.id >= 1
|
|
||||||
end
|
|
||||||
|
|
||||||
local function same_workspace(left, right)
|
|
||||||
if not left or not right then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
if left.name and right.name and tostring(left.name) == tostring(right.name) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
return left.id and right.id and left.id == right.id
|
|
||||||
end
|
|
||||||
|
|
||||||
local function is_minimized_workspace(workspace)
|
|
||||||
if not workspace then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local name = tostring(workspace.name or "")
|
|
||||||
return name == minimized_workspace or name == "minimized" or (workspace.special and name:find("minimized", 1, true) ~= nil)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function is_minimized_window(window)
|
|
||||||
return window and is_minimized_workspace(window.workspace)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function is_normal_window(window)
|
|
||||||
return window
|
|
||||||
and window.mapped ~= false
|
|
||||||
and not window.hidden
|
|
||||||
and window.workspace
|
|
||||||
and is_normal_workspace(window.workspace)
|
|
||||||
and not is_scratchpad_window(window)
|
|
||||||
and not is_minimized_window(window)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function tiled_windows(workspace)
|
|
||||||
local windows = {}
|
|
||||||
if not workspace then
|
|
||||||
return windows
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, window in ipairs(hl.get_workspace_windows(workspace)) do
|
|
||||||
if not window.floating and not window.hidden then
|
|
||||||
windows[#windows + 1] = window
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return windows
|
|
||||||
end
|
|
||||||
|
|
||||||
local function tiled_window_count(workspace)
|
|
||||||
return #tiled_windows(workspace)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function sort_windows_by_focus_history(windows)
|
|
||||||
table.sort(windows, function(left, right)
|
|
||||||
return (left.focus_history_id or 0) < (right.focus_history_id or 0)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function window_address_set(windows)
|
|
||||||
local addresses = {}
|
|
||||||
for _, window in ipairs(windows) do
|
|
||||||
if window and window.address then
|
|
||||||
addresses[window.address] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return addresses
|
|
||||||
end
|
|
||||||
|
|
||||||
local function window_address_list(windows)
|
|
||||||
local addresses = {}
|
|
||||||
for _, window in ipairs(windows) do
|
|
||||||
if window and window.address then
|
|
||||||
addresses[#addresses + 1] = window.address
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return addresses
|
|
||||||
end
|
|
||||||
|
|
||||||
local function window_address_in_set(window, addresses)
|
|
||||||
return window and window.address and addresses[window.address] or false
|
|
||||||
end
|
|
||||||
|
|
||||||
local function windows_by_address()
|
|
||||||
local windows = {}
|
|
||||||
for _, window in ipairs(hl.get_windows()) do
|
|
||||||
if window and window.address then
|
|
||||||
windows[window.address] = window
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return windows
|
|
||||||
end
|
|
||||||
|
|
||||||
local function numeric_component(value, key, index)
|
|
||||||
if type(value) ~= "table" then
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
|
|
||||||
return tonumber(value[key] or value[index]) or 0
|
|
||||||
end
|
|
||||||
|
|
||||||
local function window_center(window)
|
|
||||||
local at = window and window.at or {}
|
|
||||||
local size = window and window.size or {}
|
|
||||||
return numeric_component(at, "x", 1) + numeric_component(size, "x", 1) / 2,
|
|
||||||
numeric_component(at, "y", 2) + numeric_component(size, "y", 2) / 2
|
|
||||||
end
|
|
||||||
|
|
||||||
local function tiled_window_geometry(window)
|
|
||||||
if not window or window.floating then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local selector = window_selector(window)
|
|
||||||
if not selector then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local at = window.at or {}
|
|
||||||
local size = window.size or {}
|
|
||||||
local width = math.floor(numeric_component(size, "x", 1))
|
|
||||||
local height = math.floor(numeric_component(size, "y", 2))
|
|
||||||
if width <= 0 or height <= 0 then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
selector = selector,
|
|
||||||
x = math.floor(numeric_component(at, "x", 1)),
|
|
||||||
y = math.floor(numeric_component(at, "y", 2)),
|
|
||||||
width = width,
|
|
||||||
height = height,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local function window_distance_squared(window, x, y)
|
|
||||||
local wx, wy = window_center(window)
|
|
||||||
local dx = wx - x
|
|
||||||
local dy = wy - y
|
|
||||||
return dx * dx + dy * dy
|
|
||||||
end
|
|
||||||
|
|
||||||
local function sort_windows_by_visual_position(windows)
|
|
||||||
table.sort(windows, function(left, right)
|
|
||||||
local left_x, left_y = window_center(left)
|
|
||||||
local right_x, right_y = window_center(right)
|
|
||||||
|
|
||||||
if math.abs(left_x - right_x) > 10 then
|
|
||||||
return left_x < right_x
|
|
||||||
end
|
|
||||||
if math.abs(left_y - right_y) > 10 then
|
|
||||||
return left_y < right_y
|
|
||||||
end
|
|
||||||
return tostring(left.address or "") < tostring(right.address or "")
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function grouping_direction(window, anchor)
|
|
||||||
local wx, wy = window_center(window)
|
|
||||||
local ax, ay = window_center(anchor)
|
|
||||||
local dx = wx - ax
|
|
||||||
local dy = wy - ay
|
|
||||||
|
|
||||||
if math.abs(dx) >= math.abs(dy) then
|
|
||||||
return dx >= 0 and "left" or "right"
|
|
||||||
end
|
|
||||||
return dy >= 0 and "up" or "down"
|
|
||||||
end
|
|
||||||
|
|
||||||
local function grouping_directions(window, anchor)
|
|
||||||
local primary = grouping_direction(window, anchor)
|
|
||||||
local directions = { primary }
|
|
||||||
for _, direction in ipairs({ "left", "right", "up", "down" }) do
|
|
||||||
if direction ~= primary then
|
|
||||||
directions[#directions + 1] = direction
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return directions
|
|
||||||
end
|
|
||||||
|
|
||||||
local function workspace_window_count(workspace_id)
|
|
||||||
local workspace = hl.get_workspace(tostring(workspace_id))
|
|
||||||
if not workspace then
|
|
||||||
return 0
|
|
||||||
end
|
|
||||||
return workspace.windows or tiled_window_count(workspace)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function find_empty_workspace(target_monitor, exclude_id)
|
|
||||||
local unused_candidate = nil
|
|
||||||
local elsewhere_empty_candidate = nil
|
|
||||||
local target_monitor_name = target_monitor and target_monitor.name or nil
|
|
||||||
|
|
||||||
for i = 1, max_workspace do
|
|
||||||
if i ~= exclude_id then
|
|
||||||
local workspace = hl.get_workspace(tostring(i))
|
|
||||||
|
|
||||||
if not workspace then
|
|
||||||
unused_candidate = unused_candidate or i
|
|
||||||
elseif is_normal_workspace(workspace) and workspace_window_count(i) == 0 then
|
|
||||||
local monitor = workspace.monitor
|
|
||||||
if target_monitor_name and monitor and monitor.name == target_monitor_name then
|
|
||||||
return i
|
|
||||||
end
|
|
||||||
elsewhere_empty_candidate = elsewhere_empty_candidate or i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return unused_candidate or elsewhere_empty_candidate
|
|
||||||
end
|
|
||||||
|
|
||||||
ctx.command_line_contains = command_line_contains
|
|
||||||
ctx.bind = bind
|
|
||||||
ctx.exec = exec
|
|
||||||
ctx.dispatch = dispatch
|
|
||||||
ctx.shell_quote = shell_quote
|
|
||||||
ctx.overview_trace = overview_trace
|
|
||||||
ctx.window_selector = window_selector
|
|
||||||
ctx.hyprexpo = hyprexpo
|
|
||||||
ctx.hyprwinview = hyprwinview
|
|
||||||
ctx.workspacehistory = workspacehistory
|
|
||||||
ctx.apply_nstack_config = apply_nstack_config
|
|
||||||
ctx.apply_hyprexpo_config = apply_hyprexpo_config
|
|
||||||
ctx.apply_hyprwinview_config = apply_hyprwinview_config
|
|
||||||
ctx.active_workspace = active_workspace
|
|
||||||
ctx.active_workspace_id = active_workspace_id
|
|
||||||
ctx.workspace_key = workspace_key
|
|
||||||
ctx.current_workspace_layout = current_workspace_layout
|
|
||||||
ctx.write_layout_state = write_layout_state
|
|
||||||
ctx.is_normal_workspace = is_normal_workspace
|
|
||||||
ctx.same_workspace = same_workspace
|
|
||||||
ctx.is_minimized_workspace = is_minimized_workspace
|
|
||||||
ctx.is_minimized_window = is_minimized_window
|
|
||||||
ctx.is_normal_window = is_normal_window
|
|
||||||
ctx.tiled_windows = tiled_windows
|
|
||||||
ctx.tiled_window_count = tiled_window_count
|
|
||||||
ctx.sort_windows_by_focus_history = sort_windows_by_focus_history
|
|
||||||
ctx.window_address_set = window_address_set
|
|
||||||
ctx.window_address_list = window_address_list
|
|
||||||
ctx.window_address_in_set = window_address_in_set
|
|
||||||
ctx.windows_by_address = windows_by_address
|
|
||||||
ctx.numeric_component = numeric_component
|
|
||||||
ctx.window_center = window_center
|
|
||||||
ctx.tiled_window_geometry = tiled_window_geometry
|
|
||||||
ctx.window_distance_squared = window_distance_squared
|
|
||||||
ctx.sort_windows_by_visual_position = sort_windows_by_visual_position
|
|
||||||
ctx.grouping_direction = grouping_direction
|
|
||||||
ctx.grouping_directions = grouping_directions
|
|
||||||
ctx.workspace_window_count = workspace_window_count
|
|
||||||
ctx.find_empty_workspace = find_empty_workspace
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
local M = {}
|
|
||||||
|
|
||||||
function M.setup(ctx)
|
|
||||||
local _ENV = ctx
|
|
||||||
local fullscreen_states = {}
|
|
||||||
|
|
||||||
local function unset_fullscreen_state(window, state)
|
|
||||||
dispatch(hl.dsp.window.fullscreen_state({
|
|
||||||
internal = state.internal,
|
|
||||||
client = state.client,
|
|
||||||
action = "unset",
|
|
||||||
window = window_selector(window),
|
|
||||||
}))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function reconcile_fullscreen_state(window)
|
|
||||||
if not window or not window.address then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local address = tostring(window.address)
|
|
||||||
local previous = fullscreen_states[address]
|
|
||||||
local current = {
|
|
||||||
internal = tonumber(window.fullscreen) or 0,
|
|
||||||
client = tonumber(window.fullscreen_client) or 0,
|
|
||||||
}
|
|
||||||
fullscreen_states[address] = current
|
|
||||||
|
|
||||||
if window.floating or current_layout == monocle_layout then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if current.internal == 1 or (previous and previous.internal >= 2 and current.internal > 0 and current.client == 0) then
|
|
||||||
unset_fullscreen_state(window, current)
|
|
||||||
fullscreen_states[address] = { internal = 0, client = 0 }
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
hl.on("hyprland.start", function()
|
|
||||||
apply_nstack_config()
|
|
||||||
apply_hyprexpo_config()
|
|
||||||
apply_hyprwinview_config()
|
|
||||||
apply_hyprwobbly_config()
|
|
||||||
apply_hyprglass_config()
|
|
||||||
apply_visual_performance_mode()
|
|
||||||
apply_rules()
|
|
||||||
if not dev_session then
|
|
||||||
hl.exec_cmd("sh -lc '/run/current-system/sw/bin/uwsm finalize HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP XDG_SESSION_DESKTOP XDG_SESSION_TYPE XAUTHORITY IMALISON_SESSION_TYPE=wayland IMALISON_WINDOW_MANAGER=hyprland || dbus-update-activation-environment --systemd XDG_RUNTIME_DIR WAYLAND_DISPLAY DISPLAY XAUTHORITY HYPRLAND_INSTANCE_SIGNATURE XDG_CURRENT_DESKTOP XDG_SESSION_DESKTOP XDG_SESSION_TYPE IMALISON_SESSION_TYPE IMALISON_WINDOW_MANAGER; systemctl --user start hyprland-session.target'")
|
|
||||||
hl.exec_cmd("hypridle")
|
|
||||||
hl.exec_cmd("wl-paste --type text --watch cliphist store")
|
|
||||||
hl.exec_cmd("wl-paste --type image --watch cliphist store")
|
|
||||||
end
|
|
||||||
write_layout_state()
|
|
||||||
schedule_nstack_count_update()
|
|
||||||
refresh_monitor_reserved_cache(0.25)
|
|
||||||
refresh_monitor_reserved_cache(1.25)
|
|
||||||
end)
|
|
||||||
|
|
||||||
hl.on("config.reloaded", apply_nstack_config)
|
|
||||||
hl.on("config.reloaded", apply_hyprexpo_config)
|
|
||||||
hl.on("config.reloaded", apply_hyprwinview_config)
|
|
||||||
hl.on("config.reloaded", apply_hyprwobbly_config)
|
|
||||||
hl.on("config.reloaded", apply_hyprglass_config)
|
|
||||||
hl.on("config.reloaded", apply_visual_performance_mode)
|
|
||||||
hl.on("config.reloaded", apply_rules)
|
|
||||||
hl.on("config.reloaded", refresh_shell_workarea_and_scratchpads)
|
|
||||||
hl.on("layer.opened", refresh_shell_workarea_and_scratchpads)
|
|
||||||
hl.on("layer.closed", refresh_shell_workarea_and_scratchpads)
|
|
||||||
hl.on("monitor.added", refresh_shell_workarea_and_scratchpads)
|
|
||||||
hl.on("monitor.removed", refresh_shell_workarea_and_scratchpads)
|
|
||||||
hl.on("monitor.layout_changed", refresh_shell_workarea_and_scratchpads)
|
|
||||||
|
|
||||||
hl.on("window.open", schedule_nstack_count_update)
|
|
||||||
hl.on("window.destroy", schedule_nstack_count_update)
|
|
||||||
hl.on("window.kill", schedule_nstack_count_update)
|
|
||||||
hl.on("window.move_to_workspace", schedule_nstack_count_update)
|
|
||||||
hl.on("workspace.active", sync_layout_for_active_workspace)
|
|
||||||
hl.on("monitor.focused", sync_layout_for_active_workspace)
|
|
||||||
|
|
||||||
hl.on("window.open", update_monocle_notice)
|
|
||||||
hl.on("window.destroy", update_monocle_notice)
|
|
||||||
hl.on("window.kill", update_monocle_notice)
|
|
||||||
hl.on("window.move_to_workspace", update_monocle_notice)
|
|
||||||
hl.on("window.fullscreen", reconcile_fullscreen_state)
|
|
||||||
hl.on("window.update_rules", reconcile_fullscreen_state)
|
|
||||||
|
|
||||||
hl.on("window.open", adopt_matching_scratchpad_window)
|
|
||||||
hl.on("window.class", adopt_matching_scratchpad_window)
|
|
||||||
hl.on("window.title", adopt_matching_scratchpad_window)
|
|
||||||
|
|
||||||
hl.on("window.open", raise_file_chooser_window_later)
|
|
||||||
hl.on("window.class", raise_file_chooser_window_later)
|
|
||||||
hl.on("window.title", raise_file_chooser_window_later)
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
|
||||||
@@ -1,596 +0,0 @@
|
|||||||
local M = {}
|
|
||||||
|
|
||||||
function M.setup(ctx)
|
|
||||||
local _ENV = ctx
|
|
||||||
local function is_nstack_layout(layout)
|
|
||||||
return layout == columns_layout or layout == grid_layout
|
|
||||||
end
|
|
||||||
|
|
||||||
local function hyprland_layout(layout)
|
|
||||||
if layout == grid_layout then
|
|
||||||
return columns_layout
|
|
||||||
end
|
|
||||||
return layout
|
|
||||||
end
|
|
||||||
|
|
||||||
local function update_nstack_count()
|
|
||||||
if not enable_nstack or not is_nstack_layout(current_layout) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local workspace = hl.get_active_workspace()
|
|
||||||
local count = tiled_window_count(workspace)
|
|
||||||
if count == 0 then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local stack_count = count
|
|
||||||
if current_layout == grid_layout then
|
|
||||||
stack_count = math.ceil(math.sqrt(count))
|
|
||||||
end
|
|
||||||
|
|
||||||
stack_count = math.max(stack_count, 2)
|
|
||||||
dispatch(hl.dsp.layout("setstackcount " .. tostring(stack_count)))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function schedule_nstack_count_update()
|
|
||||||
if stack_update_timer then
|
|
||||||
stack_update_timer:set_enabled(false)
|
|
||||||
end
|
|
||||||
|
|
||||||
stack_update_timer = hl.timer(update_nstack_count, { timeout = 25, type = "oneshot" })
|
|
||||||
end
|
|
||||||
|
|
||||||
local function dismiss_monocle_notice()
|
|
||||||
if monocle_notice and monocle_notice:is_alive() then
|
|
||||||
monocle_notice:dismiss()
|
|
||||||
end
|
|
||||||
monocle_notice = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local function update_monocle_notice()
|
|
||||||
if current_layout ~= monocle_layout then
|
|
||||||
dismiss_monocle_notice()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local workspace = hl.get_active_workspace()
|
|
||||||
local count = tiled_window_count(workspace)
|
|
||||||
if count <= 1 then
|
|
||||||
dismiss_monocle_notice()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local text = "Monocle: " .. tostring(count) .. " windows"
|
|
||||||
if monocle_notice and monocle_notice:is_alive() then
|
|
||||||
monocle_notice:set_text(text)
|
|
||||||
monocle_notice:set_timeout(60000)
|
|
||||||
monocle_notice:pause()
|
|
||||||
else
|
|
||||||
monocle_notice = hl.notification.create({
|
|
||||||
text = text,
|
|
||||||
duration = 60000,
|
|
||||||
icon = notification_icons.info,
|
|
||||||
color = "rgba(edb443ff)",
|
|
||||||
font_size = 13,
|
|
||||||
})
|
|
||||||
monocle_notice:pause()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function layout_name(layout)
|
|
||||||
return layout_names[layout] or tostring(layout)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function notify_layout(layout)
|
|
||||||
hl.notification.create({
|
|
||||||
text = "Layout: " .. layout_name(layout),
|
|
||||||
duration = 1200,
|
|
||||||
icon = notification_icons.info,
|
|
||||||
color = "rgba(edb443ff)",
|
|
||||||
font_size = 13,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
local function set_layout(layout)
|
|
||||||
workspace_layouts[workspace_key()] = layout
|
|
||||||
current_layout = layout
|
|
||||||
hl.config({ general = { layout = hyprland_layout(layout) } })
|
|
||||||
write_layout_state()
|
|
||||||
|
|
||||||
if is_nstack_layout(layout) then
|
|
||||||
dismiss_monocle_notice()
|
|
||||||
schedule_nstack_count_update()
|
|
||||||
else
|
|
||||||
update_monocle_notice()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
_G.im_hyprland_set_layout = function(layout)
|
|
||||||
if not layout_names[layout] then
|
|
||||||
hl.notification.create({
|
|
||||||
text = "Unknown layout: " .. tostring(layout),
|
|
||||||
duration = 1800,
|
|
||||||
icon = notification_icons.warning,
|
|
||||||
color = "rgba(edb443ff)",
|
|
||||||
font_size = 13,
|
|
||||||
})
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
set_layout(layout)
|
|
||||||
notify_layout(layout)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function sync_layout_for_active_workspace()
|
|
||||||
current_layout = current_workspace_layout()
|
|
||||||
hl.config({ general = { layout = hyprland_layout(current_layout) } })
|
|
||||||
write_layout_state()
|
|
||||||
|
|
||||||
if is_nstack_layout(current_layout) then
|
|
||||||
dismiss_monocle_notice()
|
|
||||||
schedule_nstack_count_update()
|
|
||||||
else
|
|
||||||
update_monocle_notice()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function cycle_layout(delta)
|
|
||||||
local current_index = 1
|
|
||||||
for index, layout in ipairs(layout_cycle) do
|
|
||||||
if layout == current_layout then
|
|
||||||
current_index = index
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local next_index = ((current_index - 1 + delta) % #layout_cycle) + 1
|
|
||||||
local next_layout = layout_cycle[next_index]
|
|
||||||
set_layout(next_layout)
|
|
||||||
notify_layout(next_layout)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function toggle_columns_monocle()
|
|
||||||
if current_layout == columns_layout then
|
|
||||||
set_layout(monocle_layout)
|
|
||||||
else
|
|
||||||
set_layout(columns_layout)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function active_group_size()
|
|
||||||
local window = hl.get_active_window()
|
|
||||||
return window and window.group and window.group.size or 0
|
|
||||||
end
|
|
||||||
|
|
||||||
local function monocle_next()
|
|
||||||
local window = hl.get_active_window()
|
|
||||||
if window and window.group and window.group.size and window.group.size > 1 then
|
|
||||||
dispatch(hl.dsp.group.next({ window = window_selector(window) }))
|
|
||||||
elseif current_layout == monocle_layout then
|
|
||||||
dispatch(hl.dsp.layout("cyclenext"))
|
|
||||||
update_monocle_notice()
|
|
||||||
else
|
|
||||||
dispatch(hl.dsp.window.cycle_next({ next = true, tiled = true, floating = false }))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function monocle_prev()
|
|
||||||
local window = hl.get_active_window()
|
|
||||||
if window and window.group and window.group.size and window.group.size > 1 then
|
|
||||||
dispatch(hl.dsp.group.prev({ window = window_selector(window) }))
|
|
||||||
elseif current_layout == monocle_layout then
|
|
||||||
dispatch(hl.dsp.layout("cycleprev"))
|
|
||||||
update_monocle_notice()
|
|
||||||
else
|
|
||||||
dispatch(hl.dsp.window.cycle_next({ next = false, tiled = true, floating = false }))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function focus_direction(direction)
|
|
||||||
overview_trace("focus_direction " .. direction)
|
|
||||||
if active_group_size() > 1 or current_layout == monocle_layout then
|
|
||||||
if direction == "up" or direction == "left" then
|
|
||||||
monocle_prev()
|
|
||||||
else
|
|
||||||
monocle_next()
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
dispatch(hl.dsp.focus({ direction = direction }))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function swap_direction(direction)
|
|
||||||
if enable_nstack and is_nstack_layout(current_layout) and active_group_size() <= 1 then
|
|
||||||
dispatch(hl.dsp.layout("swapdirection " .. direction))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
dispatch(hl.dsp.window.swap({ direction = direction }))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function focus_workspace(workspace_id)
|
|
||||||
dispatch(hl.dsp.focus({ workspace = tostring(workspace_id), on_current_monitor = true }))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function move_window_to_workspace(workspace_id, follow, window)
|
|
||||||
local target_window = window or hl.get_active_window()
|
|
||||||
local target_selector = window_selector(target_window)
|
|
||||||
dispatch(hl.dsp.window.move({ workspace = tostring(workspace_id), follow = false, window = target_selector }))
|
|
||||||
if follow then
|
|
||||||
focus_workspace(workspace_id)
|
|
||||||
if target_selector then
|
|
||||||
dispatch(hl.dsp.focus({ window = target_selector }))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function notify_tabbed_group(text)
|
|
||||||
hl.notification.create({
|
|
||||||
text = text,
|
|
||||||
duration = 1800,
|
|
||||||
icon = notification_icons.info,
|
|
||||||
color = "rgba(edb443ff)",
|
|
||||||
font_size = 13,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
local function active_workspace_tiled_group_candidates(workspace)
|
|
||||||
local candidates = tiled_windows(workspace)
|
|
||||||
sort_windows_by_focus_history(candidates)
|
|
||||||
return candidates
|
|
||||||
end
|
|
||||||
|
|
||||||
local function move_window_into_group(window, anchor)
|
|
||||||
local selector = window_selector(window)
|
|
||||||
if not selector then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, direction in ipairs(grouping_directions(window, anchor)) do
|
|
||||||
dispatch(hl.dsp.focus({ window = selector }))
|
|
||||||
dispatch(hl.dsp.window.move({ into_group = direction, window = selector }))
|
|
||||||
|
|
||||||
local active = hl.get_active_window()
|
|
||||||
if active and active.group and active.group.size and active.group.size > 1 then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local function find_tabbed_group_anchor(state)
|
|
||||||
local active = hl.get_active_window()
|
|
||||||
if active and active.group and active.group.size and active.group.size > 1 then
|
|
||||||
return active
|
|
||||||
end
|
|
||||||
|
|
||||||
if not state then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, window in ipairs(hl.get_windows()) do
|
|
||||||
if window and window.address == state.anchor and window.group and window.group.size and window.group.size > 1 then
|
|
||||||
return window
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local function ordered_windows_for_tabbed_group_restore(state, workspace_id)
|
|
||||||
local ordered = {}
|
|
||||||
local seen = {}
|
|
||||||
local live_windows = windows_by_address()
|
|
||||||
local workspace = workspace_id and hl.get_workspace(tostring(workspace_id)) or active_workspace()
|
|
||||||
|
|
||||||
if state and state.order then
|
|
||||||
for _, address in ipairs(state.order) do
|
|
||||||
local window = live_windows[address]
|
|
||||||
if window and not window.floating and not window.hidden and (not workspace or same_workspace(window.workspace, workspace)) then
|
|
||||||
ordered[#ordered + 1] = window
|
|
||||||
seen[address] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if workspace then
|
|
||||||
for _, window in ipairs(tiled_windows(workspace)) do
|
|
||||||
if window and window.address and not seen[window.address] then
|
|
||||||
ordered[#ordered + 1] = window
|
|
||||||
seen[window.address] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return ordered
|
|
||||||
end
|
|
||||||
|
|
||||||
local function restore_tabbed_group_window_order(state, workspace_id)
|
|
||||||
local ordered = ordered_windows_for_tabbed_group_restore(state, workspace_id)
|
|
||||||
if #ordered <= 1 or not workspace_id then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local restore_workspace = tabbed_group_restore_workspace_prefix .. tostring(workspace_id)
|
|
||||||
for _, window in ipairs(ordered) do
|
|
||||||
move_window_to_workspace(restore_workspace, false, window)
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, window in ipairs(ordered) do
|
|
||||||
move_window_to_workspace(workspace_id, false, window)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function restore_workspace_tabbed_group()
|
|
||||||
local key = workspace_key()
|
|
||||||
local state = tabbed_workspace_groups[key]
|
|
||||||
local anchor = find_tabbed_group_anchor(state)
|
|
||||||
local anchor_selector = window_selector(anchor)
|
|
||||||
local target_workspace_id = anchor and anchor.workspace and anchor.workspace.id
|
|
||||||
|
|
||||||
if not anchor_selector then
|
|
||||||
tabbed_workspace_groups[key] = nil
|
|
||||||
set_layout(columns_layout)
|
|
||||||
notify_tabbed_group("No tabbed group to restore")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
dispatch(hl.dsp.focus({ window = anchor_selector }))
|
|
||||||
dispatch(hl.dsp.group.toggle({ window = anchor_selector }))
|
|
||||||
tabbed_workspace_groups[key] = nil
|
|
||||||
set_layout(columns_layout)
|
|
||||||
restore_tabbed_group_window_order(state, target_workspace_id)
|
|
||||||
dispatch(hl.dsp.focus({ window = anchor_selector }))
|
|
||||||
schedule_nstack_count_update()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function gather_workspace_into_tabbed_group()
|
|
||||||
local workspace = active_workspace()
|
|
||||||
if not is_normal_workspace(workspace) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local key = workspace_key(workspace)
|
|
||||||
if tabbed_workspace_groups[key] or active_group_size() > 1 then
|
|
||||||
restore_workspace_tabbed_group()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local original_windows = tiled_windows(workspace)
|
|
||||||
sort_windows_by_visual_position(original_windows)
|
|
||||||
local original_order = window_address_list(original_windows)
|
|
||||||
local candidates = active_workspace_tiled_group_candidates(workspace)
|
|
||||||
if #candidates <= 1 then
|
|
||||||
set_layout(columns_layout)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local candidate_addresses = window_address_set(candidates)
|
|
||||||
local focused = hl.get_active_window()
|
|
||||||
local anchor = nil
|
|
||||||
if focused and not focused.floating and not focused.group and window_address_in_set(focused, candidate_addresses) then
|
|
||||||
anchor = focused
|
|
||||||
end
|
|
||||||
|
|
||||||
if not anchor then
|
|
||||||
for _, window in ipairs(candidates) do
|
|
||||||
if not window.group then
|
|
||||||
anchor = window
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local anchor_selector = window_selector(anchor)
|
|
||||||
if not anchor_selector then
|
|
||||||
notify_tabbed_group("Current tiled windows are already grouped")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
set_layout(columns_layout)
|
|
||||||
|
|
||||||
dispatch(hl.dsp.focus({ window = anchor_selector }))
|
|
||||||
dispatch(hl.dsp.group.toggle({ window = anchor_selector }))
|
|
||||||
|
|
||||||
local group_windows = {}
|
|
||||||
for _, window in ipairs(candidates) do
|
|
||||||
if window ~= anchor and not window.group then
|
|
||||||
group_windows[#group_windows + 1] = window
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local anchor_x, anchor_y = window_center(anchor)
|
|
||||||
table.sort(group_windows, function(left, right)
|
|
||||||
return window_distance_squared(left, anchor_x, anchor_y) < window_distance_squared(right, anchor_x, anchor_y)
|
|
||||||
end)
|
|
||||||
|
|
||||||
local grouped_count = 1
|
|
||||||
for _, window in ipairs(group_windows) do
|
|
||||||
if move_window_into_group(window, anchor) then
|
|
||||||
grouped_count = grouped_count + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if grouped_count <= 1 then
|
|
||||||
dispatch(hl.dsp.focus({ window = anchor_selector }))
|
|
||||||
dispatch(hl.dsp.group.toggle({ window = anchor_selector }))
|
|
||||||
notify_tabbed_group("Unable to group tiled windows")
|
|
||||||
return
|
|
||||||
elseif grouped_count < #candidates then
|
|
||||||
notify_tabbed_group("Grouped " .. tostring(grouped_count) .. " of " .. tostring(#candidates) .. " tiled windows")
|
|
||||||
end
|
|
||||||
|
|
||||||
tabbed_workspace_groups[key] = {
|
|
||||||
anchor = anchor.address,
|
|
||||||
order = original_order,
|
|
||||||
windows = candidate_addresses,
|
|
||||||
}
|
|
||||||
dispatch(hl.dsp.focus({ window = anchor_selector }))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function force_columns_layout()
|
|
||||||
if active_group_size() > 1 or tabbed_workspace_groups[workspace_key()] then
|
|
||||||
restore_workspace_tabbed_group()
|
|
||||||
else
|
|
||||||
set_layout(columns_layout)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function cycle_layout_or_restore_tabbed_group()
|
|
||||||
if active_group_size() > 1 or tabbed_workspace_groups[workspace_key()] then
|
|
||||||
restore_workspace_tabbed_group()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
cycle_layout(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function copy_windows(workspace)
|
|
||||||
local windows = {}
|
|
||||||
if not workspace then
|
|
||||||
return windows
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, window in ipairs(hl.get_workspace_windows(workspace)) do
|
|
||||||
if window and not window.hidden then
|
|
||||||
windows[#windows + 1] = window
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return windows
|
|
||||||
end
|
|
||||||
|
|
||||||
local function swap_current_workspace_with(target_id)
|
|
||||||
local current = active_workspace()
|
|
||||||
if not current or not current.id or current.id == target_id then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local target = hl.get_workspace(tostring(target_id))
|
|
||||||
local current_windows = copy_windows(current)
|
|
||||||
local target_windows = copy_windows(target)
|
|
||||||
|
|
||||||
for _, window in ipairs(current_windows) do
|
|
||||||
move_window_to_workspace(target_id, false, window)
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, window in ipairs(target_windows) do
|
|
||||||
move_window_to_workspace(current.id, false, window)
|
|
||||||
end
|
|
||||||
|
|
||||||
focus_workspace(current.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function enter_workspace_swap_mode()
|
|
||||||
hl.notification.create({
|
|
||||||
text = "Swap with workspace 1-9",
|
|
||||||
duration = 2200,
|
|
||||||
icon = notification_icons.info,
|
|
||||||
color = "rgba(edb443ff)",
|
|
||||||
font_size = 13,
|
|
||||||
})
|
|
||||||
dispatch(hl.dsp.submap("swap-workspace"))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function focus_next_empty_workspace()
|
|
||||||
local workspace_id = find_empty_workspace(hl.get_active_monitor(), active_workspace_id())
|
|
||||||
if workspace_id then
|
|
||||||
focus_workspace(workspace_id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function move_to_next_empty_workspace(follow)
|
|
||||||
local window = hl.get_active_window()
|
|
||||||
if not window then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local workspace_id = find_empty_workspace(hl.get_active_monitor(), active_workspace_id())
|
|
||||||
if workspace_id then
|
|
||||||
move_window_to_workspace(workspace_id, follow, window)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function cycle_workspace(delta)
|
|
||||||
local current = active_workspace_id()
|
|
||||||
local next_workspace = ((current - 1 + delta) % max_workspace) + 1
|
|
||||||
focus_workspace(next_workspace)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function move_window_to_monitor(direction, follow)
|
|
||||||
local window = hl.get_active_window()
|
|
||||||
if not window then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local original_monitor = hl.get_active_monitor()
|
|
||||||
dispatch(hl.dsp.window.move({ monitor = direction, follow = follow, window = window_selector(window) }))
|
|
||||||
|
|
||||||
if not follow and original_monitor then
|
|
||||||
dispatch(hl.dsp.focus({ monitor = original_monitor }))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function move_window_to_empty_workspace_on_monitor(direction)
|
|
||||||
local window = hl.get_active_window()
|
|
||||||
local original_monitor = hl.get_active_monitor()
|
|
||||||
local target_monitor = hl.get_monitor(direction)
|
|
||||||
|
|
||||||
if not window or not original_monitor or not target_monitor or target_monitor == original_monitor then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local workspace_id = find_empty_workspace(target_monitor, active_workspace_id())
|
|
||||||
if not workspace_id then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
dispatch(hl.dsp.focus({ monitor = target_monitor }))
|
|
||||||
focus_workspace(workspace_id)
|
|
||||||
dispatch(hl.dsp.focus({ monitor = original_monitor }))
|
|
||||||
move_window_to_workspace(workspace_id, false, window)
|
|
||||||
end
|
|
||||||
|
|
||||||
ctx.is_nstack_layout = is_nstack_layout
|
|
||||||
ctx.hyprland_layout = hyprland_layout
|
|
||||||
ctx.update_nstack_count = update_nstack_count
|
|
||||||
ctx.schedule_nstack_count_update = schedule_nstack_count_update
|
|
||||||
ctx.dismiss_monocle_notice = dismiss_monocle_notice
|
|
||||||
ctx.update_monocle_notice = update_monocle_notice
|
|
||||||
ctx.layout_name = layout_name
|
|
||||||
ctx.notify_layout = notify_layout
|
|
||||||
ctx.set_layout = set_layout
|
|
||||||
ctx.sync_layout_for_active_workspace = sync_layout_for_active_workspace
|
|
||||||
ctx.cycle_layout = cycle_layout
|
|
||||||
ctx.toggle_columns_monocle = toggle_columns_monocle
|
|
||||||
ctx.active_group_size = active_group_size
|
|
||||||
ctx.monocle_next = monocle_next
|
|
||||||
ctx.monocle_prev = monocle_prev
|
|
||||||
ctx.focus_direction = focus_direction
|
|
||||||
ctx.swap_direction = swap_direction
|
|
||||||
ctx.focus_workspace = focus_workspace
|
|
||||||
ctx.move_window_to_workspace = move_window_to_workspace
|
|
||||||
ctx.notify_tabbed_group = notify_tabbed_group
|
|
||||||
ctx.active_workspace_tiled_group_candidates = active_workspace_tiled_group_candidates
|
|
||||||
ctx.move_window_into_group = move_window_into_group
|
|
||||||
ctx.find_tabbed_group_anchor = find_tabbed_group_anchor
|
|
||||||
ctx.ordered_windows_for_tabbed_group_restore = ordered_windows_for_tabbed_group_restore
|
|
||||||
ctx.restore_tabbed_group_window_order = restore_tabbed_group_window_order
|
|
||||||
ctx.restore_workspace_tabbed_group = restore_workspace_tabbed_group
|
|
||||||
ctx.gather_workspace_into_tabbed_group = gather_workspace_into_tabbed_group
|
|
||||||
ctx.force_columns_layout = force_columns_layout
|
|
||||||
ctx.cycle_layout_or_restore_tabbed_group = cycle_layout_or_restore_tabbed_group
|
|
||||||
ctx.copy_windows = copy_windows
|
|
||||||
ctx.swap_current_workspace_with = swap_current_workspace_with
|
|
||||||
ctx.enter_workspace_swap_mode = enter_workspace_swap_mode
|
|
||||||
ctx.focus_next_empty_workspace = focus_next_empty_workspace
|
|
||||||
ctx.move_to_next_empty_workspace = move_to_next_empty_workspace
|
|
||||||
ctx.cycle_workspace = cycle_workspace
|
|
||||||
ctx.move_window_to_monitor = move_window_to_monitor
|
|
||||||
ctx.move_window_to_empty_workspace_on_monitor = move_window_to_empty_workspace_on_monitor
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
|
||||||
@@ -1,490 +0,0 @@
|
|||||||
local M = {}
|
|
||||||
|
|
||||||
function M.setup(ctx)
|
|
||||||
local _ENV = ctx
|
|
||||||
|
|
||||||
scratchpad_size_ratio = 0.95
|
|
||||||
dropdown_height_ratio = 0.5
|
|
||||||
dropdown_animation_frames = 18
|
|
||||||
dropdown_animation_frame_ms = 16
|
|
||||||
scratchpad_pending = {}
|
|
||||||
monitor_reserved_cache_path = (os.getenv("XDG_RUNTIME_DIR") or "/tmp") .. "/hyprland-monitor-reserved.tsv"
|
|
||||||
scratchpad_fallback_reserved_top = 60
|
|
||||||
|
|
||||||
scratchpads = {
|
|
||||||
codex = {
|
|
||||||
command = "codex_desktop_scratchpad",
|
|
||||||
class = "codex-desktop",
|
|
||||||
},
|
|
||||||
htop = {
|
|
||||||
command = "alacritty --class htop-scratch --title htop -e htop",
|
|
||||||
class = "htop-scratch",
|
|
||||||
},
|
|
||||||
volume = {
|
|
||||||
command = "pavucontrol",
|
|
||||||
class = "org.pulseaudio.pavucontrol",
|
|
||||||
},
|
|
||||||
spotify = {
|
|
||||||
command = "spotify",
|
|
||||||
class = "spotify",
|
|
||||||
},
|
|
||||||
element = {
|
|
||||||
command = "element-desktop",
|
|
||||||
classes = { "Element", "electron" },
|
|
||||||
title = "Element",
|
|
||||||
},
|
|
||||||
slack = {
|
|
||||||
command = "slack",
|
|
||||||
class = "Slack",
|
|
||||||
},
|
|
||||||
messages = {
|
|
||||||
command = "google-chrome-stable --profile-directory=Default --app=https://messages.google.com/web/conversations",
|
|
||||||
class = "chrome-messages.google.com",
|
|
||||||
},
|
|
||||||
transmission = {
|
|
||||||
command = "transmission-gtk",
|
|
||||||
class = "transmission-gtk",
|
|
||||||
},
|
|
||||||
dropdown = {
|
|
||||||
command = "ghostty --config-file=/home/imalison/.config/ghostty/dropdown",
|
|
||||||
class = "com.mitchellh.ghostty.dropdown",
|
|
||||||
dropdown = true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
local function lower_contains(value, needle)
|
|
||||||
if not needle or needle == "" then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
value = string.lower(tostring(value or ""))
|
|
||||||
needle = string.lower(tostring(needle))
|
|
||||||
return value:find(needle, 1, true) ~= nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local function lower_contains_any(value, needles)
|
|
||||||
if type(needles) ~= "table" then
|
|
||||||
return lower_contains(value, needles)
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, needle in ipairs(needles) do
|
|
||||||
if lower_contains(value, needle) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local function scratchpad_window_matches(window, def)
|
|
||||||
return window
|
|
||||||
and not (type(is_file_chooser_window) == "function" and is_file_chooser_window(window))
|
|
||||||
and lower_contains_any(window.class, def.classes or def.class)
|
|
||||||
and lower_contains(window.title, def.title)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function is_scratchpad_window(window)
|
|
||||||
for _, def in pairs(scratchpads) do
|
|
||||||
if scratchpad_window_matches(window, def) then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local function matching_scratchpad_name(window)
|
|
||||||
for name, def in pairs(scratchpads) do
|
|
||||||
if scratchpad_window_matches(window, def) then
|
|
||||||
return name
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local function scratchpad_workspace(name)
|
|
||||||
return "special:scratch-" .. name
|
|
||||||
end
|
|
||||||
|
|
||||||
local function as_number(value, default)
|
|
||||||
local number = tonumber(value)
|
|
||||||
if number == nil then
|
|
||||||
return default
|
|
||||||
end
|
|
||||||
return number
|
|
||||||
end
|
|
||||||
|
|
||||||
local function logical_monitor_dimension(value, scale)
|
|
||||||
value = as_number(value, 0)
|
|
||||||
scale = as_number(scale, 1)
|
|
||||||
if scale <= 0 then
|
|
||||||
scale = 1
|
|
||||||
end
|
|
||||||
return math.floor((value / scale) + 0.5)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function split_tsv(line)
|
|
||||||
local fields = {}
|
|
||||||
for field in (line .. "\t"):gmatch("([^\t]*)\t") do
|
|
||||||
fields[#fields + 1] = field
|
|
||||||
end
|
|
||||||
return fields
|
|
||||||
end
|
|
||||||
|
|
||||||
local function monitor_from_reserved_fields(monitor, fields)
|
|
||||||
if not monitor or not monitor.name or fields[1] ~= monitor.name or #fields < 10 then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
name = monitor.name,
|
|
||||||
x = tonumber(fields[2]),
|
|
||||||
y = tonumber(fields[3]),
|
|
||||||
width = tonumber(fields[4]),
|
|
||||||
height = tonumber(fields[5]),
|
|
||||||
scale = tonumber(fields[6]),
|
|
||||||
reserved = {
|
|
||||||
tonumber(fields[7]),
|
|
||||||
tonumber(fields[8]),
|
|
||||||
tonumber(fields[9]),
|
|
||||||
tonumber(fields[10]),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local function monitor_from_reserved_lines(monitor, lines)
|
|
||||||
if not monitor or not monitor.name then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
for line in lines do
|
|
||||||
local cached = monitor_from_reserved_fields(monitor, split_tsv(line))
|
|
||||||
if cached then
|
|
||||||
return cached
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local function monitor_from_reserved_cache(monitor)
|
|
||||||
if verify_config or not monitor or not monitor.name then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local file = io.open(monitor_reserved_cache_path, "r")
|
|
||||||
if not file then
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local cached = monitor_from_reserved_lines(monitor, file:lines())
|
|
||||||
file:close()
|
|
||||||
return cached
|
|
||||||
end
|
|
||||||
|
|
||||||
local function refresh_monitor_reserved_cache(delay)
|
|
||||||
if verify_config then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local command = string.format(
|
|
||||||
[=[sleep %.2f; cache="${XDG_RUNTIME_DIR:-/tmp}/hyprland-monitor-reserved.tsv"; tmp="$cache.tmp"; /run/current-system/sw/bin/hyprctl -j monitors 2>/dev/null | /run/current-system/sw/bin/jq -r '.[] | [.name, .x, .y, .width, .height, .scale, .reserved[0], .reserved[1], .reserved[2], .reserved[3]] | @tsv' > "$tmp" && mv "$tmp" "$cache"]=],
|
|
||||||
as_number(delay, 0)
|
|
||||||
)
|
|
||||||
hl.exec_cmd("sh -lc " .. shell_quote(command))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function monitor_workarea(monitor)
|
|
||||||
monitor = monitor_from_reserved_cache(monitor) or monitor
|
|
||||||
local width = logical_monitor_dimension(monitor.width, monitor.scale)
|
|
||||||
local height = logical_monitor_dimension(monitor.height, monitor.scale)
|
|
||||||
local reserved = monitor.reserved or { 0, scratchpad_fallback_reserved_top, 0, 0 }
|
|
||||||
local left = math.floor(as_number(reserved[1], 0))
|
|
||||||
local top = math.floor(as_number(reserved[2], 0))
|
|
||||||
local right = math.floor(as_number(reserved[3], 0))
|
|
||||||
local bottom = math.floor(as_number(reserved[4], 0))
|
|
||||||
local work_width = width - left - right
|
|
||||||
local work_height = height - top - bottom
|
|
||||||
|
|
||||||
if work_width <= 0 then
|
|
||||||
left = 0
|
|
||||||
right = 0
|
|
||||||
work_width = width
|
|
||||||
end
|
|
||||||
if work_height <= 0 then
|
|
||||||
top = 0
|
|
||||||
bottom = 0
|
|
||||||
work_height = height
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
x = math.floor(as_number(monitor.x, 0)) + left,
|
|
||||||
y = math.floor(as_number(monitor.y, 0)) + top,
|
|
||||||
width = work_width,
|
|
||||||
height = work_height,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local function matching_scratchpad_windows(name)
|
|
||||||
local def = scratchpads[name]
|
|
||||||
local windows = {}
|
|
||||||
if not def then
|
|
||||||
return windows
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, window in ipairs(hl.get_windows()) do
|
|
||||||
if scratchpad_window_matches(window, def) then
|
|
||||||
windows[#windows + 1] = window
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return windows
|
|
||||||
end
|
|
||||||
|
|
||||||
local function scratchpad_geometry(name, target_monitor, position)
|
|
||||||
local def = scratchpads[name]
|
|
||||||
local monitor = target_monitor or hl.get_active_monitor()
|
|
||||||
if not def or not monitor then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local workarea = monitor_workarea(monitor)
|
|
||||||
local width
|
|
||||||
local height
|
|
||||||
local x
|
|
||||||
local y
|
|
||||||
if def.dropdown then
|
|
||||||
width = workarea.width
|
|
||||||
height = math.floor(workarea.height * dropdown_height_ratio)
|
|
||||||
x = workarea.x
|
|
||||||
y = workarea.y
|
|
||||||
if position == "above" then
|
|
||||||
y = workarea.y - height
|
|
||||||
elseif type(position) == "number" then
|
|
||||||
y = position
|
|
||||||
end
|
|
||||||
else
|
|
||||||
width = math.floor(workarea.width * scratchpad_size_ratio)
|
|
||||||
height = math.floor(workarea.height * scratchpad_size_ratio)
|
|
||||||
x = workarea.x + math.floor((workarea.width - width) / 2)
|
|
||||||
y = workarea.y + math.floor((workarea.height - height) / 2)
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
width = width,
|
|
||||||
height = height,
|
|
||||||
x = x,
|
|
||||||
y = y,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local function apply_scratchpad_geometry(name, window, target_monitor, position)
|
|
||||||
local def = scratchpads[name]
|
|
||||||
if not def or not window then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local geometry = scratchpad_geometry(name, target_monitor, position)
|
|
||||||
if not geometry then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local selector = window_selector(window)
|
|
||||||
|
|
||||||
dispatch(hl.dsp.window.float({ action = "enable", window = selector }))
|
|
||||||
dispatch(hl.dsp.window.tag({ tag = "+scratchpad", window = selector }))
|
|
||||||
dispatch(hl.dsp.window.tag({ tag = "+scratchpad-" .. name, window = selector }))
|
|
||||||
dispatch(hl.dsp.window.resize({ x = geometry.width, y = geometry.height, relative = false, window = selector }))
|
|
||||||
dispatch(hl.dsp.window.move({ x = geometry.x, y = geometry.y, relative = false, window = selector }))
|
|
||||||
if def.dropdown then
|
|
||||||
dispatch(hl.dsp.window.set_prop({ prop = "border_size", value = "0", window = selector }))
|
|
||||||
dispatch(hl.dsp.window.set_prop({ prop = "no_shadow", value = "1", window = selector }))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function schedule_scratchpad_geometry(name, window, target_monitor, position, timeout)
|
|
||||||
hl.timer(function()
|
|
||||||
apply_scratchpad_geometry(name, window, target_monitor, position)
|
|
||||||
end, { timeout = timeout or 50, type = "oneshot" })
|
|
||||||
end
|
|
||||||
|
|
||||||
local function dropdown_spring_progress(progress)
|
|
||||||
if progress >= 1 then
|
|
||||||
return 1
|
|
||||||
end
|
|
||||||
return 1 - (math.exp(-5.0 * progress) * math.cos(7.0 * progress))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function animate_dropdown_scratchpad_down(name, window, target_monitor)
|
|
||||||
local from = scratchpad_geometry(name, target_monitor, "above")
|
|
||||||
local to = scratchpad_geometry(name, target_monitor)
|
|
||||||
if not from or not to then
|
|
||||||
schedule_scratchpad_geometry(name, window, target_monitor, nil, 35)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
for frame = 1, dropdown_animation_frames do
|
|
||||||
local progress = frame / dropdown_animation_frames
|
|
||||||
local eased = dropdown_spring_progress(progress)
|
|
||||||
local y = math.floor(from.y + ((to.y - from.y) * eased) + 0.5)
|
|
||||||
schedule_scratchpad_geometry(name, window, target_monitor, y, frame * dropdown_animation_frame_ms)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function hide_scratchpad_window(name, window)
|
|
||||||
remove_minimized_window(window)
|
|
||||||
move_window_to_workspace(scratchpad_workspace(name), false, window)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function show_scratchpad_window(name, window, workspace, target_monitor)
|
|
||||||
workspace = workspace or active_workspace()
|
|
||||||
if not workspace then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
remove_minimized_window(window)
|
|
||||||
if scratchpads[name] and scratchpads[name].dropdown then
|
|
||||||
apply_scratchpad_geometry(name, window, target_monitor or hl.get_active_monitor(), "above")
|
|
||||||
end
|
|
||||||
move_window_to_workspace(workspace.id, false, window)
|
|
||||||
dispatch(hl.dsp.focus({ window = window_selector(window) }))
|
|
||||||
if scratchpads[name] and scratchpads[name].dropdown then
|
|
||||||
animate_dropdown_scratchpad_down(name, window, target_monitor or hl.get_active_monitor())
|
|
||||||
else
|
|
||||||
schedule_scratchpad_geometry(name, window, target_monitor or hl.get_active_monitor())
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function scratchpad_is_visible(window)
|
|
||||||
local workspace = active_workspace()
|
|
||||||
return workspace and window and same_workspace(window.workspace, workspace)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Active scratchpads are scratchpad windows visible on the active workspace.
|
|
||||||
-- Invoking a different scratchpad replaces that active set.
|
|
||||||
local function active_scratchpad_windows(except_name)
|
|
||||||
local windows = {}
|
|
||||||
for _, window in ipairs(hl.get_windows()) do
|
|
||||||
local name = matching_scratchpad_name(window)
|
|
||||||
if name and name ~= except_name and scratchpad_is_visible(window) then
|
|
||||||
windows[#windows + 1] = {
|
|
||||||
name = name,
|
|
||||||
window = window,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return windows
|
|
||||||
end
|
|
||||||
|
|
||||||
local function hide_active_scratchpads(except_name)
|
|
||||||
for _, active in ipairs(active_scratchpad_windows(except_name)) do
|
|
||||||
hide_scratchpad_window(active.name, active.window)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function refresh_active_scratchpad_geometries()
|
|
||||||
local monitor = hl.get_active_monitor()
|
|
||||||
for _, active in ipairs(active_scratchpad_windows()) do
|
|
||||||
schedule_scratchpad_geometry(active.name, active.window, monitor)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function refresh_active_scratchpad_geometries_later(timeout)
|
|
||||||
hl.timer(refresh_active_scratchpad_geometries, { timeout = timeout or 300, type = "oneshot" })
|
|
||||||
end
|
|
||||||
|
|
||||||
local function refresh_shell_workarea_and_scratchpads()
|
|
||||||
refresh_monitor_reserved_cache(0.15)
|
|
||||||
refresh_active_scratchpad_geometries_later(400)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function adopt_matching_scratchpad_window(window)
|
|
||||||
if not window then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
for name, def in pairs(scratchpads) do
|
|
||||||
if scratchpad_window_matches(window, def) then
|
|
||||||
if scratchpad_pending[name] then
|
|
||||||
local pending = scratchpad_pending[name]
|
|
||||||
scratchpad_pending[name] = nil
|
|
||||||
show_scratchpad_window(name, window, pending.workspace or active_workspace(), pending.monitor or hl.get_active_monitor())
|
|
||||||
elseif scratchpad_is_visible(window) then
|
|
||||||
schedule_scratchpad_geometry(name, window, hl.get_active_monitor())
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function toggle_scratchpad(name)
|
|
||||||
local def = scratchpads[name]
|
|
||||||
if not def then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if current_layout == monocle_layout then
|
|
||||||
set_layout(columns_layout)
|
|
||||||
end
|
|
||||||
|
|
||||||
local windows = matching_scratchpad_windows(name)
|
|
||||||
if #windows == 0 then
|
|
||||||
hide_active_scratchpads(name)
|
|
||||||
scratchpad_pending[name] = {
|
|
||||||
monitor = hl.get_active_monitor(),
|
|
||||||
workspace = active_workspace(),
|
|
||||||
}
|
|
||||||
hl.exec_cmd(def.command)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local any_visible = false
|
|
||||||
for _, window in ipairs(windows) do
|
|
||||||
if scratchpad_is_visible(window) then
|
|
||||||
any_visible = true
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if any_visible then
|
|
||||||
for _, window in ipairs(windows) do
|
|
||||||
hide_scratchpad_window(name, window)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
hide_active_scratchpads(name)
|
|
||||||
local workspace = active_workspace()
|
|
||||||
local target_monitor = hl.get_active_monitor()
|
|
||||||
for _, window in ipairs(windows) do
|
|
||||||
show_scratchpad_window(name, window, workspace, target_monitor)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
ctx.lower_contains = lower_contains
|
|
||||||
ctx.lower_contains_any = lower_contains_any
|
|
||||||
ctx.scratchpad_window_matches = scratchpad_window_matches
|
|
||||||
ctx.is_scratchpad_window = is_scratchpad_window
|
|
||||||
ctx.matching_scratchpad_name = matching_scratchpad_name
|
|
||||||
ctx.scratchpad_workspace = scratchpad_workspace
|
|
||||||
ctx.as_number = as_number
|
|
||||||
ctx.logical_monitor_dimension = logical_monitor_dimension
|
|
||||||
ctx.split_tsv = split_tsv
|
|
||||||
ctx.monitor_from_reserved_fields = monitor_from_reserved_fields
|
|
||||||
ctx.monitor_from_reserved_lines = monitor_from_reserved_lines
|
|
||||||
ctx.monitor_from_reserved_cache = monitor_from_reserved_cache
|
|
||||||
ctx.refresh_monitor_reserved_cache = refresh_monitor_reserved_cache
|
|
||||||
ctx.monitor_workarea = monitor_workarea
|
|
||||||
ctx.scratchpad_geometry = scratchpad_geometry
|
|
||||||
ctx.matching_scratchpad_windows = matching_scratchpad_windows
|
|
||||||
ctx.apply_scratchpad_geometry = apply_scratchpad_geometry
|
|
||||||
ctx.schedule_scratchpad_geometry = schedule_scratchpad_geometry
|
|
||||||
ctx.dropdown_spring_progress = dropdown_spring_progress
|
|
||||||
ctx.animate_dropdown_scratchpad_down = animate_dropdown_scratchpad_down
|
|
||||||
ctx.hide_scratchpad_window = hide_scratchpad_window
|
|
||||||
ctx.show_scratchpad_window = show_scratchpad_window
|
|
||||||
ctx.scratchpad_is_visible = scratchpad_is_visible
|
|
||||||
ctx.active_scratchpad_windows = active_scratchpad_windows
|
|
||||||
ctx.hide_active_scratchpads = hide_active_scratchpads
|
|
||||||
ctx.refresh_active_scratchpad_geometries = refresh_active_scratchpad_geometries
|
|
||||||
ctx.refresh_active_scratchpad_geometries_later = refresh_active_scratchpad_geometries_later
|
|
||||||
ctx.refresh_shell_workarea_and_scratchpads = refresh_shell_workarea_and_scratchpads
|
|
||||||
ctx.adopt_matching_scratchpad_window = adopt_matching_scratchpad_window
|
|
||||||
ctx.toggle_scratchpad = toggle_scratchpad
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
|
||||||
@@ -1,422 +0,0 @@
|
|||||||
local M = {}
|
|
||||||
|
|
||||||
function M.setup(ctx)
|
|
||||||
local _ENV = ctx
|
|
||||||
local file_chooser_title_rule = "^(Open File|Open Files|Save File|Save Files|Save As|Select File|Select Files|Choose File|Choose Files|File Upload|Upload File|Upload Files|Select Folder|Choose Folder|Open Folder|Save Folder)$"
|
|
||||||
|
|
||||||
local function lower_string(value)
|
|
||||||
return string.lower(tostring(value or ""))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function title_indicates_file_chooser(title)
|
|
||||||
title = lower_string(title)
|
|
||||||
if title == "" then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, exact in ipairs({
|
|
||||||
"open file",
|
|
||||||
"open files",
|
|
||||||
"save file",
|
|
||||||
"save files",
|
|
||||||
"save as",
|
|
||||||
"select file",
|
|
||||||
"select files",
|
|
||||||
"choose file",
|
|
||||||
"choose files",
|
|
||||||
"file upload",
|
|
||||||
"upload file",
|
|
||||||
"upload files",
|
|
||||||
"select folder",
|
|
||||||
"choose folder",
|
|
||||||
"open folder",
|
|
||||||
"save folder",
|
|
||||||
}) do
|
|
||||||
if title == exact then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return title:find("file chooser", 1, true) ~= nil
|
|
||||||
or title:find("file picker", 1, true) ~= nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local function is_file_chooser_window(window)
|
|
||||||
return window
|
|
||||||
and (title_indicates_file_chooser(window.title) or title_indicates_file_chooser(window.initial_title))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function raise_file_chooser_window(window)
|
|
||||||
if verify_config or not is_file_chooser_window(window) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local selector = window_selector(window)
|
|
||||||
if not selector then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
dispatch(hl.dsp.window.float({ action = "enable", window = selector }))
|
|
||||||
dispatch(hl.dsp.window.center({ window = selector }))
|
|
||||||
dispatch(hl.dsp.focus({ window = selector }))
|
|
||||||
dispatch(hl.dsp.window.bring_to_top({ window = selector }))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function raise_file_chooser_window_later(window, timeout)
|
|
||||||
hl.timer(function()
|
|
||||||
local refreshed = window and window.address and hl.get_window(window_selector(window)) or window
|
|
||||||
raise_file_chooser_window(refreshed)
|
|
||||||
end, { timeout = timeout or 50, type = "oneshot" })
|
|
||||||
end
|
|
||||||
|
|
||||||
if enable_nstack and not verify_config then
|
|
||||||
hl.plugin.load("/run/current-system/sw/lib/libhyprNStack.so")
|
|
||||||
end
|
|
||||||
if enable_hyprexpo and not verify_config then
|
|
||||||
hl.plugin.load("/run/current-system/sw/lib/libhyprexpo.so")
|
|
||||||
end
|
|
||||||
if enable_hyprwinview and not verify_config then
|
|
||||||
hl.plugin.load("/run/current-system/sw/lib/libhyprwinview.so")
|
|
||||||
end
|
|
||||||
if enable_workspace_history and not verify_config then
|
|
||||||
hl.plugin.load("/run/current-system/sw/lib/libhypr-workspace-history.so")
|
|
||||||
end
|
|
||||||
if enable_hyprwobbly and not verify_config then
|
|
||||||
hl.plugin.load("/run/current-system/sw/lib/libhyprwobbly.so")
|
|
||||||
end
|
|
||||||
if enable_hyprglass and not verify_config then
|
|
||||||
hl.plugin.load("/run/current-system/sw/lib/hyprglass.so")
|
|
||||||
end
|
|
||||||
|
|
||||||
hl.env("XCURSOR_SIZE", "24")
|
|
||||||
hl.env("HYPRCURSOR_SIZE", "24")
|
|
||||||
hl.env("QT_QPA_PLATFORMTHEME", "qt5ct")
|
|
||||||
hl.env("HYPR_MAX_WORKSPACE", "9")
|
|
||||||
|
|
||||||
hl.config({
|
|
||||||
input = {
|
|
||||||
kb_layout = "us",
|
|
||||||
kb_variant = "",
|
|
||||||
kb_model = "",
|
|
||||||
kb_options = "",
|
|
||||||
kb_rules = "",
|
|
||||||
follow_mouse = 1,
|
|
||||||
sensitivity = 0,
|
|
||||||
touchpad = {
|
|
||||||
natural_scroll = false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
cursor = {
|
|
||||||
persistent_warps = true,
|
|
||||||
},
|
|
||||||
general = {
|
|
||||||
gaps_in = 5,
|
|
||||||
gaps_out = 10,
|
|
||||||
border_size = 2,
|
|
||||||
col = {
|
|
||||||
active_border = { colors = { "rgba(3b82f6ee)", "rgba(33ccffee)" }, angle = 45 },
|
|
||||||
inactive_border = "rgba(00000000)",
|
|
||||||
},
|
|
||||||
layout = columns_layout,
|
|
||||||
allow_tearing = false,
|
|
||||||
},
|
|
||||||
decoration = {
|
|
||||||
rounding = 5,
|
|
||||||
blur = {
|
|
||||||
enabled = true,
|
|
||||||
size = 7,
|
|
||||||
passes = 3,
|
|
||||||
},
|
|
||||||
active_opacity = 1.0,
|
|
||||||
inactive_opacity = 0.65,
|
|
||||||
},
|
|
||||||
animations = {
|
|
||||||
enabled = true,
|
|
||||||
},
|
|
||||||
binds = {
|
|
||||||
allow_workspace_cycles = true,
|
|
||||||
workspace_back_and_forth = true,
|
|
||||||
},
|
|
||||||
group = {
|
|
||||||
group_on_movetoworkspace = false,
|
|
||||||
col = {
|
|
||||||
border_active = "rgba(edb443ff)",
|
|
||||||
border_inactive = "rgba(091f2eff)",
|
|
||||||
},
|
|
||||||
groupbar = {
|
|
||||||
enabled = true,
|
|
||||||
blur = true,
|
|
||||||
font_size = 13,
|
|
||||||
gradients = true,
|
|
||||||
height = 26,
|
|
||||||
indicator_gap = 0,
|
|
||||||
indicator_height = 1,
|
|
||||||
rounding = 5,
|
|
||||||
gradient_rounding = 5,
|
|
||||||
text_padding = 8,
|
|
||||||
col = {
|
|
||||||
active = "rgba(edb443ff)",
|
|
||||||
inactive = "rgba(101820f2)",
|
|
||||||
},
|
|
||||||
text_color = "rgba(091018ff)",
|
|
||||||
text_color_inactive = "rgba(f2f5f7ff)",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
misc = {
|
|
||||||
force_default_wallpaper = 0,
|
|
||||||
disable_hyprland_logo = true,
|
|
||||||
exit_window_retains_fullscreen = true,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
hl.curve("overshoot", { type = "bezier", points = { { 0.05, 0.9 }, { 0.1, 1.1 } } })
|
|
||||||
hl.curve("smoothOut", { type = "bezier", points = { { 0.36, 1 }, { 0.3, 1 } } })
|
|
||||||
hl.curve("smoothInOut", { type = "bezier", points = { { 0.42, 0 }, { 0.58, 1 } } })
|
|
||||||
hl.curve("linear", { type = "bezier", points = { { 0, 0 }, { 1, 1 } } })
|
|
||||||
local spring_time_scale = 5
|
|
||||||
local function spring_curve(mass, stiffness, dampening)
|
|
||||||
return {
|
|
||||||
type = "spring",
|
|
||||||
mass = mass,
|
|
||||||
stiffness = stiffness * spring_time_scale * spring_time_scale,
|
|
||||||
dampening = dampening * spring_time_scale,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
hl.curve("workspaceSpring", spring_curve(2.4, 38, 8))
|
|
||||||
hl.curve("windowSpring", spring_curve(2.5, 40, 10))
|
|
||||||
|
|
||||||
local animations = {
|
|
||||||
{ leaf = "global", enabled = true, speed = 8, bezier = "default" },
|
|
||||||
|
|
||||||
{ leaf = "windows", enabled = true, speed = 8, spring = "windowSpring", style = "slide bottom" },
|
|
||||||
{ leaf = "windowsIn", enabled = true, speed = 8, spring = "windowSpring", style = "slide bottom" },
|
|
||||||
{ leaf = "windowsOut", enabled = true, speed = 8, spring = "windowSpring", style = "slide bottom" },
|
|
||||||
{ leaf = "windowsMove", enabled = true, speed = 8, spring = "windowSpring" },
|
|
||||||
|
|
||||||
{ leaf = "border", enabled = false },
|
|
||||||
{ leaf = "borderangle", enabled = false },
|
|
||||||
|
|
||||||
{ leaf = "fade", enabled = true, speed = 5, bezier = "smoothOut" },
|
|
||||||
{ leaf = "fadeIn", enabled = true, speed = 5, bezier = "smoothOut" },
|
|
||||||
{ leaf = "fadeOut", enabled = true, speed = 5, bezier = "smoothOut" },
|
|
||||||
{ leaf = "fadeSwitch", enabled = true, speed = 5, bezier = "smoothOut" },
|
|
||||||
{ leaf = "fadeShadow", enabled = true, speed = 5, bezier = "smoothOut" },
|
|
||||||
{ leaf = "fadeGlow", enabled = true, speed = 5, bezier = "smoothOut" },
|
|
||||||
{ leaf = "fadeDim", enabled = true, speed = 5, bezier = "smoothOut" },
|
|
||||||
{ leaf = "fadeLayers", enabled = true, speed = 5, bezier = "smoothOut" },
|
|
||||||
{ leaf = "fadeLayersIn", enabled = true, speed = 5, bezier = "smoothOut" },
|
|
||||||
{ leaf = "fadeLayersOut", enabled = true, speed = 5, bezier = "smoothOut" },
|
|
||||||
{ leaf = "fadePopups", enabled = true, speed = 5, bezier = "smoothOut" },
|
|
||||||
{ leaf = "fadePopupsIn", enabled = true, speed = 5, bezier = "smoothOut" },
|
|
||||||
{ leaf = "fadePopupsOut", enabled = true, speed = 5, bezier = "smoothOut" },
|
|
||||||
{ leaf = "fadeDpms", enabled = true, speed = 5, bezier = "smoothOut" },
|
|
||||||
|
|
||||||
{ leaf = "layers", enabled = true, speed = 5, bezier = "smoothOut", style = "fade" },
|
|
||||||
{ leaf = "layersIn", enabled = true, speed = 5, bezier = "smoothOut", style = "fade" },
|
|
||||||
{ leaf = "layersOut", enabled = true, speed = 5, bezier = "smoothOut", style = "fade" },
|
|
||||||
|
|
||||||
{ leaf = "workspaces", enabled = true, speed = 10, spring = "workspaceSpring", style = "slide" },
|
|
||||||
{ leaf = "workspacesIn", enabled = true, speed = 10, spring = "workspaceSpring", style = "slide" },
|
|
||||||
{ leaf = "workspacesOut", enabled = true, speed = 10, spring = "workspaceSpring", style = "slide" },
|
|
||||||
{ leaf = "specialWorkspace", enabled = true, speed = 8, spring = "workspaceSpring", style = "slidevert" },
|
|
||||||
{ leaf = "specialWorkspaceIn", enabled = true, speed = 8, spring = "workspaceSpring", style = "slidevert" },
|
|
||||||
{ leaf = "specialWorkspaceOut", enabled = true, speed = 8, spring = "workspaceSpring", style = "slidevert" },
|
|
||||||
|
|
||||||
{ leaf = "zoomFactor", enabled = true, speed = 7, bezier = "smoothOut" },
|
|
||||||
-- Disabled for now: Hyprland 0.54.0 can crash while damaging a monitor
|
|
||||||
-- from this startup animation's update callback during output discovery.
|
|
||||||
-- { leaf = "monitorAdded", enabled = true, speed = 5, bezier = "smoothOut" },
|
|
||||||
{ leaf = "monitorAdded", enabled = false, speed = 5, bezier = "smoothOut" },
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, animation in ipairs(animations) do
|
|
||||||
hl.animation(animation)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function apply_hyprglass_config()
|
|
||||||
if verify_config or not enable_hyprglass then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
hl.config({
|
|
||||||
plugin = {
|
|
||||||
hyprglass = {
|
|
||||||
enabled = 0,
|
|
||||||
default_theme = "dark",
|
|
||||||
default_preset = "default",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
local function apply_hyprwobbly_config()
|
|
||||||
if verify_config or not enable_hyprwobbly then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
hl.config({
|
|
||||||
plugin = {
|
|
||||||
hyprwobbly = {
|
|
||||||
enabled = hypr_visual_performance_mode and 0 or 1,
|
|
||||||
mode = "always",
|
|
||||||
grid_width = 4,
|
|
||||||
grid_height = 4,
|
|
||||||
tiles_x = 12,
|
|
||||||
tiles_y = 12,
|
|
||||||
spring_k = 18.0,
|
|
||||||
friction = 8.0,
|
|
||||||
mass = 12.0,
|
|
||||||
move_factor = 0.65,
|
|
||||||
resize_factor = 0.45,
|
|
||||||
max_warp = 140.0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
local function apply_visual_performance_mode()
|
|
||||||
if verify_config then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local visual_effects_enabled = not hypr_visual_performance_mode
|
|
||||||
hl.config({
|
|
||||||
decoration = {
|
|
||||||
blur = {
|
|
||||||
enabled = visual_effects_enabled,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
animations = {
|
|
||||||
enabled = visual_effects_enabled,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
if enable_hyprwobbly then
|
|
||||||
hl.config({
|
|
||||||
plugin = {
|
|
||||||
hyprwobbly = {
|
|
||||||
enabled = visual_effects_enabled and 1 or 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function toggle_visual_performance_mode()
|
|
||||||
hypr_visual_performance_mode = not hypr_visual_performance_mode
|
|
||||||
apply_visual_performance_mode()
|
|
||||||
hl.notification.create({
|
|
||||||
text = "Hyprland performance mode: " .. (hypr_visual_performance_mode and "on" or "off"),
|
|
||||||
duration = 1800,
|
|
||||||
icon = hypr_visual_performance_mode and notification_icons.warning or notification_icons.ok,
|
|
||||||
color = hypr_visual_performance_mode and "rgba(edb443ff)" or "rgba(33ccffee)",
|
|
||||||
font_size = 13,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
local function apply_rules()
|
|
||||||
if verify_config then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
hl.workspace_rule({ workspace = "w[tv1]s[false]", gaps_out = 0, gaps_in = 0 })
|
|
||||||
hl.workspace_rule({ workspace = "f[1]s[false]", gaps_out = 0, gaps_in = 0 })
|
|
||||||
|
|
||||||
hl.window_rule({ match = { class = "^()$", title = "^()$" }, float = true })
|
|
||||||
hl.window_rule({ match = { title = "^(Picture-in-Picture)$" }, float = true })
|
|
||||||
hl.window_rule({
|
|
||||||
name = "rofi-glass-window",
|
|
||||||
match = { class = "^(rofi)$" },
|
|
||||||
float = true,
|
|
||||||
center = true,
|
|
||||||
decorate = false,
|
|
||||||
no_shadow = true,
|
|
||||||
xray = false,
|
|
||||||
})
|
|
||||||
hl.layer_rule({
|
|
||||||
name = "rofi-glass-layer",
|
|
||||||
match = { namespace = "^(rofi)$" },
|
|
||||||
blur = true,
|
|
||||||
ignore_alpha = 0.05,
|
|
||||||
xray = false,
|
|
||||||
})
|
|
||||||
hl.window_rule({
|
|
||||||
name = "file-chooser-dialogs",
|
|
||||||
match = { title = file_chooser_title_rule },
|
|
||||||
float = true,
|
|
||||||
center = true,
|
|
||||||
focus_on_activate = true,
|
|
||||||
stay_focused = true,
|
|
||||||
})
|
|
||||||
hl.window_rule({ match = { title = "^(Confirm)$" }, float = true })
|
|
||||||
|
|
||||||
for index, match in ipairs({
|
|
||||||
{ class = "^(flameshot)$" },
|
|
||||||
{ title = "^(flameshot)$" },
|
|
||||||
}) do
|
|
||||||
hl.window_rule({
|
|
||||||
name = "flameshot-overlay-" .. tostring(index),
|
|
||||||
match = match,
|
|
||||||
float = true,
|
|
||||||
no_anim = true,
|
|
||||||
suppress_event = "fullscreen",
|
|
||||||
})
|
|
||||||
end
|
|
||||||
hl.layer_rule({
|
|
||||||
name = "flameshot-layer-overlay",
|
|
||||||
match = { namespace = "^(flameshot)$" },
|
|
||||||
no_anim = true,
|
|
||||||
})
|
|
||||||
|
|
||||||
hl.window_rule({
|
|
||||||
match = { class = "^(com\\.mitchellh\\.ghostty\\.dropdown)$" },
|
|
||||||
no_anim = true,
|
|
||||||
})
|
|
||||||
hl.window_rule({
|
|
||||||
match = { class = "^(com\\.mitchellh\\.ghostty\\.dropdown)$" },
|
|
||||||
tag = "+hyprglass_enabled",
|
|
||||||
})
|
|
||||||
hl.window_rule({
|
|
||||||
match = { class = "^(com\\.mitchellh\\.ghostty\\.dropdown)$" },
|
|
||||||
tag = "+hyprglass_theme_light",
|
|
||||||
})
|
|
||||||
hl.window_rule({
|
|
||||||
match = { class = "^(.*[Rr]umno.*)$" },
|
|
||||||
float = true,
|
|
||||||
pin = true,
|
|
||||||
center = true,
|
|
||||||
decorate = false,
|
|
||||||
no_shadow = true,
|
|
||||||
})
|
|
||||||
hl.window_rule({
|
|
||||||
match = { title = "^(.*[Rr]umno.*)$" },
|
|
||||||
float = true,
|
|
||||||
pin = true,
|
|
||||||
center = true,
|
|
||||||
decorate = false,
|
|
||||||
no_shadow = true,
|
|
||||||
})
|
|
||||||
hl.window_rule({
|
|
||||||
name = "subtle-pinned-window-border",
|
|
||||||
match = { pin = true },
|
|
||||||
border_size = 2,
|
|
||||||
border_color = "rgba(edb443ff) rgba(ff4d5dcc)",
|
|
||||||
})
|
|
||||||
hl.window_rule({
|
|
||||||
match = { tag = inactive_opacity_override_tag },
|
|
||||||
opacity = "1.0 override 1.0 override 1.0 override",
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
ctx.apply_rules = apply_rules
|
|
||||||
ctx.apply_hyprglass_config = apply_hyprglass_config
|
|
||||||
ctx.apply_hyprwobbly_config = apply_hyprwobbly_config
|
|
||||||
ctx.apply_visual_performance_mode = apply_visual_performance_mode
|
|
||||||
ctx.is_file_chooser_window = is_file_chooser_window
|
|
||||||
ctx.raise_file_chooser_window = raise_file_chooser_window
|
|
||||||
ctx.raise_file_chooser_window_later = raise_file_chooser_window_later
|
|
||||||
ctx.toggle_visual_performance_mode = toggle_visual_performance_mode
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
local shell_ui_command = "hypr_shell_ui"
|
|
||||||
local columns_layout = "nStack"
|
|
||||||
local large_main_layout = "master"
|
|
||||||
local grid_layout = "grid"
|
|
||||||
local monocle_layout = "monocle"
|
|
||||||
|
|
||||||
return {
|
|
||||||
main_mod = "SUPER",
|
|
||||||
mod_alt = "SUPER + ALT",
|
|
||||||
hyper = "SUPER + CTRL + ALT",
|
|
||||||
|
|
||||||
terminal = "ghostty --gtk-single-instance=false",
|
|
||||||
shell_ui_command = shell_ui_command,
|
|
||||||
launcher_command = shell_ui_command .. " launcher",
|
|
||||||
run_menu = shell_ui_command .. " run",
|
|
||||||
|
|
||||||
-- Hyprland shadows ordinary keybinds after one fires; without transparent,
|
|
||||||
-- the first overview chord after a focus-moving bind can be skipped.
|
|
||||||
overview_bind_opts = { dont_inhibit = true, transparent = true },
|
|
||||||
overview_trace_enabled_path = "/tmp/hypr-overview-bind.enable",
|
|
||||||
overview_trace_path = "/tmp/hypr-overview-bind.log",
|
|
||||||
notification_icons = {
|
|
||||||
warning = 0,
|
|
||||||
info = 1,
|
|
||||||
hint = 2,
|
|
||||||
error = 3,
|
|
||||||
confused = 4,
|
|
||||||
ok = 5,
|
|
||||||
none = 6,
|
|
||||||
},
|
|
||||||
|
|
||||||
max_workspace = 9,
|
|
||||||
columns_layout = columns_layout,
|
|
||||||
large_main_layout = large_main_layout,
|
|
||||||
grid_layout = grid_layout,
|
|
||||||
monocle_layout = monocle_layout,
|
|
||||||
layout_cycle = { columns_layout, large_main_layout, grid_layout },
|
|
||||||
layout_names = {
|
|
||||||
[columns_layout] = "Columns",
|
|
||||||
[large_main_layout] = "Large main",
|
|
||||||
[grid_layout] = "Grid",
|
|
||||||
[monocle_layout] = "Monocle",
|
|
||||||
},
|
|
||||||
minimized_workspace = "special:minimized",
|
|
||||||
inactive_opacity_override_tag = "no-inactive-opacity",
|
|
||||||
tabbed_group_restore_workspace_prefix = "special:tabbed-monocle-restore-",
|
|
||||||
current_layout = columns_layout,
|
|
||||||
enable_nstack = true,
|
|
||||||
enable_hyprexpo = true,
|
|
||||||
enable_hyprwinview = true,
|
|
||||||
enable_workspace_history = true,
|
|
||||||
enable_hyprwobbly = true,
|
|
||||||
enable_hyprglass = false,
|
|
||||||
hypr_visual_performance_mode = false,
|
|
||||||
configure_nstack_plugin_from_lua = false,
|
|
||||||
workspace_layouts = {},
|
|
||||||
minimized_windows = {},
|
|
||||||
tabbed_workspace_groups = {},
|
|
||||||
window_picker_mode = nil,
|
|
||||||
window_picker_candidates = {},
|
|
||||||
stack_update_timer = nil,
|
|
||||||
monocle_notice = nil,
|
|
||||||
}
|
|
||||||
@@ -1,502 +0,0 @@
|
|||||||
local M = {}
|
|
||||||
|
|
||||||
function M.setup(ctx)
|
|
||||||
local _ENV = ctx
|
|
||||||
local function same_class_windows(class_name)
|
|
||||||
local windows = {}
|
|
||||||
if not class_name or class_name == "" then
|
|
||||||
return windows
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, window in ipairs(hl.get_windows()) do
|
|
||||||
if is_normal_window(window) and window.class == class_name then
|
|
||||||
windows[#windows + 1] = window
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return windows
|
|
||||||
end
|
|
||||||
|
|
||||||
local function short_text(value, limit)
|
|
||||||
value = tostring(value or "")
|
|
||||||
value = value:gsub("[%c\t\r\n]", " ")
|
|
||||||
if #value <= limit then
|
|
||||||
return value
|
|
||||||
end
|
|
||||||
return value:sub(1, limit - 3) .. "..."
|
|
||||||
end
|
|
||||||
|
|
||||||
local function normal_windows()
|
|
||||||
local windows = {}
|
|
||||||
for _, window in ipairs(hl.get_windows()) do
|
|
||||||
if is_normal_window(window) then
|
|
||||||
windows[#windows + 1] = window
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
table.sort(windows, function(left, right)
|
|
||||||
local left_workspace = left.workspace and left.workspace.id or max_workspace + 1
|
|
||||||
local right_workspace = right.workspace and right.workspace.id or max_workspace + 1
|
|
||||||
if left_workspace ~= right_workspace then
|
|
||||||
return left_workspace < right_workspace
|
|
||||||
end
|
|
||||||
return (left.focus_history_id or 0) < (right.focus_history_id or 0)
|
|
||||||
end)
|
|
||||||
|
|
||||||
return windows
|
|
||||||
end
|
|
||||||
|
|
||||||
local function window_picker_entry(index, window)
|
|
||||||
local workspace = window.workspace and window.workspace.id or "?"
|
|
||||||
local class = short_text(window.class, 18)
|
|
||||||
local title = short_text(window.title, 48)
|
|
||||||
return tostring(index) .. " [" .. tostring(workspace) .. "] " .. class .. " " .. title
|
|
||||||
end
|
|
||||||
|
|
||||||
local function remove_minimized_window(target)
|
|
||||||
local remaining = {}
|
|
||||||
local target_address = target and target.address
|
|
||||||
for _, window in ipairs(minimized_windows) do
|
|
||||||
if window and window.address ~= target_address then
|
|
||||||
remaining[#remaining + 1] = window
|
|
||||||
end
|
|
||||||
end
|
|
||||||
minimized_windows = remaining
|
|
||||||
end
|
|
||||||
|
|
||||||
local function add_minimized_window(window)
|
|
||||||
if not window or not window.address then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
remove_minimized_window(window)
|
|
||||||
minimized_windows[#minimized_windows + 1] = window
|
|
||||||
end
|
|
||||||
|
|
||||||
local function hydrate_minimized_windows()
|
|
||||||
local by_address = {}
|
|
||||||
local current_by_address = {}
|
|
||||||
local hydrated = {}
|
|
||||||
|
|
||||||
for _, window in ipairs(hl.get_windows()) do
|
|
||||||
if window and window.address then
|
|
||||||
current_by_address[window.address] = window
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, window in ipairs(minimized_windows) do
|
|
||||||
local current = window and window.address and current_by_address[window.address]
|
|
||||||
if current and is_minimized_window(current) and not by_address[current.address] then
|
|
||||||
by_address[current.address] = true
|
|
||||||
hydrated[#hydrated + 1] = current
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, window in pairs(current_by_address) do
|
|
||||||
if window and window.address and is_minimized_window(window) and not by_address[window.address] then
|
|
||||||
by_address[window.address] = true
|
|
||||||
hydrated[#hydrated + 1] = window
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
minimized_windows = hydrated
|
|
||||||
end
|
|
||||||
|
|
||||||
local function float_active_window_preserving_tiled_geometry()
|
|
||||||
local geometry = tiled_window_geometry(hl.get_active_window())
|
|
||||||
dispatch(hl.dsp.window.float({ action = "enable", window = geometry and geometry.selector or nil }))
|
|
||||||
if geometry then
|
|
||||||
dispatch(hl.dsp.window.resize({ x = geometry.width, y = geometry.height, relative = false, window = geometry.selector }))
|
|
||||||
dispatch(hl.dsp.window.move({ x = geometry.x, y = geometry.y, relative = false, window = geometry.selector }))
|
|
||||||
end
|
|
||||||
return geometry
|
|
||||||
end
|
|
||||||
|
|
||||||
local function float_and_drag_active_window()
|
|
||||||
float_active_window_preserving_tiled_geometry()
|
|
||||||
dispatch(hl.dsp.window.drag())
|
|
||||||
end
|
|
||||||
|
|
||||||
local function float_and_resize_active_window()
|
|
||||||
float_active_window_preserving_tiled_geometry()
|
|
||||||
dispatch(hl.dsp.window.resize())
|
|
||||||
end
|
|
||||||
|
|
||||||
local function toggle_pinned_active_window()
|
|
||||||
local window = hl.get_active_window()
|
|
||||||
local selector = window_selector(window)
|
|
||||||
if not window or not selector then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if window.pinned then
|
|
||||||
dispatch(hl.dsp.window.pin({ action = "disable", window = selector }))
|
|
||||||
dispatch(hl.dsp.window.float({ action = "disable", window = selector }))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if not window.floating then
|
|
||||||
float_active_window_preserving_tiled_geometry()
|
|
||||||
end
|
|
||||||
dispatch(hl.dsp.window.pin({ action = "enable", window = selector }))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function current_minimized_windows()
|
|
||||||
hydrate_minimized_windows()
|
|
||||||
|
|
||||||
local windows = {}
|
|
||||||
for _, window in ipairs(minimized_windows) do
|
|
||||||
if window and window.address and is_minimized_window(window) then
|
|
||||||
windows[#windows + 1] = window
|
|
||||||
end
|
|
||||||
end
|
|
||||||
minimized_windows = windows
|
|
||||||
return windows
|
|
||||||
end
|
|
||||||
|
|
||||||
local function restore_minimized_window(window, workspace)
|
|
||||||
if not window or not workspace then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
move_window_to_workspace(workspace.id, false, window)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
local function window_picker_candidates_for(mode)
|
|
||||||
if mode == "minimized" then
|
|
||||||
return current_minimized_windows()
|
|
||||||
end
|
|
||||||
|
|
||||||
local focused = hl.get_active_window()
|
|
||||||
local workspace = active_workspace()
|
|
||||||
local candidates = {}
|
|
||||||
|
|
||||||
for _, window in ipairs(normal_windows()) do
|
|
||||||
local include = true
|
|
||||||
if mode == "bring" and workspace and window.workspace == workspace then
|
|
||||||
include = false
|
|
||||||
elseif mode == "replace" and focused and window == focused then
|
|
||||||
include = false
|
|
||||||
end
|
|
||||||
|
|
||||||
if include then
|
|
||||||
candidates[#candidates + 1] = window
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return candidates
|
|
||||||
end
|
|
||||||
|
|
||||||
local function activate_window_picker_candidate(index)
|
|
||||||
local window = window_picker_candidates[index]
|
|
||||||
local mode = window_picker_mode
|
|
||||||
window_picker_mode = nil
|
|
||||||
window_picker_candidates = {}
|
|
||||||
dispatch(hl.dsp.submap("reset"))
|
|
||||||
|
|
||||||
if not window then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if mode == "go" then
|
|
||||||
dispatch(hl.dsp.focus({ window = window_selector(window) }))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local workspace = active_workspace()
|
|
||||||
if mode == "bring" and workspace then
|
|
||||||
move_window_to_workspace(workspace.id, false, window)
|
|
||||||
dispatch(hl.dsp.focus({ window = window_selector(window) }))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if mode == "minimized" and workspace then
|
|
||||||
remove_minimized_window(window)
|
|
||||||
restore_minimized_window(window, workspace)
|
|
||||||
dispatch(hl.dsp.focus({ window = window_selector(window) }))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if mode == "replace" then
|
|
||||||
local focused = hl.get_active_window()
|
|
||||||
if focused and focused ~= window then
|
|
||||||
dispatch(hl.dsp.window.swap({ target = window_selector(window), window = window_selector(focused) }))
|
|
||||||
dispatch(hl.dsp.focus({ window = window_selector(window) }))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function enter_window_picker(mode)
|
|
||||||
window_picker_mode = mode
|
|
||||||
window_picker_candidates = window_picker_candidates_for(mode)
|
|
||||||
|
|
||||||
if #window_picker_candidates == 0 then
|
|
||||||
local empty_text = "No windows available"
|
|
||||||
if mode == "minimized" then
|
|
||||||
empty_text = "No minimized windows"
|
|
||||||
end
|
|
||||||
|
|
||||||
hl.notification.create({
|
|
||||||
text = empty_text,
|
|
||||||
duration = 1800,
|
|
||||||
icon = notification_icons.info,
|
|
||||||
color = "rgba(edb443ff)",
|
|
||||||
font_size = 13,
|
|
||||||
})
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local lines = {}
|
|
||||||
local count = math.min(#window_picker_candidates, 9)
|
|
||||||
for i = 1, count do
|
|
||||||
lines[#lines + 1] = window_picker_entry(i, window_picker_candidates[i])
|
|
||||||
end
|
|
||||||
|
|
||||||
hl.notification.create({
|
|
||||||
text = table.concat(lines, "\n"),
|
|
||||||
duration = 5000,
|
|
||||||
icon = notification_icons.info,
|
|
||||||
color = "rgba(edb443ff)",
|
|
||||||
font_size = 11,
|
|
||||||
})
|
|
||||||
dispatch(hl.dsp.submap("window-picker"))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function gather_focused_class()
|
|
||||||
local focused = hl.get_active_window()
|
|
||||||
local workspace = active_workspace()
|
|
||||||
if not focused or not workspace or not focused.class or focused.class == "" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local count = 0
|
|
||||||
for _, window in ipairs(same_class_windows(focused.class)) do
|
|
||||||
if window ~= focused and window.workspace ~= workspace then
|
|
||||||
move_window_to_workspace(workspace.id, false, window)
|
|
||||||
count = count + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
hl.notification.create({
|
|
||||||
text = "Gathered " .. tostring(count) .. " " .. focused.class .. " windows",
|
|
||||||
duration = 1600,
|
|
||||||
icon = notification_icons.info,
|
|
||||||
color = "rgba(edb443ff)",
|
|
||||||
font_size = 13,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
local function focus_next_class()
|
|
||||||
local focused = hl.get_active_window()
|
|
||||||
if not focused or not focused.class or focused.class == "" then
|
|
||||||
dispatch(hl.dsp.window.cycle_next({ next = true, tiled = true, floating = false }))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local classes = {}
|
|
||||||
local first_by_class = {}
|
|
||||||
for _, window in ipairs(hl.get_windows()) do
|
|
||||||
if is_normal_window(window) and window.class and window.class ~= "" and not first_by_class[window.class] then
|
|
||||||
first_by_class[window.class] = window
|
|
||||||
classes[#classes + 1] = window.class
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
table.sort(classes)
|
|
||||||
if #classes <= 1 then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local current_index = 1
|
|
||||||
for index, class_name in ipairs(classes) do
|
|
||||||
if class_name == focused.class then
|
|
||||||
current_index = index
|
|
||||||
break
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local next_class = classes[(current_index % #classes) + 1]
|
|
||||||
local target = first_by_class[next_class]
|
|
||||||
if target then
|
|
||||||
dispatch(hl.dsp.focus({ window = window_selector(target) }))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function show_active_window_info()
|
|
||||||
local window = hl.get_active_window()
|
|
||||||
if not window then
|
|
||||||
hl.notification.create({
|
|
||||||
text = "No active window",
|
|
||||||
duration = 1800,
|
|
||||||
icon = notification_icons.info,
|
|
||||||
color = "rgba(edb443ff)",
|
|
||||||
font_size = 13,
|
|
||||||
})
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local workspace = window.workspace and (window.workspace.name or window.workspace.id) or "?"
|
|
||||||
local lines = {
|
|
||||||
"Class: " .. tostring(window.class or ""),
|
|
||||||
"Title: " .. tostring(window.title or ""),
|
|
||||||
"Workspace: " .. tostring(workspace),
|
|
||||||
"Pinned: " .. tostring(window.pinned or false),
|
|
||||||
"Address: " .. tostring(window.address or ""),
|
|
||||||
"PID: " .. tostring(window.pid or ""),
|
|
||||||
}
|
|
||||||
|
|
||||||
hl.notification.create({
|
|
||||||
text = table.concat(lines, "\n"),
|
|
||||||
duration = 5000,
|
|
||||||
icon = notification_icons.info,
|
|
||||||
color = "rgba(edb443ff)",
|
|
||||||
font_size = 11,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
local function window_has_tag(window, tag)
|
|
||||||
for _, value in ipairs((window and window.tags) or {}) do
|
|
||||||
if tostring(value):gsub("%*$", "") == tag then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local function toggle_inactive_opacity_for_active_window()
|
|
||||||
local window = hl.get_active_window()
|
|
||||||
local selector = window_selector(window)
|
|
||||||
if not selector then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local disabling_reduction = not window_has_tag(window, inactive_opacity_override_tag)
|
|
||||||
dispatch(hl.dsp.window.tag({ tag = inactive_opacity_override_tag, window = selector }))
|
|
||||||
hl.notification.create({
|
|
||||||
text = "Inactive opacity reduction: " .. (disabling_reduction and "off for window" or "on for window"),
|
|
||||||
duration = 1600,
|
|
||||||
icon = notification_icons.info,
|
|
||||||
color = "rgba(edb443ff)",
|
|
||||||
font_size = 13,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
local function raise_or_spawn(class_fragment, command)
|
|
||||||
local fragment = string.lower(class_fragment)
|
|
||||||
for _, window in ipairs(hl.get_windows()) do
|
|
||||||
if is_normal_window(window) and window.class and string.find(string.lower(window.class), fragment, 1, true) then
|
|
||||||
dispatch(hl.dsp.focus({ window = window_selector(window) }))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
hl.exec_cmd(command)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function minimize_active_window()
|
|
||||||
local window = hl.get_active_window()
|
|
||||||
if not window then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
add_minimized_window(window)
|
|
||||||
move_window_to_workspace(minimized_workspace, false, window)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function restore_last_minimized()
|
|
||||||
local workspace = active_workspace()
|
|
||||||
if not workspace then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
hydrate_minimized_windows()
|
|
||||||
|
|
||||||
while #minimized_windows > 0 do
|
|
||||||
local window = table.remove(minimized_windows)
|
|
||||||
if window and window.address and is_minimized_window(window) then
|
|
||||||
restore_minimized_window(window, workspace)
|
|
||||||
dispatch(hl.dsp.focus({ window = window_selector(window) }))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function restore_all_minimized()
|
|
||||||
local workspace = active_workspace()
|
|
||||||
if not workspace then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
hydrate_minimized_windows()
|
|
||||||
|
|
||||||
while #minimized_windows > 0 do
|
|
||||||
restore_minimized_window(table.remove(minimized_windows), workspace)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function minimize_other_classes()
|
|
||||||
local focused = hl.get_active_window()
|
|
||||||
local workspace = active_workspace()
|
|
||||||
if not focused or not workspace then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, window in ipairs(tiled_windows(workspace)) do
|
|
||||||
if window ~= focused and window.class ~= focused.class then
|
|
||||||
add_minimized_window(window)
|
|
||||||
move_window_to_workspace(minimized_workspace, false, window)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function restore_focused_class()
|
|
||||||
local focused = hl.get_active_window()
|
|
||||||
local workspace = active_workspace()
|
|
||||||
if not focused or not workspace or not focused.class then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
hydrate_minimized_windows()
|
|
||||||
|
|
||||||
local remaining = {}
|
|
||||||
for _, window in ipairs(minimized_windows) do
|
|
||||||
if window and window.class == focused.class and is_minimized_window(window) then
|
|
||||||
restore_minimized_window(window, workspace)
|
|
||||||
else
|
|
||||||
remaining[#remaining + 1] = window
|
|
||||||
end
|
|
||||||
end
|
|
||||||
minimized_windows = remaining
|
|
||||||
end
|
|
||||||
|
|
||||||
ctx.same_class_windows = same_class_windows
|
|
||||||
ctx.short_text = short_text
|
|
||||||
ctx.normal_windows = normal_windows
|
|
||||||
ctx.window_picker_entry = window_picker_entry
|
|
||||||
ctx.remove_minimized_window = remove_minimized_window
|
|
||||||
ctx.add_minimized_window = add_minimized_window
|
|
||||||
ctx.hydrate_minimized_windows = hydrate_minimized_windows
|
|
||||||
ctx.float_active_window_preserving_tiled_geometry = float_active_window_preserving_tiled_geometry
|
|
||||||
ctx.float_and_drag_active_window = float_and_drag_active_window
|
|
||||||
ctx.float_and_resize_active_window = float_and_resize_active_window
|
|
||||||
ctx.toggle_pinned_active_window = toggle_pinned_active_window
|
|
||||||
ctx.current_minimized_windows = current_minimized_windows
|
|
||||||
ctx.restore_minimized_window = restore_minimized_window
|
|
||||||
ctx.window_picker_candidates_for = window_picker_candidates_for
|
|
||||||
ctx.activate_window_picker_candidate = activate_window_picker_candidate
|
|
||||||
ctx.enter_window_picker = enter_window_picker
|
|
||||||
ctx.gather_focused_class = gather_focused_class
|
|
||||||
ctx.focus_next_class = focus_next_class
|
|
||||||
ctx.show_active_window_info = show_active_window_info
|
|
||||||
ctx.toggle_inactive_opacity_for_active_window = toggle_inactive_opacity_for_active_window
|
|
||||||
ctx.raise_or_spawn = raise_or_spawn
|
|
||||||
ctx.minimize_active_window = minimize_active_window
|
|
||||||
ctx.restore_last_minimized = restore_last_minimized
|
|
||||||
ctx.restore_all_minimized = restore_all_minimized
|
|
||||||
ctx.minimize_other_classes = minimize_other_classes
|
|
||||||
ctx.restore_focused_class = restore_focused_class
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
|
||||||
40
dotfiles/config/hypr/scripts/bring-window.sh
Executable file
40
dotfiles/config/hypr/scripts/bring-window.sh
Executable file
@@ -0,0 +1,40 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Bring window to current workspace (like XMonad's bringWindow)
|
||||||
|
# Uses rofi with icons to select a window, then moves it here.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
source "$SCRIPT_DIR/window-icon-map.sh"
|
||||||
|
|
||||||
|
CURRENT_WS=$(hyprctl activeworkspace -j | jq -r '.id')
|
||||||
|
|
||||||
|
# Get windows on OTHER workspaces as TSV
|
||||||
|
WINDOW_DATA=$(hyprctl clients -j | jq -r --argjson cws "$CURRENT_WS" '
|
||||||
|
.[] | select(.workspace.id >= 0 and .workspace.id != $cws)
|
||||||
|
| [.address, .class, (.title | gsub("\t"; " ")), (.workspace.id | tostring)]
|
||||||
|
| @tsv')
|
||||||
|
|
||||||
|
if [ -z "$WINDOW_DATA" ]; then
|
||||||
|
notify-send "Bring Window" "No windows on other workspaces"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
addresses=()
|
||||||
|
TMPFILE=$(mktemp)
|
||||||
|
trap 'rm -f "$TMPFILE"' EXIT
|
||||||
|
|
||||||
|
while IFS=$'\t' read -r address class title ws_id; do
|
||||||
|
icon=$(icon_for_class "$class")
|
||||||
|
addresses+=("$address")
|
||||||
|
printf '%-24s %s WS:%s\0icon\x1f%s\n' \
|
||||||
|
"$class" "$title" "$ws_id" "$icon"
|
||||||
|
done <<< "$WINDOW_DATA" > "$TMPFILE"
|
||||||
|
|
||||||
|
INDEX=$(rofi -dmenu -i -show-icons -p "Bring window" -format i < "$TMPFILE") || exit 0
|
||||||
|
|
||||||
|
if [ -n "$INDEX" ] && [ -n "${addresses[$INDEX]:-}" ]; then
|
||||||
|
ADDRESS="${addresses[$INDEX]}"
|
||||||
|
hyprctl dispatch movetoworkspace "$CURRENT_WS,address:$ADDRESS"
|
||||||
|
hyprctl dispatch focuswindow "address:$ADDRESS"
|
||||||
|
fi
|
||||||
15
dotfiles/config/hypr/scripts/cycle-layout.sh
Executable file
15
dotfiles/config/hypr/scripts/cycle-layout.sh
Executable file
@@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Cycle between master and dwindle layouts
|
||||||
|
# Like XMonad's NextLayout
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
CURRENT=$(hyprctl getoption general:layout -j | jq -r '.str')
|
||||||
|
|
||||||
|
if [ "$CURRENT" = "master" ]; then
|
||||||
|
hyprctl keyword general:layout dwindle
|
||||||
|
notify-send "Layout" "Switched to Dwindle (binary tree)"
|
||||||
|
else
|
||||||
|
hyprctl keyword general:layout master
|
||||||
|
notify-send "Layout" "Switched to Master (XMonad-like)"
|
||||||
|
fi
|
||||||
72
dotfiles/config/hypr/scripts/find-empty-workspace.sh
Executable file
72
dotfiles/config/hypr/scripts/find-empty-workspace.sh
Executable file
@@ -0,0 +1,72 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Print an "empty" workspace id within 1..$HYPR_MAX_WORKSPACE (default 9).
|
||||||
|
#
|
||||||
|
# Preference order (lowest id wins within each tier):
|
||||||
|
# 1. Workspace exists on the target monitor and has 0 windows
|
||||||
|
# 2. Workspace id does not exist at all (will be created on dispatch)
|
||||||
|
# 3. Workspace exists (elsewhere) and has 0 windows
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# find-empty-workspace.sh [monitor] [exclude_id]
|
||||||
|
|
||||||
|
max_ws="${HYPR_MAX_WORKSPACE:-9}"
|
||||||
|
|
||||||
|
monitor="${1:-}"
|
||||||
|
exclude_id="${2:-}"
|
||||||
|
|
||||||
|
if [[ -z "${monitor}" ]]; then
|
||||||
|
monitor="$(hyprctl activeworkspace -j | jq -r '.monitor' 2>/dev/null || true)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "${monitor}" || "${monitor}" == "null" ]]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
workspaces_json="$(hyprctl workspaces -j 2>/dev/null || echo '[]')"
|
||||||
|
|
||||||
|
unused_candidate=""
|
||||||
|
elsewhere_empty_candidate=""
|
||||||
|
|
||||||
|
for i in $(seq 1 "${max_ws}"); do
|
||||||
|
if [[ -n "${exclude_id}" && "${i}" == "${exclude_id}" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
exists="$(jq -r --argjson id "${i}" '[.[] | select(.id == $id)] | length' <<<"${workspaces_json}")"
|
||||||
|
if [[ "${exists}" == "0" ]]; then
|
||||||
|
if [[ -z "${unused_candidate}" ]]; then
|
||||||
|
unused_candidate="${i}"
|
||||||
|
fi
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
windows="$(jq -r --argjson id "${i}" '([.[] | select(.id == $id) | .windows] | .[0]) // 0' <<<"${workspaces_json}")"
|
||||||
|
if [[ "${windows}" != "0" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
ws_monitor="$(jq -r --argjson id "${i}" '([.[] | select(.id == $id) | .monitor] | .[0]) // ""' <<<"${workspaces_json}")"
|
||||||
|
if [[ "${ws_monitor}" == "${monitor}" ]]; then
|
||||||
|
printf '%s\n' "${i}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "${elsewhere_empty_candidate}" ]]; then
|
||||||
|
elsewhere_empty_candidate="${i}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -n "${unused_candidate}" ]]; then
|
||||||
|
printf '%s\n' "${unused_candidate}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "${elsewhere_empty_candidate}" ]]; then
|
||||||
|
printf '%s\n' "${elsewhere_empty_candidate}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 1
|
||||||
|
|
||||||
48
dotfiles/config/hypr/scripts/focus-next-class.sh
Executable file
48
dotfiles/config/hypr/scripts/focus-next-class.sh
Executable file
@@ -0,0 +1,48 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Focus next window of a different class (like XMonad's focusNextClass)
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Get focused window class
|
||||||
|
FOCUSED_CLASS=$(hyprctl activewindow -j | jq -r '.class')
|
||||||
|
FOCUSED_ADDR=$(hyprctl activewindow -j | jq -r '.address')
|
||||||
|
|
||||||
|
if [ "$FOCUSED_CLASS" = "null" ] || [ -z "$FOCUSED_CLASS" ]; then
|
||||||
|
# No focused window, just focus any window
|
||||||
|
hyprctl dispatch cyclenext
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get all unique classes
|
||||||
|
ALL_CLASSES=$(hyprctl clients -j | jq -r '[.[] | select(.workspace.id >= 0) | .class] | unique | .[]')
|
||||||
|
|
||||||
|
# Get sorted list of classes
|
||||||
|
CLASSES_ARRAY=()
|
||||||
|
while IFS= read -r class; do
|
||||||
|
CLASSES_ARRAY+=("$class")
|
||||||
|
done <<< "$ALL_CLASSES"
|
||||||
|
|
||||||
|
# Find current class index and get next class
|
||||||
|
CURRENT_INDEX=-1
|
||||||
|
for i in "${!CLASSES_ARRAY[@]}"; do
|
||||||
|
if [ "${CLASSES_ARRAY[$i]}" = "$FOCUSED_CLASS" ]; then
|
||||||
|
CURRENT_INDEX=$i
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $CURRENT_INDEX -eq -1 ] || [ ${#CLASSES_ARRAY[@]} -le 1 ]; then
|
||||||
|
# Only one class or class not found
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get next class (wrapping around)
|
||||||
|
NEXT_INDEX=$(( (CURRENT_INDEX + 1) % ${#CLASSES_ARRAY[@]} ))
|
||||||
|
NEXT_CLASS="${CLASSES_ARRAY[$NEXT_INDEX]}"
|
||||||
|
|
||||||
|
# Find first window of next class
|
||||||
|
NEXT_WINDOW=$(hyprctl clients -j | jq -r ".[] | select(.class == \"$NEXT_CLASS\" and .workspace.id >= 0) | .address" | head -1)
|
||||||
|
|
||||||
|
if [ -n "$NEXT_WINDOW" ]; then
|
||||||
|
hyprctl dispatch focuswindow "address:$NEXT_WINDOW"
|
||||||
|
fi
|
||||||
30
dotfiles/config/hypr/scripts/gather-class.sh
Executable file
30
dotfiles/config/hypr/scripts/gather-class.sh
Executable file
@@ -0,0 +1,30 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Gather all windows of the same class as focused window (like XMonad's gatherThisClass)
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Get focused window class
|
||||||
|
FOCUSED_CLASS=$(hyprctl activewindow -j | jq -r '.class')
|
||||||
|
CURRENT_WS=$(hyprctl activeworkspace -j | jq -r '.id')
|
||||||
|
|
||||||
|
if [ "$FOCUSED_CLASS" = "null" ] || [ -z "$FOCUSED_CLASS" ]; then
|
||||||
|
notify-send "Gather Class" "No focused window"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Find all windows with same class on other workspaces
|
||||||
|
WINDOWS=$(hyprctl clients -j | jq -r ".[] | select(.class == \"$FOCUSED_CLASS\" and .workspace.id != $CURRENT_WS and .workspace.id >= 0) | .address")
|
||||||
|
|
||||||
|
if [ -z "$WINDOWS" ]; then
|
||||||
|
notify-send "Gather Class" "No other windows of class '$FOCUSED_CLASS'"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Move each window to current workspace
|
||||||
|
COUNT=0
|
||||||
|
for ADDR in $WINDOWS; do
|
||||||
|
hyprctl dispatch movetoworkspace "$CURRENT_WS,address:$ADDR"
|
||||||
|
COUNT=$((COUNT + 1))
|
||||||
|
done
|
||||||
|
|
||||||
|
notify-send "Gather Class" "Gathered $COUNT windows of class '$FOCUSED_CLASS'"
|
||||||
33
dotfiles/config/hypr/scripts/go-to-window.sh
Executable file
33
dotfiles/config/hypr/scripts/go-to-window.sh
Executable file
@@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Go to a window selected via rofi (with icons from desktop entries).
|
||||||
|
# Replaces "rofi -show window" which doesn't work well on Wayland.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
source "$SCRIPT_DIR/window-icon-map.sh"
|
||||||
|
|
||||||
|
# Get all windows on regular workspaces as TSV
|
||||||
|
WINDOW_DATA=$(hyprctl clients -j | jq -r '
|
||||||
|
.[] | select(.workspace.id >= 0)
|
||||||
|
| [.address, .class, (.title | gsub("\t"; " ")), (.workspace.id | tostring)]
|
||||||
|
| @tsv')
|
||||||
|
|
||||||
|
[ -n "$WINDOW_DATA" ] || exit 0
|
||||||
|
|
||||||
|
addresses=()
|
||||||
|
TMPFILE=$(mktemp)
|
||||||
|
trap 'rm -f "$TMPFILE"' EXIT
|
||||||
|
|
||||||
|
while IFS=$'\t' read -r address class title ws_id; do
|
||||||
|
icon=$(icon_for_class "$class")
|
||||||
|
addresses+=("$address")
|
||||||
|
printf '%-24s %s WS:%s\0icon\x1f%s\n' \
|
||||||
|
"$class" "$title" "$ws_id" "$icon"
|
||||||
|
done <<< "$WINDOW_DATA" > "$TMPFILE"
|
||||||
|
|
||||||
|
INDEX=$(rofi -dmenu -i -show-icons -p "Go to window" -format i < "$TMPFILE") || exit 0
|
||||||
|
|
||||||
|
if [ -n "$INDEX" ] && [ -n "${addresses[$INDEX]:-}" ]; then
|
||||||
|
hyprctl dispatch focuswindow "address:${addresses[$INDEX]}"
|
||||||
|
fi
|
||||||
49
dotfiles/config/hypr/scripts/minimize-active.sh
Executable file
49
dotfiles/config/hypr/scripts/minimize-active.sh
Executable file
@@ -0,0 +1,49 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Minimize the active window by moving it to a special workspace without
|
||||||
|
# toggling that special workspace open.
|
||||||
|
#
|
||||||
|
# Usage: minimize-active.sh <name>
|
||||||
|
# Example: minimize-active.sh minimized
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
NAME="${1:-minimized}"
|
||||||
|
NAME="${NAME#special:}"
|
||||||
|
|
||||||
|
if ! command -v hyprctl >/dev/null 2>&1; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if ! command -v jq >/dev/null 2>&1; then
|
||||||
|
# We could parse plain output, but jq should exist in this setup; if it
|
||||||
|
# doesn't, fail soft.
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
ACTIVE_JSON="$(hyprctl -j activewindow 2>/dev/null || true)"
|
||||||
|
ADDR="$(printf '%s' "$ACTIVE_JSON" | jq -r '.address // empty')"
|
||||||
|
if [ -z "$ADDR" ] || [ "$ADDR" = "null" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If the minimized special workspace is currently visible, closing it after the
|
||||||
|
# move keeps the window hidden (what "minimize" usually means).
|
||||||
|
MONITOR_ID="$(printf '%s' "$ACTIVE_JSON" | jq -r '.monitor // empty')"
|
||||||
|
SPECIAL_OPEN="$(
|
||||||
|
hyprctl -j monitors 2>/dev/null \
|
||||||
|
| jq -r --arg n "special:$NAME" --argjson mid "${MONITOR_ID:-0}" '
|
||||||
|
.[]
|
||||||
|
| select(.id == $mid)
|
||||||
|
| (.specialWorkspace.name // "")
|
||||||
|
| select(. == $n)
|
||||||
|
' \
|
||||||
|
| head -n 1 \
|
||||||
|
|| true
|
||||||
|
)"
|
||||||
|
|
||||||
|
hyprctl dispatch movetoworkspacesilent "special:${NAME},address:${ADDR}" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
if [ -n "$SPECIAL_OPEN" ]; then
|
||||||
|
hyprctl dispatch togglespecialworkspace "$NAME" >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
39
dotfiles/config/hypr/scripts/minimized-cancel.sh
Executable file
39
dotfiles/config/hypr/scripts/minimized-cancel.sh
Executable file
@@ -0,0 +1,39 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Exit minimized picker mode:
|
||||||
|
# - Hide the minimized special workspace on the active monitor (if visible)
|
||||||
|
# - Reset the submap
|
||||||
|
#
|
||||||
|
# Usage: minimized-cancel.sh <name>
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
NAME="${1:-minimized}"
|
||||||
|
NAME="${NAME#special:}"
|
||||||
|
SPECIAL_WS="special:${NAME}"
|
||||||
|
|
||||||
|
if ! command -v hyprctl >/dev/null 2>&1; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if ! command -v jq >/dev/null 2>&1; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
MONITOR_ID="$(hyprctl -j activeworkspace 2>/dev/null | jq -r '.monitorID // empty' || true)"
|
||||||
|
if [ -z "$MONITOR_ID" ] || [ "$MONITOR_ID" = "null" ]; then
|
||||||
|
MONITOR_ID=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
OPEN="$(
|
||||||
|
hyprctl -j monitors 2>/dev/null \
|
||||||
|
| jq -r --argjson mid "$MONITOR_ID" '.[] | select(.id == $mid) | (.specialWorkspace.name // "")' \
|
||||||
|
| head -n 1 \
|
||||||
|
|| true
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [ "$OPEN" = "$SPECIAL_WS" ]; then
|
||||||
|
hyprctl dispatch togglespecialworkspace "$NAME" >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
hyprctl dispatch submap reset >/dev/null 2>&1 || true
|
||||||
|
exit 0
|
||||||
|
|
||||||
40
dotfiles/config/hypr/scripts/minimized-mode.sh
Executable file
40
dotfiles/config/hypr/scripts/minimized-mode.sh
Executable file
@@ -0,0 +1,40 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Enter a "picker" mode for minimized windows:
|
||||||
|
# - Ensure the minimized special workspace is visible on the active monitor
|
||||||
|
# - Switch Hyprland into a submap so Enter restores and Escape cancels
|
||||||
|
#
|
||||||
|
# Usage: minimized-mode.sh <name>
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
NAME="${1:-minimized}"
|
||||||
|
NAME="${NAME#special:}"
|
||||||
|
SPECIAL_WS="special:${NAME}"
|
||||||
|
|
||||||
|
if ! command -v hyprctl >/dev/null 2>&1; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if ! command -v jq >/dev/null 2>&1; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
MONITOR_ID="$(hyprctl -j activeworkspace 2>/dev/null | jq -r '.monitorID // empty' || true)"
|
||||||
|
if [ -z "$MONITOR_ID" ] || [ "$MONITOR_ID" = "null" ]; then
|
||||||
|
MONITOR_ID=0
|
||||||
|
fi
|
||||||
|
|
||||||
|
OPEN="$(
|
||||||
|
hyprctl -j monitors 2>/dev/null \
|
||||||
|
| jq -r --argjson mid "$MONITOR_ID" '.[] | select(.id == $mid) | (.specialWorkspace.name // "")' \
|
||||||
|
| head -n 1 \
|
||||||
|
|| true
|
||||||
|
)"
|
||||||
|
|
||||||
|
# Ensure it's visible (but don't toggle it off if already open).
|
||||||
|
if [ "$OPEN" != "$SPECIAL_WS" ]; then
|
||||||
|
hyprctl dispatch togglespecialworkspace "$NAME" >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
hyprctl dispatch submap minimized >/dev/null 2>&1 || true
|
||||||
|
exit 0
|
||||||
|
|
||||||
83
dotfiles/config/hypr/scripts/movewindow-follow-cursor.sh
Executable file
83
dotfiles/config/hypr/scripts/movewindow-follow-cursor.sh
Executable file
@@ -0,0 +1,83 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Move the active window in a direction and warp the cursor to keep its
|
||||||
|
# relative position inside the moved window.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
export PATH="/run/current-system/sw/bin:${PATH}"
|
||||||
|
|
||||||
|
if [[ $# -lt 1 ]]; then
|
||||||
|
echo "usage: $0 <dir> [mode]" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
dir="$1"
|
||||||
|
mode="${2:-}"
|
||||||
|
|
||||||
|
if ! command -v hyprctl >/dev/null; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
move_window() {
|
||||||
|
if [[ -n "$mode" ]]; then
|
||||||
|
hyprctl dispatch hy3:movewindow "$dir, $mode" >/dev/null 2>&1 || true
|
||||||
|
else
|
||||||
|
hyprctl dispatch hy3:movewindow "$dir" >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
win_json="$(hyprctl -j activewindow 2>/dev/null || true)"
|
||||||
|
cur_json="$(hyprctl -j cursorpos 2>/dev/null || true)"
|
||||||
|
|
||||||
|
if [[ -z "$win_json" || "$win_json" == "null" || -z "$cur_json" || "$cur_json" == "null" ]]; then
|
||||||
|
move_window
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
win_x="$(jq -er '.at[0]' <<<"$win_json" 2>/dev/null || true)"
|
||||||
|
win_y="$(jq -er '.at[1]' <<<"$win_json" 2>/dev/null || true)"
|
||||||
|
win_w="$(jq -er '.size[0]' <<<"$win_json" 2>/dev/null || true)"
|
||||||
|
win_h="$(jq -er '.size[1]' <<<"$win_json" 2>/dev/null || true)"
|
||||||
|
cur_x="$(jq -er '.x' <<<"$cur_json" 2>/dev/null || true)"
|
||||||
|
cur_y="$(jq -er '.y' <<<"$cur_json" 2>/dev/null || true)"
|
||||||
|
|
||||||
|
if [[ ! "$win_x" =~ ^-?[0-9]+$ || ! "$win_y" =~ ^-?[0-9]+$ || ! "$win_w" =~ ^-?[0-9]+$ || ! "$win_h" =~ ^-?[0-9]+$ || ! "$cur_x" =~ ^-?[0-9]+$ || ! "$cur_y" =~ ^-?[0-9]+$ ]]; then
|
||||||
|
move_window
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
rel_x=$((cur_x - win_x))
|
||||||
|
rel_y=$((cur_y - win_y))
|
||||||
|
|
||||||
|
move_window
|
||||||
|
|
||||||
|
win_json="$(hyprctl -j activewindow 2>/dev/null || true)"
|
||||||
|
if [[ -z "$win_json" || "$win_json" == "null" ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
win_x="$(jq -er '.at[0]' <<<"$win_json" 2>/dev/null || true)"
|
||||||
|
win_y="$(jq -er '.at[1]' <<<"$win_json" 2>/dev/null || true)"
|
||||||
|
win_w="$(jq -er '.size[0]' <<<"$win_json" 2>/dev/null || true)"
|
||||||
|
win_h="$(jq -er '.size[1]' <<<"$win_json" 2>/dev/null || true)"
|
||||||
|
|
||||||
|
if [[ ! "$win_x" =~ ^-?[0-9]+$ || ! "$win_y" =~ ^-?[0-9]+$ || ! "$win_w" =~ ^-?[0-9]+$ || ! "$win_h" =~ ^-?[0-9]+$ ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ((rel_x < 0)); then
|
||||||
|
rel_x=0
|
||||||
|
elif ((rel_x > win_w)); then
|
||||||
|
rel_x=$win_w
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ((rel_y < 0)); then
|
||||||
|
rel_y=0
|
||||||
|
elif ((rel_y > win_h)); then
|
||||||
|
rel_y=$win_h
|
||||||
|
fi
|
||||||
|
|
||||||
|
new_x=$((win_x + rel_x))
|
||||||
|
new_y=$((win_y + rel_y))
|
||||||
|
|
||||||
|
hyprctl dispatch movecursor "$new_x" "$new_y" >/dev/null 2>&1 || true
|
||||||
19
dotfiles/config/hypr/scripts/raise-or-run.sh
Executable file
19
dotfiles/config/hypr/scripts/raise-or-run.sh
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Raise existing window or run command (like XMonad's raiseNextMaybe)
|
||||||
|
# Usage: raise-or-run.sh <class-pattern> <command>
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
CLASS_PATTERN="$1"
|
||||||
|
COMMAND="$2"
|
||||||
|
|
||||||
|
# Find windows matching the class pattern
|
||||||
|
MATCHING=$(hyprctl clients -j | jq -r ".[] | select(.class | test(\"$CLASS_PATTERN\"; \"i\")) | .address" | head -1)
|
||||||
|
|
||||||
|
if [ -n "$MATCHING" ]; then
|
||||||
|
# Window exists, focus it
|
||||||
|
hyprctl dispatch focuswindow "address:$MATCHING"
|
||||||
|
else
|
||||||
|
# No matching window, run the command
|
||||||
|
exec $COMMAND
|
||||||
|
fi
|
||||||
43
dotfiles/config/hypr/scripts/replace-window.sh
Executable file
43
dotfiles/config/hypr/scripts/replace-window.sh
Executable file
@@ -0,0 +1,43 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Replace focused window with selected window (like XMonad's myReplaceWindow)
|
||||||
|
# Swaps the positions of focused window and selected window
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
source "$SCRIPT_DIR/window-icon-map.sh"
|
||||||
|
|
||||||
|
FOCUSED=$(hyprctl activewindow -j | jq -r '.address')
|
||||||
|
|
||||||
|
if [ "$FOCUSED" = "null" ] || [ -z "$FOCUSED" ]; then
|
||||||
|
notify-send "Replace Window" "No focused window"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get all windows except focused as TSV
|
||||||
|
WINDOW_DATA=$(hyprctl clients -j | jq -r --arg focused "$FOCUSED" '
|
||||||
|
.[] | select(.workspace.id >= 0 and .address != $focused)
|
||||||
|
| [.address, .class, (.title | gsub("\t"; " ")), (.workspace.id | tostring)]
|
||||||
|
| @tsv')
|
||||||
|
|
||||||
|
if [ -z "$WINDOW_DATA" ]; then
|
||||||
|
notify-send "Replace Window" "No other windows available"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
addresses=()
|
||||||
|
TMPFILE=$(mktemp)
|
||||||
|
trap 'rm -f "$TMPFILE"' EXIT
|
||||||
|
|
||||||
|
while IFS=$'\t' read -r address class title ws_id; do
|
||||||
|
icon=$(icon_for_class "$class")
|
||||||
|
addresses+=("$address")
|
||||||
|
printf '%-24s %s WS:%s\0icon\x1f%s\n' \
|
||||||
|
"$class" "$title" "$ws_id" "$icon"
|
||||||
|
done <<< "$WINDOW_DATA" > "$TMPFILE"
|
||||||
|
|
||||||
|
INDEX=$(rofi -dmenu -i -show-icons -p "Replace with" -format i < "$TMPFILE") || exit 0
|
||||||
|
|
||||||
|
if [ -n "$INDEX" ] && [ -n "${addresses[$INDEX]:-}" ]; then
|
||||||
|
hyprctl dispatch hy3:movewindow "address:${addresses[$INDEX]}"
|
||||||
|
fi
|
||||||
43
dotfiles/config/hypr/scripts/shift-to-empty-on-screen.sh
Executable file
43
dotfiles/config/hypr/scripts/shift-to-empty-on-screen.sh
Executable file
@@ -0,0 +1,43 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Shift window to empty workspace on screen in given direction
|
||||||
|
# Like XMonad's shiftToEmptyOnScreen
|
||||||
|
# Usage: shift-to-empty-on-screen.sh <direction: u|d|l|r>
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
DIRECTION="$1"
|
||||||
|
max_ws="${HYPR_MAX_WORKSPACE:-9}"
|
||||||
|
|
||||||
|
# Track the current monitor so we can return
|
||||||
|
ORIG_MONITOR=$(hyprctl activeworkspace -j | jq -r '.monitor')
|
||||||
|
|
||||||
|
# Move focus to the screen in that direction
|
||||||
|
hyprctl dispatch focusmonitor "$DIRECTION"
|
||||||
|
|
||||||
|
# Get the monitor we're now on (target monitor)
|
||||||
|
MONITOR=$(hyprctl activeworkspace -j | jq -r '.monitor')
|
||||||
|
|
||||||
|
# If there is no monitor in that direction, bail
|
||||||
|
if [ "$MONITOR" = "$ORIG_MONITOR" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Find an empty workspace within 1..$HYPR_MAX_WORKSPACE.
|
||||||
|
EMPTY_WS="$(~/.config/hypr/scripts/find-empty-workspace.sh "${MONITOR}" 2>/dev/null || true)"
|
||||||
|
if [[ -z "${EMPTY_WS}" ]]; then
|
||||||
|
# No empty workspace available within the cap; restore focus and bail.
|
||||||
|
hyprctl dispatch focusmonitor "$ORIG_MONITOR"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( EMPTY_WS < 1 || EMPTY_WS > max_ws )); then
|
||||||
|
hyprctl dispatch focusmonitor "$ORIG_MONITOR"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Ensure the workspace exists on the target monitor
|
||||||
|
hyprctl dispatch workspace "$EMPTY_WS"
|
||||||
|
|
||||||
|
# Go back to original monitor and move the window (without following)
|
||||||
|
hyprctl dispatch focusmonitor "$ORIG_MONITOR"
|
||||||
|
hyprctl dispatch movetoworkspacesilent "$EMPTY_WS"
|
||||||
52
dotfiles/config/hypr/scripts/swap-workspaces.sh
Executable file
52
dotfiles/config/hypr/scripts/swap-workspaces.sh
Executable file
@@ -0,0 +1,52 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Swap the contents of the current workspace with another workspace.
|
||||||
|
# Intended to mirror XMonad's swapWithCurrent behavior.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
max_ws="${HYPR_MAX_WORKSPACE:-9}"
|
||||||
|
|
||||||
|
CURRENT_WS="$(hyprctl activeworkspace -j | jq -r '.id')"
|
||||||
|
if [[ -z "${CURRENT_WS}" || "${CURRENT_WS}" == "null" ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
TARGET_WS="${1:-}"
|
||||||
|
|
||||||
|
if [[ -z "${TARGET_WS}" ]]; then
|
||||||
|
WS_LIST="$({
|
||||||
|
seq 1 "${max_ws}"
|
||||||
|
hyprctl workspaces -j | jq -r '.[].id' 2>/dev/null || true
|
||||||
|
} | awk 'NF {print $1}' | awk '!seen[$0]++' | sort -n)"
|
||||||
|
|
||||||
|
TARGET_WS="$(printf "%s\n" "${WS_LIST}" | rofi -dmenu -p "Swap with workspace")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z "${TARGET_WS}" || "${TARGET_WS}" == "null" ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${TARGET_WS}" == "${CURRENT_WS}" ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [[ "${TARGET_WS}" =~ ^-?[0-9]+$ ]]; then
|
||||||
|
notify-send "Swap Workspace" "Invalid workspace: ${TARGET_WS}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( TARGET_WS < 1 || TARGET_WS > max_ws )); then
|
||||||
|
notify-send "Swap Workspace" "Workspace out of range (1-${max_ws}): ${TARGET_WS}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
WINDOWS_CURRENT="$(hyprctl clients -j | jq -r --arg ws "${CURRENT_WS}" '.[] | select((.workspace.id|tostring) == $ws) | .address')"
|
||||||
|
WINDOWS_TARGET="$(hyprctl clients -j | jq -r --arg ws "${TARGET_WS}" '.[] | select((.workspace.id|tostring) == $ws) | .address')"
|
||||||
|
|
||||||
|
for ADDR in ${WINDOWS_CURRENT}; do
|
||||||
|
hyprctl dispatch movetoworkspace "${TARGET_WS},address:${ADDR}"
|
||||||
|
done
|
||||||
|
|
||||||
|
for ADDR in ${WINDOWS_TARGET}; do
|
||||||
|
hyprctl dispatch movetoworkspace "${CURRENT_WS},address:${ADDR}"
|
||||||
|
done
|
||||||
51
dotfiles/config/hypr/scripts/toggle-scratchpad.sh
Executable file
51
dotfiles/config/hypr/scripts/toggle-scratchpad.sh
Executable file
@@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Toggle a named Hyprland scratchpad, spawning it if needed.
|
||||||
|
# Usage: toggle-scratchpad.sh <name> <class_regex|-> <title_regex|-> <command...>
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [ "$#" -lt 4 ]; then
|
||||||
|
echo "usage: $0 <name> <class_regex|-> <title_regex|-> <command...>" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
NAME="$1"
|
||||||
|
shift
|
||||||
|
CLASS_REGEX="$1"
|
||||||
|
shift
|
||||||
|
TITLE_REGEX="$1"
|
||||||
|
shift
|
||||||
|
COMMAND=("$@")
|
||||||
|
|
||||||
|
if [ "$CLASS_REGEX" = "-" ]; then
|
||||||
|
CLASS_REGEX=""
|
||||||
|
fi
|
||||||
|
if [ "$TITLE_REGEX" = "-" ]; then
|
||||||
|
TITLE_REGEX=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$CLASS_REGEX" ] && [ -z "$TITLE_REGEX" ]; then
|
||||||
|
echo "toggle-scratchpad: provide a class or title regex" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
MATCHING=$(hyprctl clients -j | jq -r --arg cre "$CLASS_REGEX" --arg tre "$TITLE_REGEX" '
|
||||||
|
.[]
|
||||||
|
| select(
|
||||||
|
(($cre == "") or (.class | test($cre; "i")))
|
||||||
|
and
|
||||||
|
(($tre == "") or (.title | test($tre; "i")))
|
||||||
|
)
|
||||||
|
| .address
|
||||||
|
')
|
||||||
|
|
||||||
|
if [ -z "$MATCHING" ]; then
|
||||||
|
"${COMMAND[@]}" &
|
||||||
|
else
|
||||||
|
while IFS= read -r ADDR; do
|
||||||
|
[ -n "$ADDR" ] || continue
|
||||||
|
hyprctl dispatch movetoworkspacesilent "special:$NAME,address:$ADDR"
|
||||||
|
done <<< "$MATCHING"
|
||||||
|
fi
|
||||||
|
|
||||||
|
hyprctl dispatch togglespecialworkspace "$NAME"
|
||||||
86
dotfiles/config/hypr/scripts/unminimize-last.sh
Executable file
86
dotfiles/config/hypr/scripts/unminimize-last.sh
Executable file
@@ -0,0 +1,86 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Restore a minimized window by moving it out of a special workspace.
|
||||||
|
#
|
||||||
|
# Usage: unminimize-last.sh <name>
|
||||||
|
# Example: unminimize-last.sh minimized
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
NAME="${1:-minimized}"
|
||||||
|
NAME="${NAME#special:}"
|
||||||
|
SPECIAL_WS="special:${NAME}"
|
||||||
|
|
||||||
|
if ! command -v hyprctl >/dev/null 2>&1; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if ! command -v jq >/dev/null 2>&1; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
ACTIVE_JSON="$(hyprctl -j activewindow 2>/dev/null || true)"
|
||||||
|
ACTIVE_ADDR="$(printf '%s' "$ACTIVE_JSON" | jq -r '.address // empty')"
|
||||||
|
ACTIVE_WS="$(printf '%s' "$ACTIVE_JSON" | jq -r '.workspace.name // empty')"
|
||||||
|
MONITOR_ID="$(printf '%s' "$ACTIVE_JSON" | jq -r '.monitor // empty')"
|
||||||
|
|
||||||
|
# Destination is the normal active workspace for the active monitor.
|
||||||
|
DEST_WS="$(
|
||||||
|
hyprctl -j monitors 2>/dev/null \
|
||||||
|
| jq -r --argjson mid "${MONITOR_ID:-0}" '.[] | select(.id == $mid) | .activeWorkspace.name' \
|
||||||
|
| head -n 1 \
|
||||||
|
|| true
|
||||||
|
)"
|
||||||
|
if [ -z "$DEST_WS" ] || [ "$DEST_WS" = "null" ]; then
|
||||||
|
DEST_WS="$(hyprctl -j activeworkspace 2>/dev/null | jq -r '.name // empty' || true)"
|
||||||
|
fi
|
||||||
|
if [ -z "$DEST_WS" ] || [ "$DEST_WS" = "null" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If we're focused on a minimized window already, restore that one.
|
||||||
|
ADDR=""
|
||||||
|
if [ "$ACTIVE_WS" = "$SPECIAL_WS" ] && [ -n "$ACTIVE_ADDR" ] && [ "$ACTIVE_ADDR" != "null" ]; then
|
||||||
|
ADDR="$ACTIVE_ADDR"
|
||||||
|
else
|
||||||
|
# Otherwise, restore the "most recent" minimized window we can find.
|
||||||
|
# focusHistoryID tends to have 0 as most recent; pick the smallest value.
|
||||||
|
ADDR="$(
|
||||||
|
hyprctl -j clients 2>/dev/null \
|
||||||
|
| jq -r --arg sw "$SPECIAL_WS" '
|
||||||
|
[ .[]
|
||||||
|
| select(.workspace.name == $sw)
|
||||||
|
| { addr: .address, fh: (.focusHistoryID // 999999999) }
|
||||||
|
]
|
||||||
|
| sort_by(.fh)
|
||||||
|
| (.[0].addr // empty)
|
||||||
|
' \
|
||||||
|
| head -n 1 \
|
||||||
|
|| true
|
||||||
|
)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$ADDR" ] || [ "$ADDR" = "null" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
hyprctl dispatch movetoworkspacesilent "${DEST_WS},address:${ADDR}" >/dev/null 2>&1 || true
|
||||||
|
hyprctl dispatch focuswindow "address:${ADDR}" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
# If the minimized special workspace is currently visible, close it so we don't
|
||||||
|
# leave things in a special state after a restore.
|
||||||
|
SPECIAL_OPEN="$(
|
||||||
|
hyprctl -j monitors 2>/dev/null \
|
||||||
|
| jq -r --arg n "$SPECIAL_WS" --argjson mid "${MONITOR_ID:-0}" '
|
||||||
|
.[]
|
||||||
|
| select(.id == $mid)
|
||||||
|
| (.specialWorkspace.name // "")
|
||||||
|
| select(. == $n)
|
||||||
|
' \
|
||||||
|
| head -n 1 \
|
||||||
|
|| true
|
||||||
|
)"
|
||||||
|
if [ -n "$SPECIAL_OPEN" ]; then
|
||||||
|
hyprctl dispatch togglespecialworkspace "$NAME" >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
exit 0
|
||||||
|
|
||||||
66
dotfiles/config/hypr/scripts/window-icon-map.sh
Executable file
66
dotfiles/config/hypr/scripts/window-icon-map.sh
Executable file
@@ -0,0 +1,66 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Source this file to get icon_for_class function.
|
||||||
|
# Builds a mapping from window class → freedesktop icon name
|
||||||
|
# by scanning .desktop files for StartupWMClass and Icon fields.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# source "$(dirname "$0")/window-icon-map.sh"
|
||||||
|
# icon=$(icon_for_class "google-chrome")
|
||||||
|
|
||||||
|
declare -A _WINDOW_ICON_MAP
|
||||||
|
|
||||||
|
_build_window_icon_map() {
|
||||||
|
local IFS=':'
|
||||||
|
local -a search_dirs=()
|
||||||
|
local dir
|
||||||
|
|
||||||
|
for dir in ${XDG_DATA_DIRS:-/run/current-system/sw/share:/usr/share:/usr/local/share}; do
|
||||||
|
[ -d "$dir/applications" ] && search_dirs+=("$dir/applications")
|
||||||
|
done
|
||||||
|
[ -d "$HOME/.local/share/applications" ] && search_dirs+=("$HOME/.local/share/applications")
|
||||||
|
[ ${#search_dirs[@]} -eq 0 ] && return
|
||||||
|
|
||||||
|
# Expand globs per-directory so the pattern works correctly
|
||||||
|
local -a desktop_files=()
|
||||||
|
for dir in "${search_dirs[@]}"; do
|
||||||
|
desktop_files+=("$dir"/*.desktop)
|
||||||
|
done
|
||||||
|
[ ${#desktop_files[@]} -eq 0 ] && return
|
||||||
|
|
||||||
|
# Single grep pass across all desktop files
|
||||||
|
local -A file_icons file_wmclass
|
||||||
|
local filepath line
|
||||||
|
while IFS=: read -r filepath line; do
|
||||||
|
case "$line" in
|
||||||
|
Icon=*)
|
||||||
|
[ -z "${file_icons[$filepath]:-}" ] && file_icons["$filepath"]="${line#Icon=}"
|
||||||
|
;;
|
||||||
|
StartupWMClass=*)
|
||||||
|
[ -z "${file_wmclass[$filepath]:-}" ] && file_wmclass["$filepath"]="${line#StartupWMClass=}"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done < <(grep -H '^Icon=\|^StartupWMClass=' "${desktop_files[@]}" 2>/dev/null)
|
||||||
|
|
||||||
|
# Build class → icon map
|
||||||
|
local icon wm_class bn name
|
||||||
|
for filepath in "${!file_icons[@]}"; do
|
||||||
|
icon="${file_icons[$filepath]}"
|
||||||
|
[ -n "$icon" ] || continue
|
||||||
|
|
||||||
|
wm_class="${file_wmclass[$filepath]:-}"
|
||||||
|
if [ -n "$wm_class" ]; then
|
||||||
|
_WINDOW_ICON_MAP["${wm_class,,}"]="$icon"
|
||||||
|
fi
|
||||||
|
|
||||||
|
bn="${filepath##*/}"
|
||||||
|
name="${bn%.desktop}"
|
||||||
|
_WINDOW_ICON_MAP["${name,,}"]="$icon"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
_build_window_icon_map
|
||||||
|
|
||||||
|
icon_for_class() {
|
||||||
|
local class_lower="${1,,}"
|
||||||
|
echo "${_WINDOW_ICON_MAP[$class_lower]:-$class_lower}"
|
||||||
|
}
|
||||||
16
dotfiles/config/hypr/scripts/workspace-goto-empty.sh
Executable file
16
dotfiles/config/hypr/scripts/workspace-goto-empty.sh
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cur_ws="$(hyprctl activeworkspace -j | jq -r '.id' 2>/dev/null || true)"
|
||||||
|
monitor="$(hyprctl activeworkspace -j | jq -r '.monitor' 2>/dev/null || true)"
|
||||||
|
|
||||||
|
ws="$(
|
||||||
|
~/.config/hypr/scripts/find-empty-workspace.sh "${monitor}" "${cur_ws}" 2>/dev/null || true
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ -z "${ws}" ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
hyprctl dispatch workspace "${ws}" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
16
dotfiles/config/hypr/scripts/workspace-move-to-empty.sh
Executable file
16
dotfiles/config/hypr/scripts/workspace-move-to-empty.sh
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cur_ws="$(hyprctl activeworkspace -j | jq -r '.id' 2>/dev/null || true)"
|
||||||
|
monitor="$(hyprctl activeworkspace -j | jq -r '.monitor' 2>/dev/null || true)"
|
||||||
|
|
||||||
|
ws="$(
|
||||||
|
~/.config/hypr/scripts/find-empty-workspace.sh "${monitor}" "${cur_ws}" 2>/dev/null || true
|
||||||
|
)"
|
||||||
|
|
||||||
|
if [[ -z "${ws}" ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
hyprctl dispatch movetoworkspace "${ws}" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
42
dotfiles/config/hypr/scripts/workspace-scroll.sh
Executable file
42
dotfiles/config/hypr/scripts/workspace-scroll.sh
Executable file
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
max_ws="${HYPR_MAX_WORKSPACE:-9}"
|
||||||
|
delta="${1:-}"
|
||||||
|
|
||||||
|
case "${delta}" in
|
||||||
|
+1|-1) ;;
|
||||||
|
next) delta="+1" ;;
|
||||||
|
prev) delta="-1" ;;
|
||||||
|
*)
|
||||||
|
exit 2
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
cur="$(hyprctl activeworkspace -j | jq -r '.id' 2>/dev/null || true)"
|
||||||
|
if ! [[ "${cur}" =~ ^[0-9]+$ ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( cur < 1 )); then
|
||||||
|
cur=1
|
||||||
|
elif (( cur > max_ws )); then
|
||||||
|
cur="${max_ws}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${delta}" == "+1" ]]; then
|
||||||
|
if (( cur >= max_ws )); then
|
||||||
|
nxt=1
|
||||||
|
else
|
||||||
|
nxt=$((cur + 1))
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if (( cur <= 1 )); then
|
||||||
|
nxt="${max_ws}"
|
||||||
|
else
|
||||||
|
nxt=$((cur - 1))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
hyprctl dispatch workspace "${nxt}" >/dev/null 2>&1 || true
|
||||||
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
default {
|
|
||||||
shader /run/current-system/sw/share/neowall/shaders/train_journey_optimized.glsl
|
|
||||||
shader_speed 0.7
|
|
||||||
shader_fps 30
|
|
||||||
mode fill
|
|
||||||
duration 0
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
default {
|
|
||||||
shader /run/current-system/sw/share/neowall/shaders/matrix_rain.glsl
|
|
||||||
shader_speed 0.85
|
|
||||||
shader_fps 30
|
|
||||||
mode fill
|
|
||||||
duration 0
|
|
||||||
}
|
|
||||||
@@ -1,780 +0,0 @@
|
|||||||
{-# LANGUAGE FlexibleContexts #-}
|
|
||||||
{-# LANGUAGE FlexibleInstances #-}
|
|
||||||
{-# LANGUAGE MultiParamTypeClasses #-}
|
|
||||||
{-# LANGUAGE NamedFieldPuns #-}
|
|
||||||
{-# LANGUAGE RecordWildCards #-}
|
|
||||||
{-# LANGUAGE DeriveDataTypeable #-}
|
|
||||||
|
|
||||||
module Main where
|
|
||||||
|
|
||||||
import Control.Concurrent (forkIO)
|
|
||||||
import Data.Bits ((.&.), complement)
|
|
||||||
import Data.Char (toLower)
|
|
||||||
import Data.Function (on)
|
|
||||||
import Data.List (find, foldl', isInfixOf, isPrefixOf, minimumBy)
|
|
||||||
import qualified Data.Map.Strict as M
|
|
||||||
import Data.Maybe (fromMaybe, mapMaybe)
|
|
||||||
import Data.Typeable (Typeable)
|
|
||||||
import Data.Word (Word32)
|
|
||||||
import Graphics.X11.ExtraTypes.XF86
|
|
||||||
import System.Exit (ExitCode(..))
|
|
||||||
import System.IO (hFlush, stdout)
|
|
||||||
import System.Process (readCreateProcessWithExitCode, shell, spawnCommand, waitForProcess)
|
|
||||||
import XMonad
|
|
||||||
import qualified XMonad.Layout.Renamed as RN
|
|
||||||
import XMonad.River.WindowManager
|
|
||||||
import XMonad.River.WindowManager.Wayland
|
|
||||||
import qualified XMonad.StackSet as W
|
|
||||||
|
|
||||||
data Direction = DirectionUp | DirectionDown | DirectionLeft | DirectionRight
|
|
||||||
deriving (Eq, Show)
|
|
||||||
|
|
||||||
data EqualColumns a = EqualColumns
|
|
||||||
deriving (Read, Show, Typeable)
|
|
||||||
|
|
||||||
instance LayoutClass EqualColumns a where
|
|
||||||
description _ = "Columns"
|
|
||||||
pureLayout _ rect stack =
|
|
||||||
zip windows (equalColumnRects rect (length windows))
|
|
||||||
where
|
|
||||||
windows = W.integrate stack
|
|
||||||
|
|
||||||
main :: IO ()
|
|
||||||
main = do
|
|
||||||
let bindings = keyBindings
|
|
||||||
configLog $ "starting imalison-river-xmonad with keybindings=" ++ show (length bindings)
|
|
||||||
initialState <- initialRiverWMState riverConfig
|
|
||||||
runRiverWMWaylandConfig
|
|
||||||
RiverWMWaylandConfig
|
|
||||||
{ riverWMWaylandInitialState = initialState
|
|
||||||
, riverWMWaylandKeyBindings = bindings
|
|
||||||
}
|
|
||||||
|
|
||||||
riverLayouts =
|
|
||||||
renamed "Columns" EqualColumns
|
|
||||||
||| Full
|
|
||||||
where
|
|
||||||
renamed name = RN.renamed [RN.Replace name]
|
|
||||||
|
|
||||||
riverConfig =
|
|
||||||
(defaultRiverWMConfig riverLayouts)
|
|
||||||
{ riverWMWorkspaces = ordinaryWorkspaces ++ specialWorkspaces
|
|
||||||
, riverWMMouseFollowsFocus = True
|
|
||||||
, riverWMBorderWidth = 2
|
|
||||||
, riverWMFocusedBorderColor = rgba8 0xed 0xb4 0x43 0xee
|
|
||||||
, riverWMUnfocusedBorderColor = rgba8 0x59 0x59 0x59 0xaa
|
|
||||||
}
|
|
||||||
|
|
||||||
rgba8 :: Word32 -> Word32 -> Word32 -> Word32 -> RiverWMColor
|
|
||||||
rgba8 red green blue alpha =
|
|
||||||
RiverWMColor (wide red) (wide green) (wide blue) (wide alpha)
|
|
||||||
where
|
|
||||||
wide component = component * 0x01010101
|
|
||||||
|
|
||||||
keyBindings
|
|
||||||
:: (LayoutClass l Window, Read (l Window))
|
|
||||||
=> [RiverWMWaylandKeyBinding l]
|
|
||||||
keyBindings =
|
|
||||||
addHyperChordBindings hyper hyperChord $
|
|
||||||
concat
|
|
||||||
[ directionalBindings super directionalFocus
|
|
||||||
, directionalBindings (super .|. shift) directionalSwap
|
|
||||||
, directionalBindings (super .|. ctrl) (shiftFocusedToDirectionalScreen False)
|
|
||||||
, directionalBindings (super .|. ctrl .|. shift) shiftFocusedToEmptyWorkspaceOnDirectionalScreen
|
|
||||||
, directionalBindings hyper focusDirectionalScreen
|
|
||||||
, directionalBindings (hyper .|. shift) (shiftFocusedToDirectionalScreen True)
|
|
||||||
, workspaceBindings
|
|
||||||
, layoutBindings
|
|
||||||
, spawnBindings
|
|
||||||
, mediaBindings
|
|
||||||
]
|
|
||||||
|
|
||||||
directionalBindings
|
|
||||||
:: RiverWMWaylandModifiers
|
|
||||||
-> (Direction -> RiverWMWaylandAction l)
|
|
||||||
-> [RiverWMWaylandKeyBinding l]
|
|
||||||
directionalBindings mods command =
|
|
||||||
[ key mods xK_w (command DirectionUp)
|
|
||||||
, key mods xK_s (command DirectionDown)
|
|
||||||
, key mods xK_a (command DirectionLeft)
|
|
||||||
, key mods xK_d (command DirectionRight)
|
|
||||||
]
|
|
||||||
|
|
||||||
workspaceBindings
|
|
||||||
:: [RiverWMWaylandKeyBinding l]
|
|
||||||
workspaceBindings =
|
|
||||||
[ key (mods .|. super) keysym (action $ command workspace)
|
|
||||||
| (workspace, keysym) <- zip (map show [(1 :: Int) .. 9]) [xK_1 .. xK_9]
|
|
||||||
, (command, mods, action) <-
|
|
||||||
[ (W.greedyView, noMods, stackAction)
|
|
||||||
, (W.shift, shift, stackAction)
|
|
||||||
, (\workspaceId stackSet -> W.greedyView workspaceId (W.shift workspaceId stackSet), ctrl, stackActionWarpPointer)
|
|
||||||
]
|
|
||||||
]
|
|
||||||
|
|
||||||
layoutBindings
|
|
||||||
:: (LayoutClass l Window, Read (l Window))
|
|
||||||
=> [RiverWMWaylandKeyBinding l]
|
|
||||||
layoutBindings =
|
|
||||||
[ key super xK_space (layoutAction NextLayout)
|
|
||||||
, key (super .|. shift) xK_space (layoutAction (JumpToLayout "Columns"))
|
|
||||||
, key (super .|. ctrl) xK_space (layoutAction (JumpToLayout "Full"))
|
|
||||||
, key super xK_bracketleft (layoutAction Shrink)
|
|
||||||
, key super xK_bracketright (layoutAction Expand)
|
|
||||||
, key super xK_comma (layoutAction (IncMasterN 1))
|
|
||||||
, key super xK_period (layoutAction (IncMasterN (-1)))
|
|
||||||
]
|
|
||||||
|
|
||||||
spawnBindings
|
|
||||||
:: [RiverWMWaylandKeyBinding l]
|
|
||||||
spawnBindings =
|
|
||||||
[ key super xK_Return (spawnAction "ghostty --gtk-single-instance=false")
|
|
||||||
, key (super .|. shift) xK_Return (spawnAction "ghostty --gtk-single-instance=false")
|
|
||||||
, key super xK_p (spawnAction "rofi -show drun -show-icons")
|
|
||||||
, key (super .|. shift) xK_p (spawnAction "rofi -show run")
|
|
||||||
, key super xK_Tab (selectWindowAction "windows" focusSelectedWindow)
|
|
||||||
, key super xK_g (selectWindowAction "go to window" focusSelectedWindow)
|
|
||||||
, key super xK_b (selectWindowAction "bring window" bringSelectedWindow)
|
|
||||||
, key (super .|. shift) xK_b (selectWindowAction "replace window" replaceSelectedWindow)
|
|
||||||
, key super xK_m minimizeFocusedWindow
|
|
||||||
, key (super .|. shift) xK_m restoreLastMinimizedWindow
|
|
||||||
, key super xK_q (spawnAction "river-xmonad-restart")
|
|
||||||
, key (super .|. shift) xK_c closeFocusedWindow
|
|
||||||
, key (super .|. shift) xK_q (spawnAction "riverctl exit")
|
|
||||||
, key (super .|. alt) xK_e (toggleScratchpad "element")
|
|
||||||
, key (super .|. alt) xK_h (toggleScratchpad "htop")
|
|
||||||
, key (super .|. alt) xK_k (toggleScratchpad "slack")
|
|
||||||
, key (super .|. alt) xK_s (toggleScratchpad "spotify")
|
|
||||||
, key (super .|. alt) xK_t (toggleScratchpad "transmission")
|
|
||||||
, key (super .|. alt) xK_v (toggleScratchpad "volume")
|
|
||||||
, key (super .|. alt) xK_c (spawnAction "google-chrome-stable")
|
|
||||||
, key super xK_e (spawnAction "emacsclient --eval '(emacs-everywhere)'")
|
|
||||||
, key (super .|. ctrl) xK_e (shiftFocusedToNextEmptyWorkspace False)
|
|
||||||
, key (super .|. shift) xK_e (shiftFocusedToNextEmptyWorkspace True)
|
|
||||||
, key super xK_v (spawnAction "wl-paste | wtype -")
|
|
||||||
, key super xK_x (spawnAction "rofi_command.sh")
|
|
||||||
, key hyper xK_e viewNextEmptyWorkspace
|
|
||||||
, key hyper xK_v (spawnAction "rofi -modi 'clipboard:greenclip print' -show clipboard")
|
|
||||||
, key hyper xK_p (spawnAction "rofi-pass")
|
|
||||||
, key noMods xK_Print (spawnAction "flameshot gui")
|
|
||||||
, key hyper xK_h (spawnAction "flameshot gui")
|
|
||||||
, key hyper xK_c (spawnAction "shell_command.sh")
|
|
||||||
, key hyper xK_g gatherFocusedAppId
|
|
||||||
, key (hyper .|. shift) xK_l (spawnAction "loginctl lock-session")
|
|
||||||
, key hyper xK_k (spawnAction "rofi_kill_process.sh")
|
|
||||||
, key (hyper .|. shift) xK_k (spawnAction "rofi_kill_all.sh")
|
|
||||||
, key hyper xK_r (spawnAction "rofi_systemd_mono")
|
|
||||||
, key hyper xK_9 (spawnAction "start_synergy.sh")
|
|
||||||
, key hyper xK_backslash (spawnAction "$HOME/dotfiles/dotfiles/lib/functions/mpg341cx_input toggle")
|
|
||||||
, key hyper xK_i (spawnAction "rofi_select_input.hs")
|
|
||||||
, key hyper xK_o (spawnAction "rofi_paswitch")
|
|
||||||
, key hyper xK_comma (spawnAction "rofi_wallpaper.sh")
|
|
||||||
, key hyper xK_slash (spawnAction "toggle_taffybar")
|
|
||||||
, key hyper xK_y (spawnAction "rofi_agentic_skill")
|
|
||||||
]
|
|
||||||
|
|
||||||
mediaBindings
|
|
||||||
:: [RiverWMWaylandKeyBinding l]
|
|
||||||
mediaBindings =
|
|
||||||
[ key super xK_semicolon (spawnAction "playerctl play-pause")
|
|
||||||
, key noMods xF86XK_AudioPause (spawnAction "playerctl play-pause")
|
|
||||||
, key noMods xF86XK_AudioPlay (spawnAction "playerctl play-pause")
|
|
||||||
, key super xK_l (spawnAction "playerctl next")
|
|
||||||
, key noMods xF86XK_AudioNext (spawnAction "playerctl next")
|
|
||||||
, key super xK_j (spawnAction "playerctl previous")
|
|
||||||
, key noMods xF86XK_AudioPrev (spawnAction "playerctl previous")
|
|
||||||
, key noMods xF86XK_AudioRaiseVolume (spawnAction "set_volume --unmute --change-volume +5")
|
|
||||||
, key noMods xF86XK_AudioLowerVolume (spawnAction "set_volume --unmute --change-volume -5")
|
|
||||||
, key noMods xF86XK_AudioMute (spawnAction "set_volume --toggle-mute")
|
|
||||||
, key super xK_i (spawnAction "set_volume --unmute --change-volume +5")
|
|
||||||
, key super xK_k (spawnAction "set_volume --unmute --change-volume -5")
|
|
||||||
, key super xK_u (spawnAction "set_volume --toggle-mute")
|
|
||||||
, key (hyper .|. shift) xK_q (spawnAction "toggle_mute_current_window.sh")
|
|
||||||
, key (hyper .|. ctrl) xK_q (spawnAction "toggle_mute_current_window.sh only")
|
|
||||||
, key noMods xF86XK_MonBrightnessUp (spawnAction "brightness.sh up")
|
|
||||||
, key noMods xF86XK_MonBrightnessDown (spawnAction "brightness.sh down")
|
|
||||||
]
|
|
||||||
|
|
||||||
key
|
|
||||||
:: RiverWMWaylandModifiers
|
|
||||||
-> KeySym
|
|
||||||
-> RiverWMWaylandAction l
|
|
||||||
-> RiverWMWaylandKeyBinding l
|
|
||||||
key modifiers keysym action =
|
|
||||||
RiverWMWaylandKeyBinding
|
|
||||||
{ riverWMWaylandKeyModifiers = modifiers
|
|
||||||
, riverWMWaylandKeyKeysym = fromIntegral keysym
|
|
||||||
, riverWMWaylandKeyAction = action
|
|
||||||
}
|
|
||||||
|
|
||||||
spawnAction :: String -> RiverWMWaylandAction l
|
|
||||||
spawnAction command state = do
|
|
||||||
configLog $ "spawn start: " ++ command
|
|
||||||
process <- spawnCommand (riverSpawnPrelude ++ command)
|
|
||||||
_ <- forkIO $ do
|
|
||||||
exitCode <- waitForProcess process
|
|
||||||
configLog $ "spawn exit: " ++ command ++ " -> " ++ show exitCode
|
|
||||||
pure ()
|
|
||||||
pure ([], state)
|
|
||||||
|
|
||||||
riverSpawnPrelude :: String
|
|
||||||
riverSpawnPrelude =
|
|
||||||
"XDG_RUNTIME_DIR=\"${XDG_RUNTIME_DIR:-/run/user/$(id -u)}\"; "
|
|
||||||
++ "export XDG_RUNTIME_DIR; "
|
|
||||||
++ "if [ -z \"${WAYLAND_DISPLAY:-}\" ]; then "
|
|
||||||
++ "for socket in \"$XDG_RUNTIME_DIR\"/wayland-*; do "
|
|
||||||
++ "[ -S \"$socket\" ] || continue; "
|
|
||||||
++ "WAYLAND_DISPLAY=\"$(basename \"$socket\")\"; "
|
|
||||||
++ "break; "
|
|
||||||
++ "done; "
|
|
||||||
++ "fi; "
|
|
||||||
++ "export WAYLAND_DISPLAY=\"${WAYLAND_DISPLAY:-wayland-1}\"; "
|
|
||||||
++ "export XDG_CURRENT_DESKTOP=river; "
|
|
||||||
++ "export XDG_SESSION_DESKTOP=river-xmonad; "
|
|
||||||
++ "export XDG_SESSION_TYPE=wayland; "
|
|
||||||
++ "export IMALISON_SESSION_TYPE=wayland; "
|
|
||||||
++ "export IMALISON_WINDOW_MANAGER=river-xmonad; "
|
|
||||||
|
|
||||||
configLog :: String -> IO ()
|
|
||||||
configLog message = do
|
|
||||||
putStrLn $ "imalison-river-xmonad: " ++ message
|
|
||||||
hFlush stdout
|
|
||||||
|
|
||||||
layoutAction
|
|
||||||
:: (LayoutClass l Window, Read (l Window), Message message)
|
|
||||||
=> message
|
|
||||||
-> RiverWMWaylandAction l
|
|
||||||
layoutAction = handleRiverWMLayoutMessage
|
|
||||||
|
|
||||||
stackAction
|
|
||||||
:: (W.StackSet WorkspaceId (l Window) Window RiverWMOutputId ScreenDetail
|
|
||||||
-> W.StackSet WorkspaceId (l Window) Window RiverWMOutputId ScreenDetail)
|
|
||||||
-> RiverWMWaylandAction l
|
|
||||||
stackAction f state =
|
|
||||||
pure $ modifyRiverWMStackSet f state
|
|
||||||
|
|
||||||
stackActionWarpPointer
|
|
||||||
:: (W.StackSet WorkspaceId (l Window) Window RiverWMOutputId ScreenDetail
|
|
||||||
-> W.StackSet WorkspaceId (l Window) Window RiverWMOutputId ScreenDetail)
|
|
||||||
-> RiverWMWaylandAction l
|
|
||||||
stackActionWarpPointer f state =
|
|
||||||
pure $ modifyRiverWMStackSetAndWarpPointer f state
|
|
||||||
|
|
||||||
data ScratchpadDefinition = ScratchpadDefinition
|
|
||||||
{ scratchpadName :: !String
|
|
||||||
, scratchpadCommand :: !String
|
|
||||||
, scratchpadMatches :: !(RiverWMWindowState -> Bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
ordinaryWorkspaces :: [WorkspaceId]
|
|
||||||
ordinaryWorkspaces = map show [(1 :: Int) .. 9]
|
|
||||||
|
|
||||||
minimizedWorkspace :: WorkspaceId
|
|
||||||
minimizedWorkspace = "__minimized"
|
|
||||||
|
|
||||||
specialWorkspaces :: [WorkspaceId]
|
|
||||||
specialWorkspaces =
|
|
||||||
minimizedWorkspace : map (scratchpadWorkspace . scratchpadName) scratchpadDefinitions
|
|
||||||
|
|
||||||
scratchpadWorkspace :: String -> WorkspaceId
|
|
||||||
scratchpadWorkspace name = "__scratchpad:" ++ name
|
|
||||||
|
|
||||||
isSpecialWorkspace :: WorkspaceId -> Bool
|
|
||||||
isSpecialWorkspace workspace =
|
|
||||||
workspace == minimizedWorkspace || "__scratchpad:" `isPrefixOf` workspace
|
|
||||||
|
|
||||||
scratchpadDefinitions :: [ScratchpadDefinition]
|
|
||||||
scratchpadDefinitions =
|
|
||||||
[ ScratchpadDefinition "element" "element-desktop" $
|
|
||||||
anyMatcher [appIdMatches "Element", appIdMatches "element"]
|
|
||||||
, ScratchpadDefinition "htop" "ghostty --title=htop -e htop" $
|
|
||||||
titleContains "htop"
|
|
||||||
, ScratchpadDefinition "slack" "slack" $
|
|
||||||
anyMatcher [appIdMatches "Slack", appIdMatches "slack"]
|
|
||||||
, ScratchpadDefinition "spotify" "spotify" $
|
|
||||||
anyMatcher [appIdMatches "Spotify", appIdMatches "spotify"]
|
|
||||||
, ScratchpadDefinition "transmission" "transmission-gtk" $
|
|
||||||
anyMatcher [titleContains "Transmission", appIdContains "transmission"]
|
|
||||||
, ScratchpadDefinition "volume" "pavucontrol" $
|
|
||||||
anyMatcher [appIdMatches "Pavucontrol", appIdContains "pavucontrol"]
|
|
||||||
]
|
|
||||||
|
|
||||||
anyMatcher :: [RiverWMWindowState -> Bool] -> RiverWMWindowState -> Bool
|
|
||||||
anyMatcher matchers windowState =
|
|
||||||
any ($ windowState) matchers
|
|
||||||
|
|
||||||
appIdMatches :: String -> RiverWMWindowState -> Bool
|
|
||||||
appIdMatches expected windowState =
|
|
||||||
lower expected == maybe "" lower (riverWMWindowAppId windowState)
|
|
||||||
|
|
||||||
appIdContains :: String -> RiverWMWindowState -> Bool
|
|
||||||
appIdContains needle windowState =
|
|
||||||
lower needle `isInfixOf` maybe "" lower (riverWMWindowAppId windowState)
|
|
||||||
|
|
||||||
titleContains :: String -> RiverWMWindowState -> Bool
|
|
||||||
titleContains needle windowState =
|
|
||||||
lower needle `isInfixOf` maybe "" lower (riverWMWindowTitle windowState)
|
|
||||||
|
|
||||||
lower :: String -> String
|
|
||||||
lower = map toLower
|
|
||||||
|
|
||||||
closeFocusedWindow :: RiverWMWaylandAction l
|
|
||||||
closeFocusedWindow state@RiverWMState{riverWMStackSet, riverWMWindowIds} =
|
|
||||||
pure
|
|
||||||
( maybe [] ((: []) . RiverWMCloseWindow) $
|
|
||||||
W.peek riverWMStackSet >>= (`M.lookup` riverWMWindowIds)
|
|
||||||
, state
|
|
||||||
)
|
|
||||||
|
|
||||||
minimizeFocusedWindow :: RiverWMWaylandAction l
|
|
||||||
minimizeFocusedWindow =
|
|
||||||
stackAction $ W.shift minimizedWorkspace
|
|
||||||
|
|
||||||
restoreLastMinimizedWindow :: RiverWMWaylandAction l
|
|
||||||
restoreLastMinimizedWindow =
|
|
||||||
stackActionWarpPointer $ \stackSet ->
|
|
||||||
case workspaceFocusedWindow minimizedWorkspace stackSet of
|
|
||||||
Nothing -> stackSet
|
|
||||||
Just window ->
|
|
||||||
let currentTag = W.currentTag stackSet
|
|
||||||
in W.focusWindow window (W.shiftWin currentTag window stackSet)
|
|
||||||
|
|
||||||
toggleScratchpad :: String -> RiverWMWaylandAction l
|
|
||||||
toggleScratchpad name state@RiverWMState{riverWMStackSet} =
|
|
||||||
case find ((== name) . scratchpadName) scratchpadDefinitions of
|
|
||||||
Nothing ->
|
|
||||||
pure ([], state)
|
|
||||||
Just scratchpad ->
|
|
||||||
case W.peek riverWMStackSet of
|
|
||||||
Just focused | focused `elem` matchingWindows ->
|
|
||||||
pure $ modifyRiverWMStackSet (W.shift $ scratchpadWorkspace name) state
|
|
||||||
_ ->
|
|
||||||
case matchingWindows of
|
|
||||||
window : _ ->
|
|
||||||
pure $ modifyRiverWMStackSetAndWarpPointer (showScratchpadWindow window) state
|
|
||||||
[] ->
|
|
||||||
spawnAction (scratchpadCommand scratchpad) state
|
|
||||||
where
|
|
||||||
matchingWindows = scratchpadWindows scratchpad state
|
|
||||||
showScratchpadWindow window stackSet =
|
|
||||||
let currentTag = W.currentTag stackSet
|
|
||||||
in W.float window nearFullScratchpadRect $
|
|
||||||
W.focusWindow window (W.shiftWin currentTag window stackSet)
|
|
||||||
|
|
||||||
nearFullScratchpadRect :: W.RationalRect
|
|
||||||
nearFullScratchpadRect =
|
|
||||||
W.RationalRect left top width height
|
|
||||||
where
|
|
||||||
width = 0.9
|
|
||||||
height = 0.9
|
|
||||||
left = 0.95 - width
|
|
||||||
top = 0.95 - height
|
|
||||||
|
|
||||||
scratchpadWindows :: ScratchpadDefinition -> RiverWMState l -> [Window]
|
|
||||||
scratchpadWindows ScratchpadDefinition{scratchpadMatches} RiverWMState{riverWMWindows} =
|
|
||||||
[ riverWMWindowXWindow windowState
|
|
||||||
| windowState <- M.elems riverWMWindows
|
|
||||||
, scratchpadMatches windowState
|
|
||||||
]
|
|
||||||
|
|
||||||
selectWindowAction
|
|
||||||
:: String
|
|
||||||
-> (Window -> RiverWMState l -> ([RiverWMRequest], RiverWMState l))
|
|
||||||
-> RiverWMWaylandAction l
|
|
||||||
selectWindowAction prompt action state = do
|
|
||||||
selected <- rofiSelectWindow prompt state
|
|
||||||
pure $ maybe ([], state) (`action` state) selected
|
|
||||||
|
|
||||||
focusSelectedWindow :: Window -> RiverWMState l -> ([RiverWMRequest], RiverWMState l)
|
|
||||||
focusSelectedWindow window state =
|
|
||||||
modifyRiverWMStackSetAndWarpPointer (focusWindowEverywhere window) state
|
|
||||||
|
|
||||||
bringSelectedWindow :: Window -> RiverWMState l -> ([RiverWMRequest], RiverWMState l)
|
|
||||||
bringSelectedWindow window state =
|
|
||||||
modifyRiverWMStackSetAndWarpPointer (bringWindowToCurrentWorkspace window) state
|
|
||||||
|
|
||||||
replaceSelectedWindow :: Window -> RiverWMState l -> ([RiverWMRequest], RiverWMState l)
|
|
||||||
replaceSelectedWindow selected state =
|
|
||||||
modifyRiverWMStackSetAndWarpPointer replaceWindow state
|
|
||||||
where
|
|
||||||
replaceWindow stackSet =
|
|
||||||
case (W.peek stackSet, W.findTag selected stackSet) of
|
|
||||||
(Just focused, Just selectedWorkspace)
|
|
||||||
| focused /= selected ->
|
|
||||||
W.focusWindow selected $
|
|
||||||
W.shiftWin selectedWorkspace focused $
|
|
||||||
W.shiftWin (W.currentTag stackSet) selected stackSet
|
|
||||||
_ -> stackSet
|
|
||||||
|
|
||||||
gatherFocusedAppId :: RiverWMWaylandAction l
|
|
||||||
gatherFocusedAppId state@RiverWMState{riverWMStackSet, riverWMWindowIds, riverWMWindows} =
|
|
||||||
pure $ modifyRiverWMStackSet gatherMatching state
|
|
||||||
where
|
|
||||||
focusedAppId = do
|
|
||||||
focused <- W.peek riverWMStackSet
|
|
||||||
windowId <- M.lookup focused riverWMWindowIds
|
|
||||||
riverWMWindowAppId =<< M.lookup windowId riverWMWindows
|
|
||||||
|
|
||||||
matchingWindows =
|
|
||||||
[ riverWMWindowXWindow windowState
|
|
||||||
| windowState <- M.elems riverWMWindows
|
|
||||||
, riverWMWindowAppId windowState == focusedAppId
|
|
||||||
]
|
|
||||||
|
|
||||||
gatherMatching stackSet =
|
|
||||||
case focusedAppId of
|
|
||||||
Nothing -> stackSet
|
|
||||||
Just _ ->
|
|
||||||
foldl' (\acc window -> W.shiftWin (W.currentTag acc) window acc) stackSet matchingWindows
|
|
||||||
|
|
||||||
rofiSelectWindow :: String -> RiverWMState l -> IO (Maybe Window)
|
|
||||||
rofiSelectWindow prompt state =
|
|
||||||
case windowEntries state of
|
|
||||||
[] ->
|
|
||||||
pure Nothing
|
|
||||||
entries -> do
|
|
||||||
(exitCode, selected, _stderr) <-
|
|
||||||
readCreateProcessWithExitCode
|
|
||||||
(shell $ "rofi -dmenu -i -show-icons -p " ++ shellQuote prompt)
|
|
||||||
(concatMap formatWindowEntry entries)
|
|
||||||
pure $ case exitCode of
|
|
||||||
ExitSuccess -> parseSelectedWindow selected
|
|
||||||
_ -> Nothing
|
|
||||||
|
|
||||||
data WindowEntry = WindowEntry
|
|
||||||
{ windowEntryWindow :: !Window
|
|
||||||
, windowEntryWorkspace :: !WorkspaceId
|
|
||||||
, windowEntryAppId :: !String
|
|
||||||
, windowEntryTitle :: !String
|
|
||||||
}
|
|
||||||
|
|
||||||
windowEntries :: RiverWMState l -> [WindowEntry]
|
|
||||||
windowEntries RiverWMState{riverWMStackSet, riverWMWindowIds, riverWMWindows} =
|
|
||||||
[ WindowEntry window (W.tag workspace) appId title
|
|
||||||
| workspace <- W.workspaces riverWMStackSet
|
|
||||||
, not (isSpecialWorkspace $ W.tag workspace)
|
|
||||||
, window <- W.integrate' (W.stack workspace)
|
|
||||||
, let windowId = M.lookup window riverWMWindowIds
|
|
||||||
, Just windowState <- [windowId >>= (`M.lookup` riverWMWindows)]
|
|
||||||
, let appId = fromMaybe "window" (riverWMWindowAppId windowState)
|
|
||||||
title = fromMaybe "" (riverWMWindowTitle windowState)
|
|
||||||
]
|
|
||||||
|
|
||||||
formatWindowEntry :: WindowEntry -> String
|
|
||||||
formatWindowEntry WindowEntry{..} =
|
|
||||||
visibleLabel ++ "\0icon\x1f" ++ iconName ++ "\n"
|
|
||||||
where
|
|
||||||
visibleLabel =
|
|
||||||
show windowEntryWindow
|
|
||||||
++ "\t["
|
|
||||||
++ windowEntryWorkspace
|
|
||||||
++ "] "
|
|
||||||
++ if null windowEntryTitle
|
|
||||||
then windowEntryAppId
|
|
||||||
else windowEntryAppId ++ " - " ++ windowEntryTitle
|
|
||||||
iconName = if null windowEntryAppId then "application-x-executable" else windowEntryAppId
|
|
||||||
|
|
||||||
parseSelectedWindow :: String -> Maybe Window
|
|
||||||
parseSelectedWindow selected =
|
|
||||||
case reads (takeWhile (/= '\t') $ takeWhile (/= '\0') selected) of
|
|
||||||
(window, _) : _ -> Just window
|
|
||||||
[] -> Nothing
|
|
||||||
|
|
||||||
focusWindowEverywhere
|
|
||||||
:: Eq sid
|
|
||||||
=> Window
|
|
||||||
-> W.StackSet WorkspaceId l Window sid sd
|
|
||||||
-> W.StackSet WorkspaceId l Window sid sd
|
|
||||||
focusWindowEverywhere window stackSet =
|
|
||||||
maybe stackSet (\workspace -> W.focusWindow window (W.greedyView workspace stackSet)) $
|
|
||||||
W.findTag window stackSet
|
|
||||||
|
|
||||||
bringWindowToCurrentWorkspace
|
|
||||||
:: Eq sid
|
|
||||||
=> Window
|
|
||||||
-> W.StackSet WorkspaceId l Window sid sd
|
|
||||||
-> W.StackSet WorkspaceId l Window sid sd
|
|
||||||
bringWindowToCurrentWorkspace window stackSet =
|
|
||||||
W.focusWindow window (W.shiftWin (W.currentTag stackSet) window stackSet)
|
|
||||||
|
|
||||||
workspaceFocusedWindow :: WorkspaceId -> W.StackSet WorkspaceId l Window sid sd -> Maybe Window
|
|
||||||
workspaceFocusedWindow workspace stackSet =
|
|
||||||
W.focus <$> (W.stack =<< find ((== workspace) . W.tag) (W.workspaces stackSet))
|
|
||||||
|
|
||||||
shellQuote :: String -> String
|
|
||||||
shellQuote value =
|
|
||||||
"'" ++ concatMap quoteChar value ++ "'"
|
|
||||||
where
|
|
||||||
quoteChar '\'' = "'\\''"
|
|
||||||
quoteChar char = [char]
|
|
||||||
|
|
||||||
viewNextEmptyWorkspace :: RiverWMWaylandAction l
|
|
||||||
viewNextEmptyWorkspace =
|
|
||||||
stackAction $ \stackSet ->
|
|
||||||
maybe stackSet (`W.greedyView` stackSet) (nextEmptyWorkspace stackSet)
|
|
||||||
|
|
||||||
shiftFocusedToNextEmptyWorkspace :: Bool -> RiverWMWaylandAction l
|
|
||||||
shiftFocusedToNextEmptyWorkspace follow =
|
|
||||||
(if follow then stackActionWarpPointer else stackAction) $ \stackSet ->
|
|
||||||
maybe stackSet (`shiftFocusedToWorkspace` stackSet) (nextEmptyWorkspace stackSet)
|
|
||||||
where
|
|
||||||
shiftFocusedToWorkspace workspace stackSet =
|
|
||||||
let shifted = W.shift workspace stackSet
|
|
||||||
in if follow then W.greedyView workspace shifted else shifted
|
|
||||||
|
|
||||||
nextEmptyWorkspace
|
|
||||||
:: W.StackSet WorkspaceId l Window sid sd
|
|
||||||
-> Maybe WorkspaceId
|
|
||||||
nextEmptyWorkspace stackSet =
|
|
||||||
find (`workspaceIsEmpty` stackSet) candidates
|
|
||||||
where
|
|
||||||
currentTag = W.currentTag stackSet
|
|
||||||
candidates =
|
|
||||||
case break (== currentTag) ordinaryWorkspaces of
|
|
||||||
(_before, []) -> ordinaryWorkspaces
|
|
||||||
(before, _current : after) -> after ++ before
|
|
||||||
|
|
||||||
workspaceIsEmpty
|
|
||||||
:: WorkspaceId
|
|
||||||
-> W.StackSet WorkspaceId l Window sid sd
|
|
||||||
-> Bool
|
|
||||||
workspaceIsEmpty workspace stackSet =
|
|
||||||
maybe False (null . W.integrate' . W.stack) $
|
|
||||||
find ((== workspace) . W.tag) (W.workspaces stackSet)
|
|
||||||
|
|
||||||
directionalSwap :: Direction -> RiverWMWaylandAction l
|
|
||||||
directionalSwap direction state@RiverWMState{riverWMStackSet} =
|
|
||||||
pure $ modifyRiverWMStackSet swapTarget state
|
|
||||||
where
|
|
||||||
target = directionalTargetAmong (W.index riverWMStackSet) direction state
|
|
||||||
swapTarget stackSet =
|
|
||||||
maybe (fallbackDirectionalSwap direction stackSet) (`swapFocusedWithWindow` stackSet) target
|
|
||||||
|
|
||||||
fallbackDirectionalSwap
|
|
||||||
:: Direction
|
|
||||||
-> W.StackSet WorkspaceId l Window sid sd
|
|
||||||
-> W.StackSet WorkspaceId l Window sid sd
|
|
||||||
fallbackDirectionalSwap DirectionUp = W.swapUp
|
|
||||||
fallbackDirectionalSwap DirectionLeft = W.swapUp
|
|
||||||
fallbackDirectionalSwap DirectionDown = W.swapDown
|
|
||||||
fallbackDirectionalSwap DirectionRight = W.swapDown
|
|
||||||
|
|
||||||
swapFocusedWithWindow
|
|
||||||
:: Window
|
|
||||||
-> W.StackSet WorkspaceId l Window sid sd
|
|
||||||
-> W.StackSet WorkspaceId l Window sid sd
|
|
||||||
swapFocusedWithWindow target stackSet =
|
|
||||||
case W.peek stackSet of
|
|
||||||
Just focused | focused /= target ->
|
|
||||||
W.modify' (swapStackOrder focused target) stackSet
|
|
||||||
_ -> stackSet
|
|
||||||
|
|
||||||
swapStackOrder :: Eq a => a -> a -> W.Stack a -> W.Stack a
|
|
||||||
swapStackOrder focused target stack =
|
|
||||||
stackFromListFocused stack focused $
|
|
||||||
map swapWindow (W.integrate stack)
|
|
||||||
where
|
|
||||||
swapWindow window
|
|
||||||
| window == focused = target
|
|
||||||
| window == target = focused
|
|
||||||
| otherwise = window
|
|
||||||
|
|
||||||
stackFromListFocused :: Eq a => W.Stack a -> a -> [a] -> W.Stack a
|
|
||||||
stackFromListFocused fallback focused windows =
|
|
||||||
case break (== focused) windows of
|
|
||||||
(before, _focused : after) -> W.Stack focused (reverse before) after
|
|
||||||
_ -> fallback
|
|
||||||
|
|
||||||
focusDirectionalScreen :: Direction -> RiverWMWaylandAction l
|
|
||||||
focusDirectionalScreen direction =
|
|
||||||
stackAction $ \stackSet ->
|
|
||||||
maybe stackSet ((`W.view` stackSet) . W.tag . W.workspace) $
|
|
||||||
directionalScreenTarget direction stackSet
|
|
||||||
|
|
||||||
shiftFocusedToDirectionalScreen :: Bool -> Direction -> RiverWMWaylandAction l
|
|
||||||
shiftFocusedToDirectionalScreen follow direction =
|
|
||||||
(if follow then stackActionWarpPointer else stackAction) $ \stackSet ->
|
|
||||||
maybe stackSet (shiftToScreen stackSet) $
|
|
||||||
directionalScreenTarget direction stackSet
|
|
||||||
where
|
|
||||||
shiftToScreen stackSet screen =
|
|
||||||
let workspace = W.tag (W.workspace screen)
|
|
||||||
shifted = W.shift workspace stackSet
|
|
||||||
in if follow then W.view workspace shifted else shifted
|
|
||||||
|
|
||||||
shiftFocusedToEmptyWorkspaceOnDirectionalScreen :: Direction -> RiverWMWaylandAction l
|
|
||||||
shiftFocusedToEmptyWorkspaceOnDirectionalScreen direction =
|
|
||||||
stackActionWarpPointer $ \stackSet ->
|
|
||||||
maybe stackSet (shiftToEmptyWorkspaceOnScreen stackSet) $
|
|
||||||
directionalScreenTarget direction stackSet
|
|
||||||
where
|
|
||||||
shiftToEmptyWorkspaceOnScreen stackSet screen =
|
|
||||||
let workspace = W.tag (W.workspace screen)
|
|
||||||
onDestination = W.view workspace (W.shift workspace stackSet)
|
|
||||||
in maybe onDestination
|
|
||||||
(\emptyWorkspace -> W.greedyView emptyWorkspace (W.shift emptyWorkspace onDestination))
|
|
||||||
(nextEmptyWorkspace onDestination)
|
|
||||||
|
|
||||||
directionalFocus :: Direction -> RiverWMWaylandAction l
|
|
||||||
directionalFocus direction state =
|
|
||||||
pure $ modifyRiverWMStackSet focusDirectionalWindow state
|
|
||||||
where
|
|
||||||
focusDirectionalWindow stackSet =
|
|
||||||
maybe (fallbackDirectionalFocus direction stackSet) (`W.focusWindow` stackSet) $
|
|
||||||
directionalTarget direction state
|
|
||||||
|
|
||||||
fallbackDirectionalFocus
|
|
||||||
:: Direction
|
|
||||||
-> W.StackSet WorkspaceId l Window sid sd
|
|
||||||
-> W.StackSet WorkspaceId l Window sid sd
|
|
||||||
fallbackDirectionalFocus DirectionUp = W.focusUp
|
|
||||||
fallbackDirectionalFocus DirectionLeft = W.focusUp
|
|
||||||
fallbackDirectionalFocus DirectionDown = W.focusDown
|
|
||||||
fallbackDirectionalFocus DirectionRight = W.focusDown
|
|
||||||
|
|
||||||
directionalTarget :: Direction -> RiverWMState l -> Maybe Window
|
|
||||||
directionalTarget direction state@RiverWMState{riverWMStackSet} =
|
|
||||||
directionalTargetAmong (W.index riverWMStackSet) direction state
|
|
||||||
|
|
||||||
directionalTargetAmong :: [Window] -> Direction -> RiverWMState l -> Maybe Window
|
|
||||||
directionalTargetAmong allowed direction RiverWMState{riverWMStackSet, riverWMWindows, riverWMWindowIds} = do
|
|
||||||
focused <- W.peek riverWMStackSet
|
|
||||||
focusedId <- M.lookup focused riverWMWindowIds
|
|
||||||
focusedRect <- riverWMWindowDesired =<< M.lookup focusedId riverWMWindows
|
|
||||||
let focusedCenter = rectCenter focusedRect
|
|
||||||
candidates =
|
|
||||||
[ (window, directionScore direction focusedCenter (rectCenter rect))
|
|
||||||
| (windowId, RiverWMWindowState{riverWMWindowXWindow = window, riverWMWindowDesired = Just rect}) <-
|
|
||||||
M.toList riverWMWindows
|
|
||||||
, windowId /= focusedId
|
|
||||||
, window `elem` allowed
|
|
||||||
]
|
|
||||||
viable = mapMaybe sequenceCandidate candidates
|
|
||||||
fst <$> minimumMaybeBy (compare `on` snd) viable
|
|
||||||
|
|
||||||
directionalScreenTarget
|
|
||||||
:: Direction
|
|
||||||
-> W.StackSet WorkspaceId l Window sid ScreenDetail
|
|
||||||
-> Maybe (W.Screen WorkspaceId l Window sid ScreenDetail)
|
|
||||||
directionalScreenTarget direction stackSet =
|
|
||||||
fst <$> minimumMaybeBy (compare `on` snd) viable
|
|
||||||
where
|
|
||||||
focusedCenter = screenCenter (W.current stackSet)
|
|
||||||
candidates =
|
|
||||||
[ (screen, directionScore direction focusedCenter (screenCenter screen))
|
|
||||||
| screen <- W.visible stackSet
|
|
||||||
]
|
|
||||||
viable = mapMaybe sequenceCandidate candidates
|
|
||||||
|
|
||||||
screenCenter :: W.Screen WorkspaceId l Window sid ScreenDetail -> (Double, Double)
|
|
||||||
screenCenter = rectCenter . screenRect . W.screenDetail
|
|
||||||
|
|
||||||
equalColumnRects :: Rectangle -> Int -> [Rectangle]
|
|
||||||
equalColumnRects _ count | count <= 0 = []
|
|
||||||
equalColumnRects rect 1 = [rect]
|
|
||||||
equalColumnRects (Rectangle x y width height) count =
|
|
||||||
[ Rectangle
|
|
||||||
(x + fromIntegral riverOuterGap + fromIntegral (columnOffset index))
|
|
||||||
(y + fromIntegral riverOuterGap)
|
|
||||||
(fromIntegral (columnWidth index))
|
|
||||||
contentHeight
|
|
||||||
| index <- [0 .. count - 1]
|
|
||||||
]
|
|
||||||
where
|
|
||||||
totalWidth = max 0 (fromIntegral width - 2 * riverOuterGap - riverInnerGap * (count - 1))
|
|
||||||
contentHeight = fromIntegral (max 1 (fromIntegral height - 2 * riverOuterGap :: Int))
|
|
||||||
baseWidth = totalWidth `div` count
|
|
||||||
extraPixels = totalWidth `mod` count
|
|
||||||
columnWidth index = baseWidth + if index < extraPixels then 1 else 0
|
|
||||||
columnOffset index = index * baseWidth + min index extraPixels + index * riverInnerGap
|
|
||||||
|
|
||||||
riverOuterGap :: Int
|
|
||||||
riverOuterGap = 10
|
|
||||||
|
|
||||||
riverInnerGap :: Int
|
|
||||||
riverInnerGap = 5
|
|
||||||
|
|
||||||
sequenceCandidate :: (a, Maybe b) -> Maybe (a, b)
|
|
||||||
sequenceCandidate (value, Just score) = Just (value, score)
|
|
||||||
sequenceCandidate (_, Nothing) = Nothing
|
|
||||||
|
|
||||||
rectCenter :: Rectangle -> (Double, Double)
|
|
||||||
rectCenter (Rectangle x y width height) =
|
|
||||||
( fromIntegral x + fromIntegral width / 2
|
|
||||||
, fromIntegral y + fromIntegral height / 2
|
|
||||||
)
|
|
||||||
|
|
||||||
directionScore :: Direction -> (Double, Double) -> (Double, Double) -> Maybe (Double, Double)
|
|
||||||
directionScore direction (fx, fy) (cx, cy) =
|
|
||||||
case direction of
|
|
||||||
DirectionUp | cy < fy -> Just (fy - cy, abs (cx - fx))
|
|
||||||
DirectionDown | cy > fy -> Just (cy - fy, abs (cx - fx))
|
|
||||||
DirectionLeft | cx < fx -> Just (fx - cx, abs (cy - fy))
|
|
||||||
DirectionRight | cx > fx -> Just (cx - fx, abs (cy - fy))
|
|
||||||
_ -> Nothing
|
|
||||||
|
|
||||||
minimumMaybeBy :: (a -> a -> Ordering) -> [a] -> Maybe a
|
|
||||||
minimumMaybeBy _ [] = Nothing
|
|
||||||
minimumMaybeBy compareFn xs = Just (minimumBy compareFn xs)
|
|
||||||
|
|
||||||
addHyperChordBindings
|
|
||||||
:: RiverWMWaylandModifiers
|
|
||||||
-> RiverWMWaylandModifiers
|
|
||||||
-> [RiverWMWaylandKeyBinding l]
|
|
||||||
-> [RiverWMWaylandKeyBinding l]
|
|
||||||
addHyperChordBindings hyperMask chordMask bindings =
|
|
||||||
bindings ++ M.elems chosen
|
|
||||||
where
|
|
||||||
existingKeys =
|
|
||||||
M.fromList
|
|
||||||
[ ((riverWMWaylandKeyModifiers binding, riverWMWaylandKeyKeysym binding), ())
|
|
||||||
| binding <- bindings
|
|
||||||
]
|
|
||||||
|
|
||||||
chordBinding binding@RiverWMWaylandKeyBinding{riverWMWaylandKeyModifiers} =
|
|
||||||
binding
|
|
||||||
{ riverWMWaylandKeyModifiers =
|
|
||||||
(riverWMWaylandKeyModifiers .&. complement hyperMask) .|. chordMask
|
|
||||||
}
|
|
||||||
|
|
||||||
candidates =
|
|
||||||
[ ( (riverWMWaylandKeyModifiers chorded, riverWMWaylandKeyKeysym chorded)
|
|
||||||
, (score (riverWMWaylandKeyModifiers binding), chorded)
|
|
||||||
)
|
|
||||||
| binding <- bindings
|
|
||||||
, riverWMWaylandKeyModifiers binding .&. hyperMask /= 0
|
|
||||||
, let chorded = chordBinding binding
|
|
||||||
, M.notMember (riverWMWaylandKeyModifiers chorded, riverWMWaylandKeyKeysym chorded) existingKeys
|
|
||||||
]
|
|
||||||
|
|
||||||
chosen =
|
|
||||||
fmap snd $
|
|
||||||
foldl' keepBest M.empty candidates
|
|
||||||
|
|
||||||
keepBest selected (bindingKey, candidate@(candidateScore, _binding)) =
|
|
||||||
case M.lookup bindingKey selected of
|
|
||||||
Nothing -> M.insert bindingKey candidate selected
|
|
||||||
Just (bestScore, _) ->
|
|
||||||
if candidateScore < bestScore
|
|
||||||
then M.insert bindingKey candidate selected
|
|
||||||
else selected
|
|
||||||
|
|
||||||
score modifiers =
|
|
||||||
length $
|
|
||||||
filter (/= 0)
|
|
||||||
[ modifiers .&. shift
|
|
||||||
, modifiers .&. ctrl
|
|
||||||
, modifiers .&. alt
|
|
||||||
, modifiers .&. hyper
|
|
||||||
, modifiers .&. super
|
|
||||||
, modifiers .&. riverWMWaylandModifierMod5
|
|
||||||
]
|
|
||||||
|
|
||||||
noMods, shift, ctrl, alt, hyper, super, hyperChord :: RiverWMWaylandModifiers
|
|
||||||
noMods = riverWMWaylandModifierNone
|
|
||||||
shift = riverWMWaylandModifierShift
|
|
||||||
ctrl = riverWMWaylandModifierCtrl
|
|
||||||
alt = riverWMWaylandModifierAlt
|
|
||||||
hyper = riverWMWaylandModifierHyper
|
|
||||||
super = riverWMWaylandModifierSuper
|
|
||||||
hyperChord = ctrl .|. alt .|. super
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
cabal-version: 2.4
|
|
||||||
name: imalison-river-xmonad
|
|
||||||
version: 0.1.0.0
|
|
||||||
license: BSD-3-Clause
|
|
||||||
author: Ivan Malison
|
|
||||||
maintainer: IvanMalison@gmail.com
|
|
||||||
build-type: Simple
|
|
||||||
|
|
||||||
executable imalison-river-xmonad
|
|
||||||
main-is: Main.hs
|
|
||||||
build-depends: base >= 4.12 && < 5
|
|
||||||
, containers
|
|
||||||
, process
|
|
||||||
, X11
|
|
||||||
, xmonad
|
|
||||||
, xmonad-contrib
|
|
||||||
ghc-options: -threaded -Wall -Wno-unused-do-bind -Wno-deprecations -Wno-missing-signatures -Wno-name-shadowing
|
|
||||||
default-language: Haskell2010
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
_: pkgs: {
|
|
||||||
haskellPackages = pkgs.haskellPackages.override (old: {
|
|
||||||
overrides = pkgs.lib.composeExtensions (old.overrides or (_: _: {})) (self: _super: {
|
|
||||||
imalison-river-xmonad = self.callCabal2nix "imalison-river-xmonad" (
|
|
||||||
pkgs.lib.sourceByRegex ./.
|
|
||||||
[
|
|
||||||
"Main.hs"
|
|
||||||
"imalison-river-xmonad.cabal"
|
|
||||||
]
|
|
||||||
) { };
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -1,138 +0,0 @@
|
|||||||
configuration {
|
|
||||||
font: "Roboto 12";
|
|
||||||
show-icons: true;
|
|
||||||
icon-theme: "Papirus";
|
|
||||||
display-drun: "Search";
|
|
||||||
display-run: "Run";
|
|
||||||
display-window: "Windows";
|
|
||||||
drun-display-format: "{name}";
|
|
||||||
disable-history: false;
|
|
||||||
sidebar-mode: false;
|
|
||||||
}
|
|
||||||
|
|
||||||
* {
|
|
||||||
bg: #00000000;
|
|
||||||
backdrop: #0b102026;
|
|
||||||
panel: #00000000;
|
|
||||||
control: #ffffffe0;
|
|
||||||
candidate: #18203372;
|
|
||||||
candidate-active:#2430489c;
|
|
||||||
text: #111827ff;
|
|
||||||
text-muted: #667085ff;
|
|
||||||
text-on-dark: #f8fafcff;
|
|
||||||
text-dark-muted: #d0d6e0ff;
|
|
||||||
accent: #007affff;
|
|
||||||
accent-soft: #d8eaffcc;
|
|
||||||
border: #ffffff96;
|
|
||||||
hairline: #cfd6df70;
|
|
||||||
}
|
|
||||||
|
|
||||||
window {
|
|
||||||
transparency: "real";
|
|
||||||
location: center;
|
|
||||||
anchor: center;
|
|
||||||
width: 72%;
|
|
||||||
height: 78%;
|
|
||||||
background-color: @backdrop;
|
|
||||||
text-color: @text;
|
|
||||||
border: 1px;
|
|
||||||
border-color: @border;
|
|
||||||
border-radius: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
mainbox {
|
|
||||||
background-color: @panel;
|
|
||||||
children: [ inputbar, listview ];
|
|
||||||
spacing: 10px;
|
|
||||||
padding: 88px 136px;
|
|
||||||
margin: 0px;
|
|
||||||
border: 0px;
|
|
||||||
border-radius: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
inputbar {
|
|
||||||
background-color: @control;
|
|
||||||
text-color: @text;
|
|
||||||
children: [ prompt, entry ];
|
|
||||||
border: 1px;
|
|
||||||
border-color: @hairline;
|
|
||||||
border-radius: 18px;
|
|
||||||
padding: 13px 15px;
|
|
||||||
spacing: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
prompt {
|
|
||||||
enabled: true;
|
|
||||||
background-color: @bg;
|
|
||||||
text-color: @accent;
|
|
||||||
font: "Roboto 12";
|
|
||||||
}
|
|
||||||
|
|
||||||
entry {
|
|
||||||
background-color: @bg;
|
|
||||||
text-color: @text;
|
|
||||||
placeholder-color: @text-muted;
|
|
||||||
placeholder: "";
|
|
||||||
cursor: text;
|
|
||||||
expand: true;
|
|
||||||
}
|
|
||||||
|
|
||||||
listview {
|
|
||||||
background-color: @bg;
|
|
||||||
columns: 1;
|
|
||||||
lines: 10;
|
|
||||||
spacing: 0px;
|
|
||||||
border: 1px;
|
|
||||||
border-color: @border;
|
|
||||||
border-radius: 14px;
|
|
||||||
cycle: false;
|
|
||||||
dynamic: true;
|
|
||||||
layout: vertical;
|
|
||||||
scrollbar: false;
|
|
||||||
}
|
|
||||||
|
|
||||||
element {
|
|
||||||
background-color: @bg;
|
|
||||||
text-color: @text-on-dark;
|
|
||||||
orientation: horizontal;
|
|
||||||
border: 0px 0px 1px 0px;
|
|
||||||
border-color: @hairline;
|
|
||||||
border-radius: 0px;
|
|
||||||
padding: 11px 11px;
|
|
||||||
spacing: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
element-icon {
|
|
||||||
background-color: @bg;
|
|
||||||
text-color: inherit;
|
|
||||||
size: 24px;
|
|
||||||
vertical-align: 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
element-text {
|
|
||||||
background-color: @bg;
|
|
||||||
text-color: inherit;
|
|
||||||
vertical-align: 0.5;
|
|
||||||
horizontal-align: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
element selected {
|
|
||||||
background-color: @candidate;
|
|
||||||
text-color: @text-on-dark;
|
|
||||||
border-color: @border;
|
|
||||||
}
|
|
||||||
|
|
||||||
element selected element-text {
|
|
||||||
text-color: @text-on-dark;
|
|
||||||
}
|
|
||||||
|
|
||||||
message {
|
|
||||||
background-color: @candidate;
|
|
||||||
border-radius: 14px;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
textbox {
|
|
||||||
background-color: @bg;
|
|
||||||
text-color: @text-dark-muted;
|
|
||||||
}
|
|
||||||
9
dotfiles/config/rofi/colorful/colors.rasi
Normal file
9
dotfiles/config/rofi/colorful/colors.rasi
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/* colors */
|
||||||
|
|
||||||
|
* {
|
||||||
|
al: #00000000;
|
||||||
|
bg: #000000ff;
|
||||||
|
se: #101010ff;
|
||||||
|
fg: #FFFFFFff;
|
||||||
|
ac: #EC7875ff;
|
||||||
|
}
|
||||||
51
dotfiles/config/rofi/colorful/launcher.sh
Executable file
51
dotfiles/config/rofi/colorful/launcher.sh
Executable file
@@ -0,0 +1,51 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
## Author : Aditya Shakya
|
||||||
|
## Mail : adi1090x@gmail.com
|
||||||
|
## Github : @adi1090x
|
||||||
|
## Twitter : @adi1090x
|
||||||
|
|
||||||
|
# Available Styles
|
||||||
|
# >> Created and tested on : rofi 1.6.0-1
|
||||||
|
#
|
||||||
|
# style_1 style_2 style_3 style_4 style_5 style_6
|
||||||
|
# style_7 style_8 style_9 style_10 style_11 style_12
|
||||||
|
|
||||||
|
theme="style_1"
|
||||||
|
dir="$HOME/.config/rofi/launchers/colorful"
|
||||||
|
|
||||||
|
# dark
|
||||||
|
ALPHA="#00000000"
|
||||||
|
BG="#000000ff"
|
||||||
|
FG="#FFFFFFff"
|
||||||
|
SELECT="#101010ff"
|
||||||
|
|
||||||
|
# light
|
||||||
|
#ALPHA="#00000000"
|
||||||
|
#BG="#FFFFFFff"
|
||||||
|
#FG="#000000ff"
|
||||||
|
#SELECT="#f3f3f3ff"
|
||||||
|
|
||||||
|
# accent colors
|
||||||
|
COLORS=('#EC7875' '#61C766' '#FDD835' '#42A5F5' '#BA68C8' '#4DD0E1' '#00B19F' \
|
||||||
|
'#FBC02D' '#E57C46' '#AC8476' '#6D8895' '#EC407A' '#B9C244' '#6C77BB')
|
||||||
|
ACCENT="${COLORS[$(( $RANDOM % 14 ))]}ff"
|
||||||
|
|
||||||
|
# overwrite colors file
|
||||||
|
cat > $dir/colors.rasi <<- EOF
|
||||||
|
/* colors */
|
||||||
|
|
||||||
|
* {
|
||||||
|
al: $ALPHA;
|
||||||
|
bg: $BG;
|
||||||
|
se: $SELECT;
|
||||||
|
fg: $FG;
|
||||||
|
ac: $ACCENT;
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# comment these lines to disable random style
|
||||||
|
themes=($(ls -p --hide="launcher.sh" --hide="colors.rasi" $dir))
|
||||||
|
theme="${themes[$(( $RANDOM % 12 ))]}"
|
||||||
|
|
||||||
|
rofi -no-lazy-grab -show drun -modi drun -theme $dir/"$theme"
|
||||||
119
dotfiles/config/rofi/colorful/style_1.rasi
Normal file
119
dotfiles/config/rofi/colorful/style_1.rasi
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Author : Aditya Shakya
|
||||||
|
* Mail : adi1090x@gmail.com
|
||||||
|
* Github : @adi1090x
|
||||||
|
* Twitter : @adi1090x
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
configuration {
|
||||||
|
font: "Iosevka Nerd Font 10";
|
||||||
|
show-icons: true;
|
||||||
|
icon-theme: "Papirus";
|
||||||
|
display-drun: "";
|
||||||
|
drun-display-format: "{name}";
|
||||||
|
disable-history: false;
|
||||||
|
sidebar-mode: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@import "colors.rasi"
|
||||||
|
|
||||||
|
window {
|
||||||
|
transparency: "real";
|
||||||
|
background-color: @bg;
|
||||||
|
text-color: @fg;
|
||||||
|
border: 0px;
|
||||||
|
border-color: @ac;
|
||||||
|
border-radius: 12px;
|
||||||
|
width: 35%;
|
||||||
|
location: center;
|
||||||
|
x-offset: 0;
|
||||||
|
y-offset: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt {
|
||||||
|
enabled: true;
|
||||||
|
padding: 0.30% 1% 0% -0.5%;
|
||||||
|
background-color: @al;
|
||||||
|
text-color: @bg;
|
||||||
|
font: "FantasqueSansMono Nerd Font 12";
|
||||||
|
}
|
||||||
|
|
||||||
|
entry {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: @bg;
|
||||||
|
placeholder-color: @bg;
|
||||||
|
expand: true;
|
||||||
|
horizontal-align: 0;
|
||||||
|
placeholder: "Search";
|
||||||
|
padding: 0.10% 0% 0% 0%;
|
||||||
|
blink: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputbar {
|
||||||
|
children: [ prompt, entry ];
|
||||||
|
background-color: @ac;
|
||||||
|
text-color: @bg;
|
||||||
|
expand: false;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 0px;
|
||||||
|
border-color: @ac;
|
||||||
|
margin: 0% 0% 0% 0%;
|
||||||
|
padding: 1.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
listview {
|
||||||
|
background-color: @al;
|
||||||
|
padding: 10px;
|
||||||
|
columns: 5;
|
||||||
|
lines: 3;
|
||||||
|
spacing: 0%;
|
||||||
|
cycle: false;
|
||||||
|
dynamic: true;
|
||||||
|
layout: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
mainbox {
|
||||||
|
background-color: @al;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 0% 0% 0% 0%;
|
||||||
|
border-color: @ac;
|
||||||
|
children: [ inputbar, listview ];
|
||||||
|
spacing: 0%;
|
||||||
|
padding: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: @fg;
|
||||||
|
orientation: vertical;
|
||||||
|
border-radius: 0%;
|
||||||
|
padding: 2% 0% 2% 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element-icon {
|
||||||
|
background-color: inherit;
|
||||||
|
text-color: inherit;
|
||||||
|
horizontal-align: 0.5;
|
||||||
|
vertical-align: 0.5;
|
||||||
|
size: 64px;
|
||||||
|
border: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
element-text {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: inherit;
|
||||||
|
expand: true;
|
||||||
|
horizontal-align: 0.5;
|
||||||
|
vertical-align: 0.5;
|
||||||
|
margin: 0.5% 0.5% -0.5% 0.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element selected {
|
||||||
|
background-color: @se;
|
||||||
|
text-color: @fg;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 12px;
|
||||||
|
border-color: @bg;
|
||||||
|
}
|
||||||
123
dotfiles/config/rofi/colorful/style_10.rasi
Normal file
123
dotfiles/config/rofi/colorful/style_10.rasi
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Author : Aditya Shakya
|
||||||
|
* Mail : adi1090x@gmail.com
|
||||||
|
* Github : @adi1090x
|
||||||
|
* Twitter : @adi1090x
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
configuration {
|
||||||
|
font: "Iosevka Nerd Font 10";
|
||||||
|
show-icons: true;
|
||||||
|
icon-theme: "Papirus";
|
||||||
|
display-drun: "Applications";
|
||||||
|
drun-display-format: "{name}";
|
||||||
|
disable-history: false;
|
||||||
|
sidebar-mode: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@import "colors.rasi"
|
||||||
|
|
||||||
|
window {
|
||||||
|
transparency: "real";
|
||||||
|
background-color: @bg;
|
||||||
|
text-color: @fg;
|
||||||
|
border: 0px;
|
||||||
|
border-color: @ac;
|
||||||
|
border-radius: 0px;
|
||||||
|
width: 80%;
|
||||||
|
height: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt {
|
||||||
|
enabled: true;
|
||||||
|
padding: 1% 0.75% 1% 0.75%;
|
||||||
|
background-color: @ac;
|
||||||
|
text-color: @fg;
|
||||||
|
border-radius: 100%;
|
||||||
|
font: "Iosevka Nerd Font 12";
|
||||||
|
}
|
||||||
|
|
||||||
|
textbox-prompt-colon {
|
||||||
|
padding: 1% 0% 1% 0%;
|
||||||
|
background-color: @se;
|
||||||
|
text-color: @fg;
|
||||||
|
expand: false;
|
||||||
|
str: " :: ";
|
||||||
|
}
|
||||||
|
|
||||||
|
entry {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: @fg;
|
||||||
|
placeholder-color: @fg;
|
||||||
|
expand: true;
|
||||||
|
horizontal-align: 0;
|
||||||
|
placeholder: "Search...";
|
||||||
|
padding: 1.15% 0.5% 1% 0.5%;
|
||||||
|
blink: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputbar {
|
||||||
|
children: [ prompt, entry ];
|
||||||
|
background-color: @se;
|
||||||
|
text-color: @fg;
|
||||||
|
expand: false;
|
||||||
|
border: 0% 0.2% 0.3% 0%;
|
||||||
|
border-radius: 100%;
|
||||||
|
border-color: @ac;
|
||||||
|
}
|
||||||
|
|
||||||
|
listview {
|
||||||
|
background-color: @al;
|
||||||
|
padding: 0px;
|
||||||
|
columns: 3;
|
||||||
|
spacing: 1%;
|
||||||
|
cycle: false;
|
||||||
|
dynamic: true;
|
||||||
|
layout: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
mainbox {
|
||||||
|
background-color: @al;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 0% 0% 0% 0%;
|
||||||
|
border-color: @ac;
|
||||||
|
children: [ inputbar, listview ];
|
||||||
|
spacing: 2%;
|
||||||
|
padding: 20% 15% 20% 15%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element {
|
||||||
|
background-color: @se;
|
||||||
|
text-color: @fg;
|
||||||
|
orientation: horizontal;
|
||||||
|
border-radius: 100%;
|
||||||
|
padding: 1% 0.5% 1% 0.75%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element-icon {
|
||||||
|
background-color: inherit;
|
||||||
|
text-color: inherit;
|
||||||
|
horizontal-align: 0.5;
|
||||||
|
vertical-align: 0.5;
|
||||||
|
size: 24px;
|
||||||
|
border: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
element-text {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: inherit;
|
||||||
|
expand: true;
|
||||||
|
horizontal-align: 0;
|
||||||
|
vertical-align: 0.5;
|
||||||
|
margin: 0% 0.25% 0% 0.25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element selected {
|
||||||
|
background-color: @se;
|
||||||
|
text-color: @ac;
|
||||||
|
border: 0% 0% 0.3% 0.2%;
|
||||||
|
border-radius: 100%;
|
||||||
|
border-color: @ac;
|
||||||
|
}
|
||||||
129
dotfiles/config/rofi/colorful/style_11.rasi
Normal file
129
dotfiles/config/rofi/colorful/style_11.rasi
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Author : Aditya Shakya
|
||||||
|
* Mail : adi1090x@gmail.com
|
||||||
|
* Github : @adi1090x
|
||||||
|
* Twitter : @adi1090x
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
configuration {
|
||||||
|
font: "Iosevka Nerd Font 10";
|
||||||
|
show-icons: true;
|
||||||
|
icon-theme: "Papirus";
|
||||||
|
display-drun: "Applications";
|
||||||
|
drun-display-format: "{name}";
|
||||||
|
disable-history: false;
|
||||||
|
sidebar-mode: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@import "colors.rasi"
|
||||||
|
|
||||||
|
window {
|
||||||
|
transparency: "real";
|
||||||
|
background-color: @bg;
|
||||||
|
text-color: @fg;
|
||||||
|
border: 0px;
|
||||||
|
border-color: @ac;
|
||||||
|
border-radius: 25px;
|
||||||
|
width: 50%;
|
||||||
|
location: center;
|
||||||
|
x-offset: 0;
|
||||||
|
y-offset: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt {
|
||||||
|
enabled: true;
|
||||||
|
padding: 1.25% 0.75% 1.25% 0.75%;
|
||||||
|
background-color: @ac;
|
||||||
|
text-color: @fg;
|
||||||
|
font: "Iosevka Nerd Font 12";
|
||||||
|
border-radius: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
textbox-prompt-colon {
|
||||||
|
padding: 1.40% 0% 1% 0%;
|
||||||
|
background-color: @se;
|
||||||
|
text-color: @fg;
|
||||||
|
expand: false;
|
||||||
|
str: " :: ";
|
||||||
|
}
|
||||||
|
|
||||||
|
entry {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: @fg;
|
||||||
|
placeholder-color: @fg;
|
||||||
|
expand: true;
|
||||||
|
horizontal-align: 0;
|
||||||
|
placeholder: "Search";
|
||||||
|
padding: 1.5% 0.5% 1% 0%;
|
||||||
|
blink: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputbar {
|
||||||
|
children: [ prompt, textbox-prompt-colon, entry ];
|
||||||
|
background-color: @se;
|
||||||
|
text-color: @fg;
|
||||||
|
expand: false;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 100px;
|
||||||
|
border-color: @ac;
|
||||||
|
}
|
||||||
|
|
||||||
|
listview {
|
||||||
|
background-color: @al;
|
||||||
|
padding: 0px;
|
||||||
|
columns: 3;
|
||||||
|
lines: 8;
|
||||||
|
spacing: 1%;
|
||||||
|
cycle: false;
|
||||||
|
dynamic: true;
|
||||||
|
layout: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
mainbox {
|
||||||
|
background-color: @al;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 0% 0% 0% 0%;
|
||||||
|
border-color: @ac;
|
||||||
|
children: [ inputbar, listview ];
|
||||||
|
spacing: 2%;
|
||||||
|
padding: 4% 2% 4% 2%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element {
|
||||||
|
background-color: @bg;
|
||||||
|
text-color: @fg;
|
||||||
|
orientation: horizontal;
|
||||||
|
border-radius: 0%;
|
||||||
|
padding: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element-icon {
|
||||||
|
background-color: inherit;
|
||||||
|
text-color: inherit;
|
||||||
|
horizontal-align: 0.5;
|
||||||
|
vertical-align: 0.5;
|
||||||
|
size: 24px;
|
||||||
|
border: 1%;
|
||||||
|
border-color: @ac;
|
||||||
|
border-radius: 15px;
|
||||||
|
background-color: @ac;
|
||||||
|
}
|
||||||
|
|
||||||
|
element-text {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: inherit;
|
||||||
|
expand: true;
|
||||||
|
horizontal-align: 0;
|
||||||
|
vertical-align: 0.5;
|
||||||
|
margin: 0% 0.25% 0% 0.25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element selected {
|
||||||
|
background-color: @se;
|
||||||
|
text-color: @ac;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 15px;
|
||||||
|
border-color: @ac;
|
||||||
|
}
|
||||||
132
dotfiles/config/rofi/colorful/style_12.rasi
Normal file
132
dotfiles/config/rofi/colorful/style_12.rasi
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Author : Aditya Shakya
|
||||||
|
* Mail : adi1090x@gmail.com
|
||||||
|
* Github : @adi1090x
|
||||||
|
* Twitter : @adi1090x
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
configuration {
|
||||||
|
font: "Iosevka Nerd Font 10";
|
||||||
|
show-icons: true;
|
||||||
|
icon-theme: "Papirus";
|
||||||
|
display-drun: " Applications";
|
||||||
|
drun-display-format: "{name}";
|
||||||
|
disable-history: false;
|
||||||
|
sidebar-mode: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@import "colors.rasi"
|
||||||
|
|
||||||
|
window {
|
||||||
|
transparency: "real";
|
||||||
|
background-color: @bg;
|
||||||
|
text-color: @fg;
|
||||||
|
border: 0px;
|
||||||
|
border-color: @ac;
|
||||||
|
border-radius: 50px;
|
||||||
|
width: 50%;
|
||||||
|
location: center;
|
||||||
|
x-offset: 0;
|
||||||
|
y-offset: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt {
|
||||||
|
enabled: true;
|
||||||
|
padding: 1.25% 0.75% 1.25% 0.75%;
|
||||||
|
background-color: @ac;
|
||||||
|
text-color: @fg;
|
||||||
|
font: "Iosevka Nerd Font 12";
|
||||||
|
border-radius: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
textbox-prompt-colon {
|
||||||
|
padding: 1.40% 0% 1% 0%;
|
||||||
|
background-color: @se;
|
||||||
|
text-color: @fg;
|
||||||
|
expand: false;
|
||||||
|
str: " :: ";
|
||||||
|
}
|
||||||
|
|
||||||
|
entry {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: @fg;
|
||||||
|
placeholder-color: @fg;
|
||||||
|
expand: true;
|
||||||
|
horizontal-align: 0;
|
||||||
|
placeholder: "Search";
|
||||||
|
padding: 1.5% 0.5% 1% 0%;
|
||||||
|
blink: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputbar {
|
||||||
|
children: [ prompt, textbox-prompt-colon, entry ];
|
||||||
|
background-color: @se;
|
||||||
|
text-color: @fg;
|
||||||
|
expand: false;
|
||||||
|
border: 0%;
|
||||||
|
border-radius: 100%;
|
||||||
|
border-color: @ac;
|
||||||
|
}
|
||||||
|
|
||||||
|
listview {
|
||||||
|
background-color: @al;
|
||||||
|
padding: 0px;
|
||||||
|
columns: 6;
|
||||||
|
lines: 3;
|
||||||
|
spacing: 1%;
|
||||||
|
cycle: false;
|
||||||
|
dynamic: true;
|
||||||
|
layout: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
mainbox {
|
||||||
|
background-color: @al;
|
||||||
|
border: 10px 0px 10px 0px;
|
||||||
|
border-radius: 50px;
|
||||||
|
border-color: @ac;
|
||||||
|
children: [ inputbar, listview ];
|
||||||
|
spacing: 2%;
|
||||||
|
padding: 4% 2% 2% 2%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element {
|
||||||
|
background-color: @bg;
|
||||||
|
text-color: @fg;
|
||||||
|
orientation: vertical;
|
||||||
|
border-radius: 0%;
|
||||||
|
padding: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element-icon {
|
||||||
|
background-color: inherit;
|
||||||
|
text-color: inherit;
|
||||||
|
horizontal-align: 0.5;
|
||||||
|
vertical-align: 0.5;
|
||||||
|
size: 64px;
|
||||||
|
border: 1%;
|
||||||
|
border-color: @se;
|
||||||
|
border-radius: 15px;
|
||||||
|
background-color: @se;
|
||||||
|
padding: 2% 1% 2% 1%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element-text {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: inherit;
|
||||||
|
expand: true;
|
||||||
|
horizontal-align: 0.5;
|
||||||
|
vertical-align: 0.5;
|
||||||
|
margin: 0.5% 0.25% 0.5% 0.25%;
|
||||||
|
padding: 1% 0.5% 1% 0.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element-text selected {
|
||||||
|
expand: true;
|
||||||
|
horizontal-align: 0.5;
|
||||||
|
vertical-align: 0.5;
|
||||||
|
background-color: @ac;
|
||||||
|
text-color: @bg;
|
||||||
|
border-radius: 100%;
|
||||||
|
}
|
||||||
119
dotfiles/config/rofi/colorful/style_2.rasi
Normal file
119
dotfiles/config/rofi/colorful/style_2.rasi
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Author : Aditya Shakya
|
||||||
|
* Mail : adi1090x@gmail.com
|
||||||
|
* Github : @adi1090x
|
||||||
|
* Twitter : @adi1090x
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
configuration {
|
||||||
|
font: "Iosevka Nerd Font 10";
|
||||||
|
show-icons: true;
|
||||||
|
icon-theme: "Papirus";
|
||||||
|
display-drun: "";
|
||||||
|
drun-display-format: "{name}";
|
||||||
|
disable-history: false;
|
||||||
|
sidebar-mode: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@import "colors.rasi"
|
||||||
|
|
||||||
|
window {
|
||||||
|
transparency: "real";
|
||||||
|
background-color: @bg;
|
||||||
|
text-color: @fg;
|
||||||
|
border: 0px;
|
||||||
|
border-color: @ac;
|
||||||
|
border-radius: 12px;
|
||||||
|
width: 18%;
|
||||||
|
location: center;
|
||||||
|
x-offset: 0;
|
||||||
|
y-offset: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt {
|
||||||
|
enabled: true;
|
||||||
|
padding: 0.30% 1% 0% -0.5%;
|
||||||
|
background-color: @al;
|
||||||
|
text-color: @bg;
|
||||||
|
font: "FantasqueSansMono Nerd Font 12";
|
||||||
|
}
|
||||||
|
|
||||||
|
entry {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: @bg;
|
||||||
|
placeholder-color: @bg;
|
||||||
|
expand: true;
|
||||||
|
horizontal-align: 0;
|
||||||
|
placeholder: "Search";
|
||||||
|
padding: 0.10% 0% 0% 0%;
|
||||||
|
blink: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputbar {
|
||||||
|
children: [ prompt, entry ];
|
||||||
|
background-color: @ac;
|
||||||
|
text-color: @bg;
|
||||||
|
expand: false;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 0px;
|
||||||
|
border-color: @ac;
|
||||||
|
margin: 0% 0% 0% 0%;
|
||||||
|
padding: 1.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
listview {
|
||||||
|
background-color: @al;
|
||||||
|
padding: 0px;
|
||||||
|
columns: 1;
|
||||||
|
lines: 5;
|
||||||
|
spacing: 0%;
|
||||||
|
cycle: false;
|
||||||
|
dynamic: true;
|
||||||
|
layout: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
mainbox {
|
||||||
|
background-color: @al;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 0% 0% 0% 0%;
|
||||||
|
border-color: @ac;
|
||||||
|
children: [ inputbar, listview ];
|
||||||
|
spacing: 0%;
|
||||||
|
padding: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: @fg;
|
||||||
|
orientation: horizontal;
|
||||||
|
border-radius: 0%;
|
||||||
|
padding: 1% 0.5% 1% 0.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element-icon {
|
||||||
|
background-color: inherit;
|
||||||
|
text-color: inherit;
|
||||||
|
horizontal-align: 0.5;
|
||||||
|
vertical-align: 0.5;
|
||||||
|
size: 32px;
|
||||||
|
border: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
element-text {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: inherit;
|
||||||
|
expand: true;
|
||||||
|
horizontal-align: 0;
|
||||||
|
vertical-align: 0.5;
|
||||||
|
margin: 0% 0.25% 0% 0.25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element selected {
|
||||||
|
background-color: @se;
|
||||||
|
text-color: @fg;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 0px;
|
||||||
|
border-color: @bg;
|
||||||
|
}
|
||||||
120
dotfiles/config/rofi/colorful/style_3.rasi
Normal file
120
dotfiles/config/rofi/colorful/style_3.rasi
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Author : Aditya Shakya
|
||||||
|
* Mail : adi1090x@gmail.com
|
||||||
|
* Github : @adi1090x
|
||||||
|
* Twitter : @adi1090x
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
configuration {
|
||||||
|
font: "Iosevka Nerd Font 10";
|
||||||
|
show-icons: true;
|
||||||
|
icon-theme: "Papirus";
|
||||||
|
display-drun: "";
|
||||||
|
drun-display-format: "{name}";
|
||||||
|
disable-history: false;
|
||||||
|
sidebar-mode: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@import "colors.rasi"
|
||||||
|
|
||||||
|
window {
|
||||||
|
transparency: "real";
|
||||||
|
background-color: @bg;
|
||||||
|
text-color: @fg;
|
||||||
|
border: 0px;
|
||||||
|
border-color: @ac;
|
||||||
|
border-radius: 0px;
|
||||||
|
height: 100%;
|
||||||
|
width: 18%;
|
||||||
|
location: west;
|
||||||
|
x-offset: 0;
|
||||||
|
y-offset: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt {
|
||||||
|
enabled: true;
|
||||||
|
padding: 0.30% 1% 0% -0.5%;
|
||||||
|
background-color: @al;
|
||||||
|
text-color: @bg;
|
||||||
|
font: "FantasqueSansMono Nerd Font 12";
|
||||||
|
}
|
||||||
|
|
||||||
|
entry {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: @bg;
|
||||||
|
placeholder-color: @bg;
|
||||||
|
expand: true;
|
||||||
|
horizontal-align: 0;
|
||||||
|
placeholder: "Search";
|
||||||
|
padding: 0.10% 0% 0% 0%;
|
||||||
|
blink: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputbar {
|
||||||
|
children: [ prompt, entry ];
|
||||||
|
background-color: @ac;
|
||||||
|
text-color: @bg;
|
||||||
|
expand: false;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 0px;
|
||||||
|
border-color: @ac;
|
||||||
|
margin: 0% 0% 0% 0%;
|
||||||
|
padding: 1.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
listview {
|
||||||
|
background-color: @al;
|
||||||
|
padding: 0px;
|
||||||
|
columns: 1;
|
||||||
|
lines: 5;
|
||||||
|
spacing: 0%;
|
||||||
|
cycle: false;
|
||||||
|
dynamic: true;
|
||||||
|
layout: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
mainbox {
|
||||||
|
background-color: @al;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 0% 0% 0% 0%;
|
||||||
|
border-color: @ac;
|
||||||
|
children: [ inputbar, listview ];
|
||||||
|
spacing: 0%;
|
||||||
|
padding: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: @fg;
|
||||||
|
orientation: horizontal;
|
||||||
|
border-radius: 0%;
|
||||||
|
padding: 1% 0.5% 1% 0.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element-icon {
|
||||||
|
background-color: inherit;
|
||||||
|
text-color: inherit;
|
||||||
|
horizontal-align: 0.5;
|
||||||
|
vertical-align: 0.5;
|
||||||
|
size: 32px;
|
||||||
|
border: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
element-text {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: inherit;
|
||||||
|
expand: true;
|
||||||
|
horizontal-align: 0;
|
||||||
|
vertical-align: 0.5;
|
||||||
|
margin: 0% 0.25% 0% 0.25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element selected {
|
||||||
|
background-color: @se;
|
||||||
|
text-color: @fg;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 0px;
|
||||||
|
border-color: @bg;
|
||||||
|
}
|
||||||
119
dotfiles/config/rofi/colorful/style_4.rasi
Normal file
119
dotfiles/config/rofi/colorful/style_4.rasi
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Author : Aditya Shakya
|
||||||
|
* Mail : adi1090x@gmail.com
|
||||||
|
* Github : @adi1090x
|
||||||
|
* Twitter : @adi1090x
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
configuration {
|
||||||
|
font: "Iosevka Nerd Font 10";
|
||||||
|
show-icons: true;
|
||||||
|
icon-theme: "Papirus";
|
||||||
|
display-drun: "";
|
||||||
|
drun-display-format: "{name}";
|
||||||
|
disable-history: false;
|
||||||
|
sidebar-mode: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@import "colors.rasi"
|
||||||
|
|
||||||
|
window {
|
||||||
|
transparency: "real";
|
||||||
|
background-color: @bg;
|
||||||
|
text-color: @fg;
|
||||||
|
border: 0px;
|
||||||
|
border-color: @ac;
|
||||||
|
border-radius: 0px;
|
||||||
|
height: 100%;
|
||||||
|
width: 19%;
|
||||||
|
location: east;
|
||||||
|
x-offset: 0;
|
||||||
|
y-offset: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt {
|
||||||
|
enabled: true;
|
||||||
|
padding: 0.30% 1% 0% -0.5%;
|
||||||
|
background-color: @al;
|
||||||
|
text-color: @bg;
|
||||||
|
font: "FantasqueSansMono Nerd Font 12";
|
||||||
|
}
|
||||||
|
|
||||||
|
entry {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: @bg;
|
||||||
|
placeholder-color: @bg;
|
||||||
|
expand: true;
|
||||||
|
horizontal-align: 0;
|
||||||
|
placeholder: "Search";
|
||||||
|
padding: 0.10% 0% 0% 0%;
|
||||||
|
blink: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputbar {
|
||||||
|
children: [ prompt, entry ];
|
||||||
|
background-color: @ac;
|
||||||
|
text-color: @bg;
|
||||||
|
expand: false;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 0px;
|
||||||
|
border-color: @ac;
|
||||||
|
margin: 0% 0% 0% 0%;
|
||||||
|
padding: 1.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
listview {
|
||||||
|
background-color: @al;
|
||||||
|
padding: 10px 10px 0px 10px;
|
||||||
|
columns: 3;
|
||||||
|
spacing: 0%;
|
||||||
|
cycle: false;
|
||||||
|
dynamic: true;
|
||||||
|
layout: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
mainbox {
|
||||||
|
background-color: @al;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 0% 0% 0% 0%;
|
||||||
|
border-color: @ac;
|
||||||
|
children: [ inputbar, listview ];
|
||||||
|
spacing: 0%;
|
||||||
|
padding: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: @fg;
|
||||||
|
orientation: vertical;
|
||||||
|
border-radius: 0%;
|
||||||
|
padding: 2% 0% 2% 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element-icon {
|
||||||
|
background-color: inherit;
|
||||||
|
text-color: inherit;
|
||||||
|
horizontal-align: 0.5;
|
||||||
|
vertical-align: 0.5;
|
||||||
|
size: 48px;
|
||||||
|
border: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
element-text {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: inherit;
|
||||||
|
expand: true;
|
||||||
|
horizontal-align: 0.5;
|
||||||
|
vertical-align: 0.5;
|
||||||
|
margin: 0.5% 0.5% -0.5% 0.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element selected {
|
||||||
|
background-color: @se;
|
||||||
|
text-color: @fg;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 0px;
|
||||||
|
border-color: @bg;
|
||||||
|
}
|
||||||
119
dotfiles/config/rofi/colorful/style_5.rasi
Normal file
119
dotfiles/config/rofi/colorful/style_5.rasi
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Author : Aditya Shakya
|
||||||
|
* Mail : adi1090x@gmail.com
|
||||||
|
* Github : @adi1090x
|
||||||
|
* Twitter : @adi1090x
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
configuration {
|
||||||
|
font: "Iosevka Nerd Font 10";
|
||||||
|
show-icons: true;
|
||||||
|
icon-theme: "Papirus";
|
||||||
|
display-drun: "";
|
||||||
|
drun-display-format: "{name}";
|
||||||
|
disable-history: false;
|
||||||
|
sidebar-mode: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@import "colors.rasi"
|
||||||
|
|
||||||
|
window {
|
||||||
|
transparency: "real";
|
||||||
|
background-color: @bg;
|
||||||
|
text-color: @fg;
|
||||||
|
border: 0px;
|
||||||
|
border-color: @ac;
|
||||||
|
border-radius: 0px;
|
||||||
|
width: 35%;
|
||||||
|
location: center;
|
||||||
|
x-offset: 0;
|
||||||
|
y-offset: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt {
|
||||||
|
enabled: true;
|
||||||
|
padding: 0.30% 1% 0% -0.5%;
|
||||||
|
background-color: @al;
|
||||||
|
text-color: @bg;
|
||||||
|
font: "FantasqueSansMono Nerd Font 12";
|
||||||
|
}
|
||||||
|
|
||||||
|
entry {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: @bg;
|
||||||
|
placeholder-color: @bg;
|
||||||
|
expand: true;
|
||||||
|
horizontal-align: 0;
|
||||||
|
placeholder: "Search";
|
||||||
|
padding: 0.10% 0% 0% 0%;
|
||||||
|
blink: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputbar {
|
||||||
|
children: [ prompt, entry ];
|
||||||
|
background-color: @fg;
|
||||||
|
text-color: @bg;
|
||||||
|
expand: false;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 0px;
|
||||||
|
border-color: @ac;
|
||||||
|
margin: 0% 0% 0% 0%;
|
||||||
|
padding: 1.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
listview {
|
||||||
|
background-color: @al;
|
||||||
|
padding: 10px;
|
||||||
|
columns: 2;
|
||||||
|
lines: 10;
|
||||||
|
spacing: 0%;
|
||||||
|
cycle: false;
|
||||||
|
dynamic: true;
|
||||||
|
layout: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
mainbox {
|
||||||
|
background-color: @al;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 0% 0% 0% 0%;
|
||||||
|
border-color: @ac;
|
||||||
|
children: [ inputbar, listview ];
|
||||||
|
spacing: 0%;
|
||||||
|
padding: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: @fg;
|
||||||
|
orientation: horizontal;
|
||||||
|
border-radius: 0%;
|
||||||
|
padding: 1% 0.5% 1% 0.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element-icon {
|
||||||
|
background-color: inherit;
|
||||||
|
text-color: inherit;
|
||||||
|
horizontal-align: 0.5;
|
||||||
|
vertical-align: 0.5;
|
||||||
|
size: 24px;
|
||||||
|
border: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
element-text {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: inherit;
|
||||||
|
expand: true;
|
||||||
|
horizontal-align: 0;
|
||||||
|
vertical-align: 0.5;
|
||||||
|
margin: 0% 0.25% 0% 0.25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element selected {
|
||||||
|
background-color: @ac;
|
||||||
|
text-color: @bg;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 0px;
|
||||||
|
border-color: @bg;
|
||||||
|
}
|
||||||
116
dotfiles/config/rofi/colorful/style_6.rasi
Normal file
116
dotfiles/config/rofi/colorful/style_6.rasi
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Author : Aditya Shakya
|
||||||
|
* Mail : adi1090x@gmail.com
|
||||||
|
* Github : @adi1090x
|
||||||
|
* Twitter : @adi1090x
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
configuration {
|
||||||
|
font: "Iosevka Nerd Font 10";
|
||||||
|
show-icons: true;
|
||||||
|
icon-theme: "Papirus";
|
||||||
|
display-drun: "";
|
||||||
|
drun-display-format: "{name}";
|
||||||
|
disable-history: false;
|
||||||
|
sidebar-mode: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@import "colors.rasi"
|
||||||
|
|
||||||
|
window {
|
||||||
|
transparency: "real";
|
||||||
|
background-color: @bg;
|
||||||
|
text-color: @fg;
|
||||||
|
border: 0px;
|
||||||
|
border-color: @ac;
|
||||||
|
border-radius: 0px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt {
|
||||||
|
enabled: true;
|
||||||
|
padding: 0.30% 1% 0% -0.5%;
|
||||||
|
background-color: @al;
|
||||||
|
text-color: @bg;
|
||||||
|
font: "FantasqueSansMono Nerd Font 12";
|
||||||
|
}
|
||||||
|
|
||||||
|
entry {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: @bg;
|
||||||
|
placeholder-color: @bg;
|
||||||
|
expand: true;
|
||||||
|
horizontal-align: 0;
|
||||||
|
placeholder: "Search";
|
||||||
|
padding: 0.10% 0% 0% 0%;
|
||||||
|
blink: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputbar {
|
||||||
|
children: [ prompt, entry ];
|
||||||
|
background-color: @ac;
|
||||||
|
text-color: @bg;
|
||||||
|
expand: false;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 100%;
|
||||||
|
border-color: @ac;
|
||||||
|
margin: 0% 54.5% 0% 0%;
|
||||||
|
padding: 1.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
listview {
|
||||||
|
background-color: @al;
|
||||||
|
padding: 0px;
|
||||||
|
columns: 10;
|
||||||
|
spacing: 0%;
|
||||||
|
cycle: false;
|
||||||
|
dynamic: true;
|
||||||
|
layout: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
mainbox {
|
||||||
|
background-color: @al;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 0% 0% 0% 0%;
|
||||||
|
border-color: @ac;
|
||||||
|
children: [ inputbar, listview ];
|
||||||
|
spacing: 2.5%;
|
||||||
|
padding: 20% 5% 20% 5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: @fg;
|
||||||
|
orientation: vertical;
|
||||||
|
border-radius: 0%;
|
||||||
|
padding: 4% 0% 4% 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element-icon {
|
||||||
|
background-color: inherit;
|
||||||
|
text-color: inherit;
|
||||||
|
horizontal-align: 0.5;
|
||||||
|
vertical-align: 0.5;
|
||||||
|
size: 80px;
|
||||||
|
border: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
element-text {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: inherit;
|
||||||
|
expand: true;
|
||||||
|
horizontal-align: 0.5;
|
||||||
|
vertical-align: 0.5;
|
||||||
|
margin: 0.5% 0.5% -0.5% 0.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element selected {
|
||||||
|
background-color: @se;
|
||||||
|
text-color: @fg;
|
||||||
|
border: 0% 0% 0.5% 0%;
|
||||||
|
border-radius: 25px;
|
||||||
|
border-color: @ac;
|
||||||
|
}
|
||||||
118
dotfiles/config/rofi/colorful/style_7.rasi
Normal file
118
dotfiles/config/rofi/colorful/style_7.rasi
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Author : Aditya Shakya
|
||||||
|
* Mail : adi1090x@gmail.com
|
||||||
|
* Github : @adi1090x
|
||||||
|
* Twitter : @adi1090x
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
configuration {
|
||||||
|
font: "Fira Code 10";
|
||||||
|
show-icons: true;
|
||||||
|
display-drun: "";
|
||||||
|
drun-display-format: "{name} {generic}";
|
||||||
|
disable-history: false;
|
||||||
|
sidebar-mode: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@import "colors.rasi"
|
||||||
|
|
||||||
|
window {
|
||||||
|
transparency: "real";
|
||||||
|
background-color: @bg;
|
||||||
|
text-color: @fg;
|
||||||
|
border: 0px;
|
||||||
|
border-color: @ac;
|
||||||
|
border-radius: 12px;
|
||||||
|
width: 88%;
|
||||||
|
height: 78%;
|
||||||
|
location: center;
|
||||||
|
x-offset: 0;
|
||||||
|
y-offset: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt {
|
||||||
|
enabled: true;
|
||||||
|
padding: 0.30% 1% 0% -0.5%;
|
||||||
|
background-color: @al;
|
||||||
|
text-color: @fg;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: @fg;
|
||||||
|
placeholder-color: @fg;
|
||||||
|
expand: true;
|
||||||
|
horizontal-align: 0;
|
||||||
|
placeholder: "Search";
|
||||||
|
padding: 0.10% 0% 0% 0%;
|
||||||
|
blink: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputbar {
|
||||||
|
children: [ prompt, entry ];
|
||||||
|
background-color: @bg;
|
||||||
|
text-color: @fg;
|
||||||
|
expand: false;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 0px;
|
||||||
|
border-color: @ac;
|
||||||
|
margin: 0% 0% 0% 0%;
|
||||||
|
padding: 1.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
listview {
|
||||||
|
background-color: @al;
|
||||||
|
padding: 10px;
|
||||||
|
columns: 1;
|
||||||
|
lines: 18;
|
||||||
|
spacing: 1%;
|
||||||
|
cycle: false;
|
||||||
|
dynamic: true;
|
||||||
|
layout: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
mainbox {
|
||||||
|
background-color: @al;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 0% 0% 0% 0%;
|
||||||
|
border-color: @ac;
|
||||||
|
children: [ inputbar, listview ];
|
||||||
|
spacing: 0%;
|
||||||
|
padding: 0%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: @fg;
|
||||||
|
orientation: horizontal;
|
||||||
|
border-radius: 0%;
|
||||||
|
padding: 0.5% 0.5% 0.5% 0.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element-icon {
|
||||||
|
background-color: inherit;
|
||||||
|
text-color: inherit;
|
||||||
|
horizontal-align: 0.5;
|
||||||
|
vertical-align: 0.5;
|
||||||
|
size: 24px;
|
||||||
|
border: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
element-text {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: inherit;
|
||||||
|
expand: true;
|
||||||
|
horizontal-align: 0;
|
||||||
|
vertical-align: 0.5;
|
||||||
|
margin: 0% 0.25% 0% 0.25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element selected {
|
||||||
|
background-color: @ac;
|
||||||
|
text-color: @bg;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 12px;
|
||||||
|
border-color: @bg;
|
||||||
|
}
|
||||||
125
dotfiles/config/rofi/colorful/style_8.rasi
Normal file
125
dotfiles/config/rofi/colorful/style_8.rasi
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Author : Aditya Shakya
|
||||||
|
* Mail : adi1090x@gmail.com
|
||||||
|
* Github : @adi1090x
|
||||||
|
* Twitter : @adi1090x
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
configuration {
|
||||||
|
font: "Iosevka Nerd Font 10";
|
||||||
|
show-icons: true;
|
||||||
|
icon-theme: "Papirus";
|
||||||
|
display-drun: "Applications";
|
||||||
|
drun-display-format: "{name}";
|
||||||
|
disable-history: false;
|
||||||
|
sidebar-mode: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@import "colors.rasi"
|
||||||
|
|
||||||
|
window {
|
||||||
|
transparency: "real";
|
||||||
|
background-color: @bg;
|
||||||
|
text-color: @fg;
|
||||||
|
border: 0px;
|
||||||
|
border-color: @ac;
|
||||||
|
border-radius: 0px;
|
||||||
|
width: 35%;
|
||||||
|
location: center;
|
||||||
|
x-offset: 0;
|
||||||
|
y-offset: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt {
|
||||||
|
enabled: true;
|
||||||
|
padding: 1% 0.75% 1% 0.75%;
|
||||||
|
background-color: @ac;
|
||||||
|
text-color: @fg;
|
||||||
|
font: "Iosevka Nerd Font 12";
|
||||||
|
}
|
||||||
|
|
||||||
|
textbox-prompt-colon {
|
||||||
|
padding: 1% 0% 1% 0%;
|
||||||
|
background-color: @se;
|
||||||
|
text-color: @fg;
|
||||||
|
expand: false;
|
||||||
|
str: " :: ";
|
||||||
|
}
|
||||||
|
|
||||||
|
entry {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: @fg;
|
||||||
|
placeholder-color: @fg;
|
||||||
|
expand: true;
|
||||||
|
horizontal-align: 0;
|
||||||
|
placeholder: "Search...";
|
||||||
|
padding: 1.15% 0.5% 1% 0.5%;
|
||||||
|
blink: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputbar {
|
||||||
|
children: [ prompt, entry ];
|
||||||
|
background-color: @se;
|
||||||
|
text-color: @fg;
|
||||||
|
expand: false;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 0px;
|
||||||
|
border-color: @ac;
|
||||||
|
}
|
||||||
|
|
||||||
|
listview {
|
||||||
|
background-color: @al;
|
||||||
|
padding: 0px;
|
||||||
|
columns: 2;
|
||||||
|
lines: 7;
|
||||||
|
spacing: 1%;
|
||||||
|
cycle: false;
|
||||||
|
dynamic: true;
|
||||||
|
layout: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
mainbox {
|
||||||
|
background-color: @al;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 0% 0% 0% 0%;
|
||||||
|
border-color: @ac;
|
||||||
|
children: [ inputbar, listview ];
|
||||||
|
spacing: 2%;
|
||||||
|
padding: 4% 2% 4% 2%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element {
|
||||||
|
background-color: @se;
|
||||||
|
text-color: @fg;
|
||||||
|
orientation: horizontal;
|
||||||
|
border-radius: 0%;
|
||||||
|
padding: 1% 0.5% 1% 0.75%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element-icon {
|
||||||
|
background-color: inherit;
|
||||||
|
text-color: inherit;
|
||||||
|
horizontal-align: 0.5;
|
||||||
|
vertical-align: 0.5;
|
||||||
|
size: 24px;
|
||||||
|
border: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
element-text {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: inherit;
|
||||||
|
expand: true;
|
||||||
|
horizontal-align: 0;
|
||||||
|
vertical-align: 0.5;
|
||||||
|
margin: 0% 0.25% 0% 0.25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element selected {
|
||||||
|
background-color: @se;
|
||||||
|
text-color: @ac;
|
||||||
|
border: 0% 0% 0% 0.3%;
|
||||||
|
border-radius: 0px;
|
||||||
|
border-color: @ac;
|
||||||
|
}
|
||||||
126
dotfiles/config/rofi/colorful/style_9.rasi
Normal file
126
dotfiles/config/rofi/colorful/style_9.rasi
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* Author : Aditya Shakya
|
||||||
|
* Mail : adi1090x@gmail.com
|
||||||
|
* Github : @adi1090x
|
||||||
|
* Twitter : @adi1090x
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
configuration {
|
||||||
|
font: "Iosevka Nerd Font 10";
|
||||||
|
show-icons: true;
|
||||||
|
icon-theme: "Papirus";
|
||||||
|
display-drun: "Applications";
|
||||||
|
drun-display-format: "{name}";
|
||||||
|
disable-history: false;
|
||||||
|
sidebar-mode: false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@import "colors.rasi"
|
||||||
|
|
||||||
|
window {
|
||||||
|
transparency: "real";
|
||||||
|
background-color: @bg;
|
||||||
|
text-color: @fg;
|
||||||
|
border: 0px;
|
||||||
|
border-color: @ac;
|
||||||
|
border-radius: 15px;
|
||||||
|
width: 35%;
|
||||||
|
location: center;
|
||||||
|
x-offset: 0;
|
||||||
|
y-offset: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt {
|
||||||
|
enabled: true;
|
||||||
|
padding: 1% 0.75% 1% 0.75%;
|
||||||
|
background-color: @ac;
|
||||||
|
text-color: @fg;
|
||||||
|
border-radius: 10px;
|
||||||
|
font: "Iosevka Nerd Font 12";
|
||||||
|
}
|
||||||
|
|
||||||
|
textbox-prompt-colon {
|
||||||
|
padding: 1% 0% 1% 0%;
|
||||||
|
background-color: @se;
|
||||||
|
text-color: @fg;
|
||||||
|
expand: false;
|
||||||
|
str: " :: ";
|
||||||
|
}
|
||||||
|
|
||||||
|
entry {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: @fg;
|
||||||
|
placeholder-color: @fg;
|
||||||
|
expand: true;
|
||||||
|
horizontal-align: 0;
|
||||||
|
placeholder: "Search...";
|
||||||
|
padding: 1.15% 0.5% 1% 0.5%;
|
||||||
|
blink: true;
|
||||||
|
}
|
||||||
|
|
||||||
|
inputbar {
|
||||||
|
children: [ prompt, entry ];
|
||||||
|
background-color: @se;
|
||||||
|
text-color: @fg;
|
||||||
|
expand: false;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 10px;
|
||||||
|
border-color: @ac;
|
||||||
|
}
|
||||||
|
|
||||||
|
listview {
|
||||||
|
background-color: @al;
|
||||||
|
padding: 0px;
|
||||||
|
columns: 2;
|
||||||
|
lines: 7;
|
||||||
|
spacing: 1%;
|
||||||
|
cycle: false;
|
||||||
|
dynamic: true;
|
||||||
|
layout: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
mainbox {
|
||||||
|
background-color: @al;
|
||||||
|
border: 0% 0% 0% 0%;
|
||||||
|
border-radius: 0% 0% 0% 0%;
|
||||||
|
border-color: @ac;
|
||||||
|
children: [ inputbar, listview ];
|
||||||
|
spacing: 2%;
|
||||||
|
padding: 4% 2% 4% 2%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element {
|
||||||
|
background-color: @se;
|
||||||
|
text-color: @fg;
|
||||||
|
orientation: horizontal;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 1% 0.5% 1% 0.75%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element-icon {
|
||||||
|
background-color: inherit;
|
||||||
|
text-color: inherit;
|
||||||
|
horizontal-align: 0.5;
|
||||||
|
vertical-align: 0.5;
|
||||||
|
size: 24px;
|
||||||
|
border: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
element-text {
|
||||||
|
background-color: @al;
|
||||||
|
text-color: inherit;
|
||||||
|
expand: true;
|
||||||
|
horizontal-align: 0;
|
||||||
|
vertical-align: 0.5;
|
||||||
|
margin: 0% 0.25% 0% 0.25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
element selected {
|
||||||
|
background-color: @se;
|
||||||
|
text-color: @ac;
|
||||||
|
border: 0% 0.3% 0% 0.3%;
|
||||||
|
border-radius: 12px;
|
||||||
|
border-color: @ac;
|
||||||
|
}
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
configuration {
|
configuration {
|
||||||
bw: 0;
|
bw: 0;
|
||||||
padding: 0;
|
padding: 50;
|
||||||
show-icons: true;
|
show-icons: true;
|
||||||
terminal: "alacritty";
|
terminal: "alacritty";
|
||||||
sidebar-mode: false;
|
sidebar-mode: false;
|
||||||
fullscreen: false;
|
fullscreen: true;
|
||||||
/* Let rofi auto-detect DPI under Wayland/Xwayland to avoid blurry scaling. */
|
/* Let rofi auto-detect DPI under Wayland/Xwayland to avoid blurry scaling. */
|
||||||
dpi: 0;
|
dpi: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@theme "apple-frost.rasi"
|
@theme "colorful/style_7.rasi"
|
||||||
|
|||||||
@@ -7,16 +7,14 @@
|
|||||||
## Multiplexer session titling
|
## Multiplexer session titling
|
||||||
- If the `TMUX` or `ZELLIJ` environment variable is set, treat this chat as the controller for the current tmux or zellij session.
|
- If the `TMUX` or `ZELLIJ` environment variable is set, treat this chat as the controller for the current tmux or zellij session.
|
||||||
- Use `set_multiplexer_title '<project> - <task>'` to update the title. The command detects tmux vs. zellij internally, prefers tmux when both are present, and no-ops outside a multiplexer.
|
- Use `set_multiplexer_title '<project> - <task>'` to update the title. The command detects tmux vs. zellij internally, prefers tmux when both are present, and no-ops outside a multiplexer.
|
||||||
- Maintain a session/window/pane title that describes the durable purpose of the overall exchange.
|
- Maintain a session/window/pane title that updates when the task focus changes substantially.
|
||||||
- Prefer automatic titling: infer a concise <task> from the current user request and the existing chat context without asking.
|
- Prefer automatic titling: infer a concise <task> from the current user request and context without asking.
|
||||||
- Choose holistic titles over granular turn summaries. The title should answer "what has this chat been for?" rather than describe the latest command, substep, clarification, or follow-up message.
|
|
||||||
- Preserve the existing <task> when the new user turn is a continuation, status check, refinement, or implementation detail within the same broader objective.
|
|
||||||
- Title format: "<project> - <task>".
|
- Title format: "<project> - <task>".
|
||||||
- <project> is the basename of the current project directory.
|
- <project> is the basename of the current project directory.
|
||||||
- Prefer git repo root basename if available; otherwise use basename of the current working directory.
|
- Prefer git repo root basename if available; otherwise use basename of the current working directory.
|
||||||
- <task> is a short, user-friendly description of what we are doing.
|
- <task> is a short, user-friendly description of what we are doing.
|
||||||
- Ask for a short descriptive <task> only when the task is ambiguous or you are not confident in an inferred title.
|
- Ask for a short descriptive <task> only when the task is ambiguous or you are not confident in an inferred title.
|
||||||
- When the broader objective changes substantially, update the <task> automatically if clear; otherwise ask for an updated <task>.
|
- When the task changes substantially, update the <task> automatically if clear; otherwise ask for an updated <task>.
|
||||||
- When a title is provided or updated, immediately run `set_multiplexer_title '<project> - <task>'`; do not call raw tmux or zellij rename commands unless debugging the helper itself.
|
- When a title is provided or updated, immediately run `set_multiplexer_title '<project> - <task>'`; do not call raw tmux or zellij rename commands unless debugging the helper itself.
|
||||||
|
|
||||||
## Pane usage
|
## Pane usage
|
||||||
|
|||||||
@@ -1,320 +0,0 @@
|
|||||||
{-# LANGUAGE LambdaCase #-}
|
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
|
||||||
{-# LANGUAGE TypeApplications #-}
|
|
||||||
|
|
||||||
module TaffybarConfig.ChromeFavicons
|
|
||||||
( ChromeFaviconOverlayMode (..),
|
|
||||||
ChromeFaviconConfig (..),
|
|
||||||
defaultChromeFaviconConfig,
|
|
||||||
chromeFaviconIconGetter,
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Control.Exception (IOException, try)
|
|
||||||
import Control.Monad (unless, when)
|
|
||||||
import Control.Monad.IO.Class (liftIO)
|
|
||||||
import Data.Char (isAlphaNum)
|
|
||||||
import Data.Int (Int32)
|
|
||||||
import Data.Maybe (fromMaybe, listToMaybe)
|
|
||||||
import Data.Text (Text)
|
|
||||||
import qualified Data.Text as T
|
|
||||||
import qualified GI.GdkPixbuf.Enums as GdkPixbuf
|
|
||||||
import qualified GI.GdkPixbuf.Objects.Pixbuf as Gdk
|
|
||||||
import System.Directory
|
|
||||||
( createDirectoryIfMissing,
|
|
||||||
doesFileExist,
|
|
||||||
getFileSize,
|
|
||||||
renameFile,
|
|
||||||
)
|
|
||||||
import System.Environment.XDG.BaseDir (getUserCacheDir)
|
|
||||||
import System.Exit (ExitCode (ExitSuccess))
|
|
||||||
import System.FilePath ((</>))
|
|
||||||
import System.Process (readProcessWithExitCode)
|
|
||||||
import qualified System.Taffybar.Information.Workspaces.Model as WorkspaceModel
|
|
||||||
import qualified System.Taffybar.Widget.Workspaces as Workspaces
|
|
||||||
import System.Taffybar.Widget.Util (loadPixbufByName)
|
|
||||||
|
|
||||||
data ChromeFaviconOverlayMode
|
|
||||||
= FaviconWithChromeOverlay
|
|
||||||
| ChromeWithFaviconOverlay
|
|
||||||
deriving (Eq, Show)
|
|
||||||
|
|
||||||
data ChromeFaviconConfig = ChromeFaviconConfig
|
|
||||||
{ chromeFaviconOverlayMode :: ChromeFaviconOverlayMode,
|
|
||||||
chromeFaviconOverlayRatio :: Double,
|
|
||||||
chromeFaviconEnabled :: Bool
|
|
||||||
}
|
|
||||||
deriving (Eq, Show)
|
|
||||||
|
|
||||||
defaultChromeFaviconConfig :: ChromeFaviconConfig
|
|
||||||
defaultChromeFaviconConfig =
|
|
||||||
ChromeFaviconConfig
|
|
||||||
{ chromeFaviconOverlayMode = FaviconWithChromeOverlay,
|
|
||||||
chromeFaviconOverlayRatio = 0.45,
|
|
||||||
chromeFaviconEnabled = True
|
|
||||||
}
|
|
||||||
|
|
||||||
data BridgePayload = BridgePayload
|
|
||||||
{ payloadMappedWindowId :: Text,
|
|
||||||
payloadFaviconURL :: Text
|
|
||||||
}
|
|
||||||
deriving (Eq, Show)
|
|
||||||
|
|
||||||
chromeFaviconIconGetter :: ChromeFaviconConfig -> Workspaces.WindowIconPixbufGetter
|
|
||||||
chromeFaviconIconGetter cfg =
|
|
||||||
Workspaces.handleIconGetterException $ \size windowInfo ->
|
|
||||||
if chromeFaviconEnabled cfg && isChromeWindow windowInfo
|
|
||||||
then liftIO $ chromeFaviconPixbuf cfg size windowInfo
|
|
||||||
else pure Nothing
|
|
||||||
|
|
||||||
chromeFaviconPixbuf ::
|
|
||||||
ChromeFaviconConfig ->
|
|
||||||
Int32 ->
|
|
||||||
WorkspaceModel.WindowInfo ->
|
|
||||||
IO (Maybe Gdk.Pixbuf)
|
|
||||||
chromeFaviconPixbuf cfg size windowInfo = do
|
|
||||||
payload <- getChromeWindowInfoPayload windowInfo
|
|
||||||
case payload of
|
|
||||||
Just p
|
|
||||||
| payloadMatchesWindow windowInfo p,
|
|
||||||
validFaviconURL (payloadFaviconURL p) -> do
|
|
||||||
mFavicon <- loadCachedFavicon size (payloadFaviconURL p)
|
|
||||||
mChrome <- runChromeIconGetter size windowInfo
|
|
||||||
case (mFavicon, mChrome) of
|
|
||||||
(Just favicon, Just chromeIcon) ->
|
|
||||||
Just <$> composeChromeFavicon cfg size favicon chromeIcon
|
|
||||||
(Just favicon, Nothing) -> Just <$> scalePixbuf size favicon
|
|
||||||
_ -> pure Nothing
|
|
||||||
_ -> pure Nothing
|
|
||||||
|
|
||||||
runChromeIconGetter :: Int32 -> WorkspaceModel.WindowInfo -> IO (Maybe Gdk.Pixbuf)
|
|
||||||
runChromeIconGetter size _ =
|
|
||||||
loadPixbufByName size "google-chrome"
|
|
||||||
|
|
||||||
payloadMatchesWindow :: WorkspaceModel.WindowInfo -> BridgePayload -> Bool
|
|
||||||
payloadMatchesWindow windowInfo payload =
|
|
||||||
normalizedHyprlandWindowId windowInfo == Just (normalizeAddress (payloadMappedWindowId payload))
|
|
||||||
|
|
||||||
normalizedHyprlandWindowId :: WorkspaceModel.WindowInfo -> Maybe Text
|
|
||||||
normalizedHyprlandWindowId windowInfo =
|
|
||||||
case WorkspaceModel.windowIdentity windowInfo of
|
|
||||||
WorkspaceModel.HyprlandWindowIdentity address -> Just (normalizeAddress address)
|
|
||||||
WorkspaceModel.X11WindowIdentity _ -> Nothing
|
|
||||||
|
|
||||||
isChromeWindow :: WorkspaceModel.WindowInfo -> Bool
|
|
||||||
isChromeWindow windowInfo =
|
|
||||||
any looksLikeChrome (WorkspaceModel.windowClassHints windowInfo)
|
|
||||||
|
|
||||||
looksLikeChrome :: Text -> Bool
|
|
||||||
looksLikeChrome raw =
|
|
||||||
let lowered = T.toLower raw
|
|
||||||
in any (`T.isInfixOf` lowered) ["chrome", "chromium", "brave", "edge", "vivaldi"]
|
|
||||||
|
|
||||||
normalizeAddress :: Text -> Text
|
|
||||||
normalizeAddress address =
|
|
||||||
let trimmed = T.strip address
|
|
||||||
in if "0x" `T.isPrefixOf` trimmed || T.null trimmed
|
|
||||||
then trimmed
|
|
||||||
else "0x" <> trimmed
|
|
||||||
|
|
||||||
validFaviconURL :: Text -> Bool
|
|
||||||
validFaviconURL url =
|
|
||||||
any (`T.isPrefixOf` url) ["https://", "http://"]
|
|
||||||
|
|
||||||
getChromeWindowInfoPayload :: WorkspaceModel.WindowInfo -> IO (Maybe BridgePayload)
|
|
||||||
getChromeWindowInfoPayload windowInfo =
|
|
||||||
case normalizedHyprlandWindowId windowInfo of
|
|
||||||
Just windowId -> do
|
|
||||||
payloads <- getBridgeString "GetWindowPayloads"
|
|
||||||
payload <- case payloads of
|
|
||||||
Just payloadText -> extractBridgePayloadForWindow windowId payloadText
|
|
||||||
Nothing -> pure Nothing
|
|
||||||
case payload of
|
|
||||||
Just value -> pure (Just value)
|
|
||||||
Nothing -> getLastChromeWindowInfoPayload
|
|
||||||
Nothing -> getLastChromeWindowInfoPayload
|
|
||||||
|
|
||||||
getLastChromeWindowInfoPayload :: IO (Maybe BridgePayload)
|
|
||||||
getLastChromeWindowInfoPayload = do
|
|
||||||
payload <- getBridgeString "GetLastPayload"
|
|
||||||
case payload of
|
|
||||||
Just payloadText -> extractBridgePayload payloadText
|
|
||||||
Nothing -> pure Nothing
|
|
||||||
|
|
||||||
getBridgeString :: String -> IO (Maybe String)
|
|
||||||
getBridgeString method = do
|
|
||||||
result <-
|
|
||||||
try @IOException $
|
|
||||||
readProcessWithExitCode
|
|
||||||
"busctl"
|
|
||||||
[ "--user",
|
|
||||||
"call",
|
|
||||||
"org.imalison.ChromeWindowInfo",
|
|
||||||
"/org/imalison/ChromeWindowInfo",
|
|
||||||
"org.imalison.ChromeWindowInfo",
|
|
||||||
method
|
|
||||||
]
|
|
||||||
""
|
|
||||||
case result of
|
|
||||||
Right (ExitSuccess, stdoutText, _) ->
|
|
||||||
pure (parseBusctlString stdoutText)
|
|
||||||
_ -> pure Nothing
|
|
||||||
|
|
||||||
parseBusctlString :: String -> Maybe String
|
|
||||||
parseBusctlString output = do
|
|
||||||
rest <- T.stripPrefix "s " (T.strip (T.pack output))
|
|
||||||
decodeQuotedString (T.unpack rest)
|
|
||||||
|
|
||||||
decodeQuotedString :: String -> Maybe String
|
|
||||||
decodeQuotedString raw =
|
|
||||||
case reads raw of
|
|
||||||
[(decoded, trailing)] | all (`elem` (" \n\t\r" :: String)) trailing -> Just decoded
|
|
||||||
_ -> Nothing
|
|
||||||
|
|
||||||
extractBridgePayload :: String -> IO (Maybe BridgePayload)
|
|
||||||
extractBridgePayload payload = do
|
|
||||||
let jqFilter = "[.bridge.mapped_window.window_id // \"\", .tab.favicon_url // \"\"] | @tsv"
|
|
||||||
(code, stdoutText, _) <- readProcessWithExitCode "jq" ["-r", jqFilter] payload
|
|
||||||
pure $
|
|
||||||
case (code, T.splitOn "\t" (T.strip (T.pack stdoutText))) of
|
|
||||||
(ExitSuccess, [mappedWindowId, faviconURL])
|
|
||||||
| not (T.null mappedWindowId),
|
|
||||||
not (T.null faviconURL) ->
|
|
||||||
Just (BridgePayload mappedWindowId faviconURL)
|
|
||||||
_ -> Nothing
|
|
||||||
|
|
||||||
extractBridgePayloadForWindow :: Text -> String -> IO (Maybe BridgePayload)
|
|
||||||
extractBridgePayloadForWindow windowId payloads = do
|
|
||||||
let jqFilter = ".[$window_id] // empty | [.bridge.mapped_window.window_id // \"\", .tab.favicon_url // \"\"] | @tsv"
|
|
||||||
(code, stdoutText, _) <-
|
|
||||||
readProcessWithExitCode
|
|
||||||
"jq"
|
|
||||||
["-r", "--arg", "window_id", T.unpack windowId, jqFilter]
|
|
||||||
payloads
|
|
||||||
pure $
|
|
||||||
case (code, T.splitOn "\t" (T.strip (T.pack stdoutText))) of
|
|
||||||
(ExitSuccess, [mappedWindowId, faviconURL])
|
|
||||||
| not (T.null mappedWindowId),
|
|
||||||
not (T.null faviconURL) ->
|
|
||||||
Just (BridgePayload mappedWindowId faviconURL)
|
|
||||||
_ -> Nothing
|
|
||||||
|
|
||||||
loadCachedFavicon :: Int32 -> Text -> IO (Maybe Gdk.Pixbuf)
|
|
||||||
loadCachedFavicon size url = do
|
|
||||||
path <- ensureCachedFavicon url
|
|
||||||
case path of
|
|
||||||
Just faviconPath ->
|
|
||||||
try @IOException (Gdk.pixbufNewFromFileAtScale faviconPath size size True) >>= \case
|
|
||||||
Right (Just pixbuf) -> pure (Just pixbuf)
|
|
||||||
Right Nothing -> pure Nothing
|
|
||||||
Left _ -> pure Nothing
|
|
||||||
Nothing -> pure Nothing
|
|
||||||
|
|
||||||
ensureCachedFavicon :: Text -> IO (Maybe FilePath)
|
|
||||||
ensureCachedFavicon url = do
|
|
||||||
cacheRoot <- getUserCacheDir "taffybar/chrome-favicons"
|
|
||||||
let rawDir = cacheRoot </> "raw"
|
|
||||||
createDirectoryIfMissing True rawDir
|
|
||||||
hash <- hashText url
|
|
||||||
let path = rawDir </> (hash <> faviconExtension url)
|
|
||||||
cached <- nonEmptyFileExists path
|
|
||||||
unless cached $
|
|
||||||
downloadFavicon url path
|
|
||||||
exists <- nonEmptyFileExists path
|
|
||||||
pure $ if exists then Just path else Nothing
|
|
||||||
|
|
||||||
hashText :: Text -> IO String
|
|
||||||
hashText value = do
|
|
||||||
(code, stdoutText, _) <-
|
|
||||||
readProcessWithExitCode "sha256sum" [] (T.unpack value)
|
|
||||||
pure $
|
|
||||||
if code == ExitSuccess
|
|
||||||
then takeWhile (/= ' ') stdoutText
|
|
||||||
else safeFileComponent value
|
|
||||||
|
|
||||||
safeFileComponent :: Text -> String
|
|
||||||
safeFileComponent =
|
|
||||||
take 96 . map normalizeChar . T.unpack
|
|
||||||
where
|
|
||||||
normalizeChar c
|
|
||||||
| isAlphaNum c = c
|
|
||||||
| otherwise = '-'
|
|
||||||
|
|
||||||
faviconExtension :: Text -> String
|
|
||||||
faviconExtension url =
|
|
||||||
fromMaybe ".img" $
|
|
||||||
listToMaybe
|
|
||||||
[ T.unpack ext
|
|
||||||
| ext <- [".svg", ".png", ".ico", ".jpg", ".jpeg", ".webp", ".gif"] :: [Text],
|
|
||||||
ext `T.isSuffixOf` T.toLower pathOnly
|
|
||||||
]
|
|
||||||
where
|
|
||||||
pathOnly = T.takeWhile (/= '?') url
|
|
||||||
|
|
||||||
downloadFavicon :: Text -> FilePath -> IO ()
|
|
||||||
downloadFavicon url path = do
|
|
||||||
let tmp = path <> ".tmp"
|
|
||||||
(code, _, _) <-
|
|
||||||
readProcessWithExitCode
|
|
||||||
"curl"
|
|
||||||
[ "-fsSL",
|
|
||||||
"--max-time",
|
|
||||||
"10",
|
|
||||||
"--retry",
|
|
||||||
"1",
|
|
||||||
"-o",
|
|
||||||
tmp,
|
|
||||||
T.unpack url
|
|
||||||
]
|
|
||||||
""
|
|
||||||
when (code == ExitSuccess) $
|
|
||||||
renameFile tmp path
|
|
||||||
|
|
||||||
nonEmptyFileExists :: FilePath -> IO Bool
|
|
||||||
nonEmptyFileExists path = do
|
|
||||||
exists <- doesFileExist path
|
|
||||||
if exists
|
|
||||||
then (> 0) <$> getFileSize path
|
|
||||||
else pure False
|
|
||||||
|
|
||||||
composeChromeFavicon ::
|
|
||||||
ChromeFaviconConfig ->
|
|
||||||
Int32 ->
|
|
||||||
Gdk.Pixbuf ->
|
|
||||||
Gdk.Pixbuf ->
|
|
||||||
IO Gdk.Pixbuf
|
|
||||||
composeChromeFavicon cfg size favicon chromeIcon = do
|
|
||||||
let (baseSource, overlaySource) =
|
|
||||||
case chromeFaviconOverlayMode cfg of
|
|
||||||
FaviconWithChromeOverlay -> (favicon, chromeIcon)
|
|
||||||
ChromeWithFaviconOverlay -> (chromeIcon, favicon)
|
|
||||||
base <- scalePixbuf size baseSource
|
|
||||||
result <- fromMaybe base <$> Gdk.pixbufCopy base
|
|
||||||
baseWidth <- Gdk.pixbufGetWidth result
|
|
||||||
baseHeight <- Gdk.pixbufGetHeight result
|
|
||||||
let baseSize = max 1 (min baseWidth baseHeight)
|
|
||||||
overlaySize =
|
|
||||||
max 1 $
|
|
||||||
min baseSize $
|
|
||||||
round (fromIntegral baseSize * chromeFaviconOverlayRatio cfg)
|
|
||||||
overlayX = baseWidth - overlaySize
|
|
||||||
overlayY = baseHeight - overlaySize
|
|
||||||
overlay <- scalePixbuf overlaySize overlaySource
|
|
||||||
Gdk.pixbufComposite
|
|
||||||
overlay
|
|
||||||
result
|
|
||||||
overlayX
|
|
||||||
overlayY
|
|
||||||
overlaySize
|
|
||||||
overlaySize
|
|
||||||
(fromIntegral overlayX)
|
|
||||||
(fromIntegral overlayY)
|
|
||||||
1
|
|
||||||
1
|
|
||||||
GdkPixbuf.InterpTypeBilinear
|
|
||||||
255
|
|
||||||
pure result
|
|
||||||
|
|
||||||
scalePixbuf :: Int32 -> Gdk.Pixbuf -> IO Gdk.Pixbuf
|
|
||||||
scalePixbuf size pixbuf =
|
|
||||||
fromMaybe pixbuf <$> Gdk.pixbufScaleSimple pixbuf size size GdkPixbuf.InterpTypeBilinear
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
module TaffybarConfig.Config
|
|
||||||
( mkSimpleTaffyConfig,
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import TaffybarConfig.Host (compactBarHosts, smallBarHosts)
|
|
||||||
import TaffybarConfig.Widgets (clockWidget, endWidgetsForHost, startWidgetsForHostAndBackend)
|
|
||||||
import System.Taffybar.Context (Backend)
|
|
||||||
import System.Taffybar.SimpleConfig
|
|
||||||
|
|
||||||
mkSimpleTaffyConfig :: String -> Backend -> [FilePath] -> SimpleTaffyConfig
|
|
||||||
mkSimpleTaffyConfig hostName backend cssFiles =
|
|
||||||
defaultSimpleTaffyConfig
|
|
||||||
{ startWidgets = startWidgetsForHostAndBackend hostName backend,
|
|
||||||
centerWidgets = [clockWidget],
|
|
||||||
endWidgets = endWidgetsForHost hostName,
|
|
||||||
barLevels = Nothing,
|
|
||||||
barPosition = Top,
|
|
||||||
widgetSpacing = 0,
|
|
||||||
barPadding =
|
|
||||||
if hostName `elem` smallBarHosts
|
|
||||||
then 1
|
|
||||||
else
|
|
||||||
if hostName `elem` compactBarHosts
|
|
||||||
then 2
|
|
||||||
else 4,
|
|
||||||
barHeight =
|
|
||||||
if hostName `elem` smallBarHosts
|
|
||||||
then ScreenRatio $ 1 / 48
|
|
||||||
else
|
|
||||||
if hostName `elem` compactBarHosts
|
|
||||||
then ScreenRatio $ 1 / 40
|
|
||||||
else ScreenRatio $ 1 / 33,
|
|
||||||
cssPaths = cssFiles
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
module TaffybarConfig.Host
|
|
||||||
( compactBarHosts,
|
|
||||||
cssFilesForHost,
|
|
||||||
laptopHosts,
|
|
||||||
smallBarHosts,
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Data.Maybe (fromMaybe)
|
|
||||||
|
|
||||||
-- NOTE: Keep `cssPaths` to a single entrypoint file per host. GTK's
|
|
||||||
-- `cssProviderLoadFromPath` clears the provider before loading, so handing
|
|
||||||
-- Taffybar multiple files here causes only the last file to take effect.
|
|
||||||
defaultCssFiles :: [FilePath]
|
|
||||||
defaultCssFiles = ["taffybar.css"]
|
|
||||||
|
|
||||||
cssFilesByHostname :: [(String, [FilePath])]
|
|
||||||
cssFilesByHostname =
|
|
||||||
[ ("ryzen-shine", ["ryzen-shine.css"]),
|
|
||||||
("strixi-minaj", ["strixi-minaj.css"])
|
|
||||||
]
|
|
||||||
|
|
||||||
compactBarHosts :: [String]
|
|
||||||
compactBarHosts =
|
|
||||||
["ryzen-shine"]
|
|
||||||
|
|
||||||
smallBarHosts :: [String]
|
|
||||||
smallBarHosts =
|
|
||||||
["strixi-minaj"]
|
|
||||||
|
|
||||||
laptopHosts :: [String]
|
|
||||||
laptopHosts =
|
|
||||||
[ "adell",
|
|
||||||
"stevie-nixos",
|
|
||||||
"strixi-minaj",
|
|
||||||
"jay-lenovo"
|
|
||||||
]
|
|
||||||
|
|
||||||
cssFilesForHost :: String -> [FilePath]
|
|
||||||
cssFilesForHost hostName =
|
|
||||||
fromMaybe defaultCssFiles $ lookup hostName cssFilesByHostname
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
{-# LANGUAGE LambdaCase #-}
|
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
|
||||||
|
|
||||||
module TaffybarConfig.WidgetUtil
|
|
||||||
( decorateWithClassAndBox,
|
|
||||||
decorateWithClassAndBoxM,
|
|
||||||
setFixedLabelWidth,
|
|
||||||
setLabelAlignmentRecursively,
|
|
||||||
stackInPill,
|
|
||||||
usageLogoWidget,
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Control.Monad.IO.Class (MonadIO, liftIO)
|
|
||||||
import Data.Foldable (for_)
|
|
||||||
import Data.GI.Base (castTo)
|
|
||||||
import Data.Int (Int32)
|
|
||||||
import Data.Text (Text)
|
|
||||||
import qualified GI.Gtk as Gtk
|
|
||||||
import qualified GI.Pango as Pango
|
|
||||||
import System.Environment.XDG.BaseDir (getUserConfigFile)
|
|
||||||
import System.Taffybar.Context (TaffyIO)
|
|
||||||
import System.Taffybar.Widget.Util
|
|
||||||
( buildContentsBox,
|
|
||||||
pixbufNewFromFileAtScaleByHeight,
|
|
||||||
widgetSetClassGI,
|
|
||||||
)
|
|
||||||
|
|
||||||
-- | Wrap the widget in a "TaffyBox" (via 'buildContentsBox') and add a CSS class.
|
|
||||||
decorateWithClassAndBox :: (MonadIO m) => Text -> Gtk.Widget -> m Gtk.Widget
|
|
||||||
decorateWithClassAndBox klass widget = do
|
|
||||||
boxed <- buildContentsBox widget
|
|
||||||
widgetSetClassGI boxed klass
|
|
||||||
|
|
||||||
decorateWithClassAndBoxM :: (MonadIO m) => Text -> m Gtk.Widget -> m Gtk.Widget
|
|
||||||
decorateWithClassAndBoxM klass builder =
|
|
||||||
builder >>= decorateWithClassAndBox klass
|
|
||||||
|
|
||||||
forEachLabelRecursively :: Gtk.Widget -> (Gtk.Label -> IO ()) -> IO ()
|
|
||||||
forEachLabelRecursively widget action = do
|
|
||||||
maybeLabel <- castTo Gtk.Label widget
|
|
||||||
for_ maybeLabel action
|
|
||||||
|
|
||||||
maybeContainer <- castTo Gtk.Container widget
|
|
||||||
case maybeContainer of
|
|
||||||
Just container ->
|
|
||||||
Gtk.containerGetChildren container >>= mapM_ (`forEachLabelRecursively` action)
|
|
||||||
Nothing -> pure ()
|
|
||||||
|
|
||||||
setLabelAlignmentRecursively :: Float -> Gtk.Justification -> Gtk.Widget -> IO ()
|
|
||||||
setLabelAlignmentRecursively xalign justify widget =
|
|
||||||
forEachLabelRecursively widget $ \label -> do
|
|
||||||
Gtk.labelSetXalign label xalign
|
|
||||||
Gtk.labelSetJustify label justify
|
|
||||||
|
|
||||||
setFixedLabelWidth :: Int32 -> Gtk.Label -> IO ()
|
|
||||||
setFixedLabelWidth width label = do
|
|
||||||
Gtk.labelSetWidthChars label width
|
|
||||||
Gtk.labelSetMaxWidthChars label width
|
|
||||||
Gtk.labelSetEllipsize label Pango.EllipsizeModeEnd
|
|
||||||
|
|
||||||
stackInPill :: Text -> [TaffyIO Gtk.Widget] -> TaffyIO Gtk.Widget
|
|
||||||
stackInPill klass builders =
|
|
||||||
decorateWithClassAndBoxM klass $ do
|
|
||||||
widgets <- sequence builders
|
|
||||||
liftIO $ do
|
|
||||||
box <- Gtk.boxNew Gtk.OrientationVertical 0
|
|
||||||
mapM_ (\w -> Gtk.boxPackStart box w False False 0) widgets
|
|
||||||
Gtk.widgetShowAll box
|
|
||||||
Gtk.toWidget box
|
|
||||||
|
|
||||||
usageLogoWidget :: FilePath -> Text -> IO Gtk.Widget
|
|
||||||
usageLogoWidget iconFile tooltip = do
|
|
||||||
iconPath <- getUserConfigFile "taffybar" ("icons/" <> iconFile)
|
|
||||||
iconWidget <-
|
|
||||||
pixbufNewFromFileAtScaleByHeight 18 iconPath >>= \case
|
|
||||||
Right pixbuf -> Gtk.toWidget =<< Gtk.imageNewFromPixbuf (Just pixbuf)
|
|
||||||
Left _ -> Gtk.toWidget =<< Gtk.labelNew (Just "?")
|
|
||||||
Gtk.widgetSetTooltipText iconWidget (Just tooltip)
|
|
||||||
widgetSetClassGI iconWidget "usage-logo"
|
|
||||||
@@ -1,469 +0,0 @@
|
|||||||
{-# LANGUAGE LambdaCase #-}
|
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
|
||||||
|
|
||||||
module TaffybarConfig.Widgets
|
|
||||||
( clockWidget,
|
|
||||||
endWidgetsForHost,
|
|
||||||
startWidgetsForBackend,
|
|
||||||
startWidgetsForHostAndBackend,
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Control.Concurrent (threadDelay)
|
|
||||||
import Control.Monad.IO.Class (liftIO)
|
|
||||||
import Data.Char (toLower)
|
|
||||||
import Data.Maybe (fromMaybe)
|
|
||||||
import Data.Ratio ((%))
|
|
||||||
import Data.Text (Text)
|
|
||||||
import qualified Data.Text as T
|
|
||||||
import qualified GI.Gtk as Gtk
|
|
||||||
import qualified StatusNotifier.Tray as SNITray
|
|
||||||
import System.Environment (lookupEnv)
|
|
||||||
import System.Environment.XDG.BaseDir (getUserConfigFile)
|
|
||||||
import System.Taffybar.Context
|
|
||||||
( Backend (BackendWayland, BackendX11),
|
|
||||||
TaffyIO,
|
|
||||||
)
|
|
||||||
import System.Taffybar.Information.Memory (MemoryInfo (..), parseMeminfo)
|
|
||||||
import qualified System.Taffybar.Information.Workspaces.Hyprland as HyprlandWorkspaces
|
|
||||||
import System.Taffybar.Util (postGUIASync)
|
|
||||||
import System.Taffybar.Widget
|
|
||||||
import qualified System.Taffybar.Widget.ASUS as ASUS
|
|
||||||
import qualified System.Taffybar.Widget.Audio as Audio
|
|
||||||
import System.Taffybar.Widget.CPUMonitor (cpuMonitorNew)
|
|
||||||
import System.Taffybar.Widget.Generic.Graph (GraphConfig (..), GraphDirection (..), GraphStyle (..), defaultGraphConfig)
|
|
||||||
import qualified System.Taffybar.Widget.NetworkManager as NetworkManager
|
|
||||||
import System.Taffybar.Widget.OpenAIUsage
|
|
||||||
( OpenAIUsageDisplayMode (OpenAIUsageDisplayRemaining),
|
|
||||||
OpenAIUsageStackConfig (..),
|
|
||||||
defaultOpenAIUsageStackConfig,
|
|
||||||
openAIUsageSectionNewWith,
|
|
||||||
)
|
|
||||||
import System.Taffybar.Widget.SNIMenu (withNmAppletMenu)
|
|
||||||
import System.Taffybar.Widget.SNITray
|
|
||||||
( CollapsibleSNITrayParams (..),
|
|
||||||
SNITrayConfig (..),
|
|
||||||
defaultCollapsibleSNITrayParams,
|
|
||||||
defaultSNITrayConfig,
|
|
||||||
)
|
|
||||||
import System.Taffybar.Widget.SNITray.PrioritizedCollapsible
|
|
||||||
( PrioritizedCollapsibleSNITrayParams (..),
|
|
||||||
defaultPrioritizedCollapsibleSNITrayParams,
|
|
||||||
sniTrayPrioritizedCollapsibleNewFromParams,
|
|
||||||
)
|
|
||||||
import qualified System.Taffybar.Widget.ScreenLock as ScreenLock
|
|
||||||
import System.Taffybar.Widget.Util
|
|
||||||
( backgroundLoop,
|
|
||||||
buildIconLabelBox,
|
|
||||||
pixbufNewFromFileAtScaleByHeight,
|
|
||||||
widgetSetClassGI,
|
|
||||||
)
|
|
||||||
import qualified System.Taffybar.Widget.Wlsunset as Wlsunset
|
|
||||||
import qualified System.Taffybar.Widget.Workspaces as Workspaces
|
|
||||||
import TaffybarConfig.Host (laptopHosts)
|
|
||||||
import TaffybarConfig.WidgetUtil
|
|
||||||
( decorateWithClassAndBox,
|
|
||||||
decorateWithClassAndBoxM,
|
|
||||||
setFixedLabelWidth,
|
|
||||||
setLabelAlignmentRecursively,
|
|
||||||
stackInPill,
|
|
||||||
usageLogoWidget,
|
|
||||||
)
|
|
||||||
import TaffybarConfig.Workspaces (workspaceLabelSetter, workspaceShowPredicate, workspaceWindowIconGetter)
|
|
||||||
import Text.Printf (printf)
|
|
||||||
import Text.Read (readMaybe)
|
|
||||||
|
|
||||||
audioWidget :: TaffyIO Gtk.Widget
|
|
||||||
audioWidget =
|
|
||||||
decorateWithClassAndBoxM "audio" Audio.audioNew
|
|
||||||
|
|
||||||
networkInnerWidget :: TaffyIO Gtk.Widget
|
|
||||||
networkInnerWidget =
|
|
||||||
withNmAppletMenu NetworkManager.networkManagerWifiIconLabelNew
|
|
||||||
>>= flip widgetSetClassGI "network"
|
|
||||||
|
|
||||||
networkWidget :: TaffyIO Gtk.Widget
|
|
||||||
networkWidget =
|
|
||||||
decorateWithClassAndBoxM "network" networkInnerWidget
|
|
||||||
|
|
||||||
layoutWidget :: TaffyIO Gtk.Widget
|
|
||||||
layoutWidget =
|
|
||||||
decorateWithClassAndBoxM "layout" (layoutNew defaultLayoutConfig)
|
|
||||||
|
|
||||||
windowsWidget :: TaffyIO Gtk.Widget
|
|
||||||
windowsWidget =
|
|
||||||
decorateWithClassAndBoxM
|
|
||||||
"windows"
|
|
||||||
( windowsNew
|
|
||||||
defaultWindowsConfig
|
|
||||||
{ getActiveLabel = truncatedGetActiveLabel 28,
|
|
||||||
configureActiveLabel = liftIO . setFixedLabelWidth 28
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
workspacesWidget :: TaffyIO Gtk.Widget
|
|
||||||
workspacesWidget =
|
|
||||||
Workspaces.workspacesNew cfg
|
|
||||||
where
|
|
||||||
cfg =
|
|
||||||
Workspaces.defaultWorkspacesConfig
|
|
||||||
{ Workspaces.widgetGap = 0,
|
|
||||||
Workspaces.minIcons = 1,
|
|
||||||
Workspaces.getWindowIconPixbuf = workspaceWindowIconGetter,
|
|
||||||
Workspaces.hyprlandWorkspaceProviderConfig =
|
|
||||||
HyprlandWorkspaces.defaultHyprlandWorkspaceProviderConfig
|
|
||||||
{ HyprlandWorkspaces.specialWorkspaceWindowTarget =
|
|
||||||
HyprlandWorkspaces.specialWorkspaceWindowsToMinimized
|
|
||||||
},
|
|
||||||
Workspaces.labelSetter = workspaceLabelSetter,
|
|
||||||
Workspaces.showWorkspaceFn = workspaceShowPredicate
|
|
||||||
}
|
|
||||||
|
|
||||||
clockWidget :: TaffyIO Gtk.Widget
|
|
||||||
clockWidget = do
|
|
||||||
clock <-
|
|
||||||
textClockNewWith
|
|
||||||
defaultClockConfig
|
|
||||||
{ clockUpdateStrategy = RoundedTargetInterval 60 0.0,
|
|
||||||
clockFormatString = "%a %b %_d\n%I:%M %p"
|
|
||||||
}
|
|
||||||
liftIO $ setLabelAlignmentRecursively 0.5 Gtk.JustificationCenter clock
|
|
||||||
decorateWithClassAndBox "clock" clock
|
|
||||||
|
|
||||||
singleLineMprisLabel :: Text -> Text
|
|
||||||
singleLineMprisLabel =
|
|
||||||
T.replace "\n" " " . T.replace "\r" " "
|
|
||||||
|
|
||||||
stackedMprisLabel :: Text -> Text
|
|
||||||
stackedMprisLabel raw =
|
|
||||||
let normalized = singleLineMprisLabel raw
|
|
||||||
(top, rest) = T.breakOn " - " normalized
|
|
||||||
in if T.null rest
|
|
||||||
then normalized
|
|
||||||
else top <> "\n" <> T.drop 3 rest
|
|
||||||
|
|
||||||
mprisWidget :: TaffyIO Gtk.Widget
|
|
||||||
mprisWidget =
|
|
||||||
mpris2NewWithConfig
|
|
||||||
MPRIS2Config
|
|
||||||
{ mprisWidgetWrapper = decorateWithClassAndBox "mpris",
|
|
||||||
updatePlayerWidget =
|
|
||||||
simplePlayerWidget
|
|
||||||
defaultPlayerConfig
|
|
||||||
{ setNowPlayingLabel =
|
|
||||||
fmap stackedMprisLabel . playingText 20 20,
|
|
||||||
setupPlayerLabel = setFixedLabelWidth 20
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
batteryInnerWidget :: TaffyIO Gtk.Widget
|
|
||||||
batteryInnerWidget = do
|
|
||||||
iconWidget <- batteryTextIconNew
|
|
||||||
labelWidget <- textBatteryNew "$percentage$%"
|
|
||||||
liftIO (buildIconLabelBox iconWidget labelWidget) >>= flip widgetSetClassGI "battery"
|
|
||||||
|
|
||||||
batteryWidget :: TaffyIO Gtk.Widget
|
|
||||||
batteryWidget =
|
|
||||||
decorateWithClassAndBoxM "battery" batteryInnerWidget
|
|
||||||
|
|
||||||
backlightWidget :: TaffyIO Gtk.Widget
|
|
||||||
backlightWidget =
|
|
||||||
decorateWithClassAndBoxM
|
|
||||||
"backlight"
|
|
||||||
( backlightLabelNewChanWith
|
|
||||||
defaultBacklightWidgetConfig
|
|
||||||
{ backlightFormat = "☀ $percent$%",
|
|
||||||
backlightUnknownFormat = "☀ n/a",
|
|
||||||
backlightTooltipFormat =
|
|
||||||
Just "Device: $device$\nBrightness: $brightness$/$max$ ($percent$%)"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
diskUsageInnerWidget :: TaffyIO Gtk.Widget
|
|
||||||
diskUsageInnerWidget =
|
|
||||||
diskUsageNew >>= flip widgetSetClassGI "disk-usage"
|
|
||||||
|
|
||||||
diskUsageWidget :: TaffyIO Gtk.Widget
|
|
||||||
diskUsageWidget =
|
|
||||||
decorateWithClassAndBoxM "disk-usage" diskUsageInnerWidget
|
|
||||||
|
|
||||||
meminfoPercentRowWidget ::
|
|
||||||
Text ->
|
|
||||||
Text ->
|
|
||||||
(MemoryInfo -> Maybe Double) ->
|
|
||||||
(MemoryInfo -> T.Text) ->
|
|
||||||
TaffyIO Gtk.Widget
|
|
||||||
meminfoPercentRowWidget rowClass iconText getRatio tooltipText =
|
|
||||||
liftIO $ do
|
|
||||||
iconW <- Gtk.toWidget =<< Gtk.labelNew (Just iconText)
|
|
||||||
valueLabel <- Gtk.labelNew (Just "")
|
|
||||||
valueW <- Gtk.toWidget valueLabel
|
|
||||||
row <- buildIconLabelBox iconW valueW
|
|
||||||
_ <- widgetSetClassGI row rowClass
|
|
||||||
|
|
||||||
let fmtPercent :: Double -> T.Text
|
|
||||||
fmtPercent r = T.pack (printf "%.0f%%" (max 0 r * 100))
|
|
||||||
updateOnce :: IO ()
|
|
||||||
updateOnce = do
|
|
||||||
info <- parseMeminfo
|
|
||||||
let valueText = maybe "n/a" fmtPercent (getRatio info)
|
|
||||||
postGUIASync $ do
|
|
||||||
Gtk.labelSetText valueLabel valueText
|
|
||||||
Gtk.widgetSetTooltipText row (Just (tooltipText info))
|
|
||||||
threadDelay (2 * 1000000)
|
|
||||||
|
|
||||||
_ <- Gtk.onWidgetRealize row $ backgroundLoop updateOnce
|
|
||||||
pure row
|
|
||||||
|
|
||||||
ramRowWidget :: TaffyIO Gtk.Widget
|
|
||||||
ramRowWidget =
|
|
||||||
meminfoPercentRowWidget
|
|
||||||
"ram-row"
|
|
||||||
"\xF538" -- Font Awesome: memory
|
|
||||||
(Just . memoryUsedRatio)
|
|
||||||
(\info -> "RAM " <> showMemoryInfo "$used$/$total$" 2 info)
|
|
||||||
|
|
||||||
swapRowWidget :: TaffyIO Gtk.Widget
|
|
||||||
swapRowWidget =
|
|
||||||
meminfoPercentRowWidget
|
|
||||||
"swap-row"
|
|
||||||
"\xF0EC" -- Font Awesome: exchange (swap-ish)
|
|
||||||
(\info -> if memorySwapTotal info <= 0 then Nothing else Just (memorySwapUsedRatio info))
|
|
||||||
(\info -> "SWAP " <> showMemoryInfo "$swapUsed$/$swapTotal$" 2 info)
|
|
||||||
|
|
||||||
ramSwapWidget :: TaffyIO Gtk.Widget
|
|
||||||
ramSwapWidget =
|
|
||||||
stackInPill "ram-swap" [ramRowWidget, swapRowWidget]
|
|
||||||
|
|
||||||
audioBacklightWidget :: TaffyIO Gtk.Widget
|
|
||||||
audioBacklightWidget =
|
|
||||||
stackInPill
|
|
||||||
"audio-backlight"
|
|
||||||
[ Audio.audioNew,
|
|
||||||
backlightNewChanWith
|
|
||||||
defaultBacklightWidgetConfig
|
|
||||||
{ backlightFormat = "$percent$%",
|
|
||||||
backlightUnknownFormat = "n/a",
|
|
||||||
backlightTooltipFormat =
|
|
||||||
Just "Device: $device$\nBrightness: $brightness$/$max$ ($percent$%)"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
asusInnerWidget :: TaffyIO Gtk.Widget
|
|
||||||
asusInnerWidget = ASUS.asusWidgetNew
|
|
||||||
|
|
||||||
asusWidget :: TaffyIO Gtk.Widget
|
|
||||||
asusWidget =
|
|
||||||
decorateWithClassAndBoxM "asus-profile" asusInnerWidget
|
|
||||||
|
|
||||||
batteryNetworkWidget :: TaffyIO Gtk.Widget
|
|
||||||
batteryNetworkWidget =
|
|
||||||
stackInPill "battery-network" [batteryInnerWidget, networkInnerWidget]
|
|
||||||
|
|
||||||
asusDiskUsageWidget :: TaffyIO Gtk.Widget
|
|
||||||
asusDiskUsageWidget =
|
|
||||||
stackInPill "asus-disk-usage" [diskUsageInnerWidget, asusInnerWidget]
|
|
||||||
|
|
||||||
screenLockWidget :: TaffyIO Gtk.Widget
|
|
||||||
screenLockWidget =
|
|
||||||
decorateWithClassAndBoxM "screen-lock" $
|
|
||||||
ScreenLock.screenLockNewWithConfig
|
|
||||||
ScreenLock.defaultScreenLockConfig
|
|
||||||
{ ScreenLock.screenLockIcon = T.pack "\xF023" <> " Lock"
|
|
||||||
}
|
|
||||||
|
|
||||||
wlsunsetWidget :: TaffyIO Gtk.Widget
|
|
||||||
wlsunsetWidget =
|
|
||||||
decorateWithClassAndBoxM "wlsunset" $
|
|
||||||
Wlsunset.wlsunsetNewWithConfig
|
|
||||||
Wlsunset.defaultWlsunsetWidgetConfig
|
|
||||||
{ Wlsunset.wlsunsetWidgetIcon = T.pack "\xF0599" <> " Sun"
|
|
||||||
}
|
|
||||||
|
|
||||||
simplifiedScreenLockWidget :: TaffyIO Gtk.Widget
|
|
||||||
simplifiedScreenLockWidget =
|
|
||||||
-- Inner widget: no extra pill wrapping (the combiner provides that).
|
|
||||||
ScreenLock.screenLockNewWithConfig
|
|
||||||
ScreenLock.defaultScreenLockConfig
|
|
||||||
{ ScreenLock.screenLockIcon = T.pack "\xF023" <> " Lock"
|
|
||||||
}
|
|
||||||
|
|
||||||
simplifiedWlsunsetWidget :: TaffyIO Gtk.Widget
|
|
||||||
simplifiedWlsunsetWidget =
|
|
||||||
-- Inner widget: no extra pill wrapping (the combiner provides that).
|
|
||||||
Wlsunset.wlsunsetNewWithConfig
|
|
||||||
Wlsunset.defaultWlsunsetWidgetConfig
|
|
||||||
{ Wlsunset.wlsunsetWidgetIcon = T.pack "\xF0599" <> " Sun"
|
|
||||||
}
|
|
||||||
|
|
||||||
sunLockWidget :: TaffyIO Gtk.Widget
|
|
||||||
sunLockWidget =
|
|
||||||
stackInPill "sun-lock" [simplifiedWlsunsetWidget, simplifiedScreenLockWidget]
|
|
||||||
|
|
||||||
cpuWidget :: TaffyIO Gtk.Widget
|
|
||||||
cpuWidget =
|
|
||||||
decorateWithClassAndBoxM "cpu" $
|
|
||||||
cpuMonitorNew
|
|
||||||
defaultGraphConfig
|
|
||||||
{ graphDataColors = [(0, 1, 0.5, 0.8), (1, 0, 0, 0.5)],
|
|
||||||
graphBackgroundColor = (0, 0, 0, 0),
|
|
||||||
graphBorderWidth = 0,
|
|
||||||
graphLabel = Just "CPU",
|
|
||||||
graphWidth = 50,
|
|
||||||
graphDirection = LEFT_TO_RIGHT
|
|
||||||
}
|
|
||||||
1.0
|
|
||||||
"cpu"
|
|
||||||
|
|
||||||
wakeupDebugWidget :: TaffyIO Gtk.Widget
|
|
||||||
wakeupDebugWidget =
|
|
||||||
decorateWithClassAndBoxM "wakeup-debug" wakeupDebugWidgetNew
|
|
||||||
|
|
||||||
omniMenuItem :: Text -> Text -> Text -> OmniMenuItem
|
|
||||||
omniMenuItem label iconName command =
|
|
||||||
OmniMenuItem
|
|
||||||
{ omniMenuItemLabel = label,
|
|
||||||
omniMenuItemCommand = command,
|
|
||||||
omniMenuItemIcon = Just iconName,
|
|
||||||
omniMenuItemTooltip = Just command
|
|
||||||
}
|
|
||||||
|
|
||||||
omniMenuWidget :: TaffyIO Gtk.Widget
|
|
||||||
omniMenuWidget =
|
|
||||||
decorateWithClassAndBoxM "omni-menu" $ do
|
|
||||||
icon <-
|
|
||||||
liftIO $ do
|
|
||||||
iconPath <- getUserConfigFile "taffybar" "icons/nix-snowflake.svg"
|
|
||||||
pixbufNewFromFileAtScaleByHeight 18 iconPath >>= \case
|
|
||||||
Right pixbuf -> Gtk.toWidget =<< Gtk.imageNewFromPixbuf (Just pixbuf)
|
|
||||||
Left _ ->
|
|
||||||
Gtk.imageNewFromIconName
|
|
||||||
(Just "system-run")
|
|
||||||
(fromIntegral $ fromEnum Gtk.IconSizeMenu)
|
|
||||||
>>= Gtk.toWidget
|
|
||||||
omniMenuNewWithConfig
|
|
||||||
(defaultOmniMenuConfig icon)
|
|
||||||
{ omniMenuIncludeApplications = True,
|
|
||||||
omniMenuSections =
|
|
||||||
[ OmniMenuSection
|
|
||||||
"Launch"
|
|
||||||
[ omniMenuItem "App launcher" "view-app-grid-symbolic" "hypr_shell_ui launcher",
|
|
||||||
omniMenuItem "Run command" "system-run" "hypr_shell_ui run",
|
|
||||||
omniMenuItem "Terminal" "utilities-terminal" "ghostty --gtk-single-instance=false",
|
|
||||||
omniMenuItem "Window picker" "preferences-system-windows" "hypr_shell_ui window go"
|
|
||||||
],
|
|
||||||
OmniMenuSection
|
|
||||||
"System"
|
|
||||||
[ omniMenuItem "Lock" "system-lock-screen" "loginctl lock-session",
|
|
||||||
omniMenuItem "Toggle screensaver" "video-display" "/home/imalison/dotfiles/dotfiles/lib/bin/hypr-screensaver toggle",
|
|
||||||
omniMenuItem "Reload WM" "view-refresh" "sh -lc 'hyprctl reload || xmonad --restart || river-xmonad-restart'",
|
|
||||||
omniMenuItem "Restart taffybar" "view-refresh-symbolic" "/home/imalison/dotfiles/dotfiles/config/taffybar/scripts/taffybar-restart",
|
|
||||||
omniMenuItem "Logout" "system-log-out" "sh -lc 'hyprctl dispatch exit || riverctl exit'",
|
|
||||||
omniMenuItem "Suspend" "media-playback-pause" "systemctl suspend",
|
|
||||||
omniMenuItem "Reboot" "system-reboot" "systemctl reboot",
|
|
||||||
omniMenuItem "Power off" "system-shutdown" "systemctl poweroff"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
usageSectionWidget :: Text -> FilePath -> Text -> TaffyIO Gtk.Widget -> TaffyIO Gtk.Widget
|
|
||||||
usageSectionWidget klass iconFile tooltip stackBuilder =
|
|
||||||
decorateWithClassAndBoxM klass $ do
|
|
||||||
stack <- stackBuilder
|
|
||||||
liftIO $ do
|
|
||||||
iconWidget <- usageLogoWidget iconFile tooltip
|
|
||||||
section <- buildIconLabelBox iconWidget stack
|
|
||||||
widgetSetClassGI section "usage-section"
|
|
||||||
|
|
||||||
openAIUsageWidget :: TaffyIO Gtk.Widget
|
|
||||||
openAIUsageWidget = do
|
|
||||||
iconWidget <- liftIO $ usageLogoWidget "openai-symbol.svg" "OpenAI usage"
|
|
||||||
decorateWithClassAndBoxM "openai-usage" $
|
|
||||||
openAIUsageSectionNewWith
|
|
||||||
iconWidget
|
|
||||||
defaultOpenAIUsageStackConfig
|
|
||||||
{ openAIUsageStackDefaultDisplayMode = OpenAIUsageDisplayRemaining
|
|
||||||
}
|
|
||||||
|
|
||||||
sniPriorityVisibilityThresholdDefault :: Int
|
|
||||||
sniPriorityVisibilityThresholdDefault = 0
|
|
||||||
|
|
||||||
sniTrayWidget :: TaffyIO Gtk.Widget
|
|
||||||
sniTrayWidget = do
|
|
||||||
-- If the Haskell backend regresses, flip at runtime:
|
|
||||||
-- TAFFYBAR_SNI_MENU_BACKEND=lib
|
|
||||||
backendEnv <- liftIO (lookupEnv "TAFFYBAR_SNI_MENU_BACKEND")
|
|
||||||
thresholdEnv <- liftIO (lookupEnv "TAFFYBAR_SNI_PRIORITY_THRESHOLD")
|
|
||||||
let menuBackend =
|
|
||||||
case fmap (map toLower) backendEnv of
|
|
||||||
Just "lib" -> SNITray.LibDBusMenu
|
|
||||||
_ -> SNITray.HaskellDBusMenu
|
|
||||||
visibilityThreshold =
|
|
||||||
fromMaybe
|
|
||||||
sniPriorityVisibilityThresholdDefault
|
|
||||||
(thresholdEnv >>= readMaybe)
|
|
||||||
trayParams =
|
|
||||||
SNITray.defaultTrayParams
|
|
||||||
{ SNITray.trayMenuBackend = menuBackend,
|
|
||||||
SNITray.trayOverlayScale = 1 % 3,
|
|
||||||
SNITray.trayEventHooks = SNITray.defaultTrayEventHooks
|
|
||||||
}
|
|
||||||
sniTrayConfig =
|
|
||||||
defaultSNITrayConfig
|
|
||||||
{ sniTrayTrayParams = trayParams
|
|
||||||
}
|
|
||||||
collapsibleParams =
|
|
||||||
defaultCollapsibleSNITrayParams
|
|
||||||
{ collapsibleSNITrayConfig = sniTrayConfig
|
|
||||||
}
|
|
||||||
prioritizedParams =
|
|
||||||
defaultPrioritizedCollapsibleSNITrayParams
|
|
||||||
{ prioritizedCollapsibleSNITrayParams = collapsibleParams,
|
|
||||||
prioritizedCollapsibleSNITrayVisibilityThreshold = Just visibilityThreshold,
|
|
||||||
prioritizedCollapsibleSNITrayHoverExpand = True
|
|
||||||
}
|
|
||||||
decorateWithClassAndBoxM
|
|
||||||
"sni-tray"
|
|
||||||
(sniTrayPrioritizedCollapsibleNewFromParams prioritizedParams)
|
|
||||||
|
|
||||||
startWidgetsForBackend :: Backend -> [TaffyIO Gtk.Widget]
|
|
||||||
startWidgetsForBackend backend =
|
|
||||||
case backend of
|
|
||||||
BackendX11 -> [omniMenuWidget, workspacesWidget, layoutWidget]
|
|
||||||
-- These Wayland widgets are Hyprland-specific.
|
|
||||||
BackendWayland -> [omniMenuWidget, workspacesWidget]
|
|
||||||
|
|
||||||
startWidgetsForHostAndBackend :: String -> Backend -> [TaffyIO Gtk.Widget]
|
|
||||||
startWidgetsForHostAndBackend _hostName = startWidgetsForBackend
|
|
||||||
|
|
||||||
endWidgetsForHost :: String -> [TaffyIO Gtk.Widget]
|
|
||||||
endWidgetsForHost hostName =
|
|
||||||
-- NOTE: end widgets are packed with Gtk.boxPackEnd, so the list order is
|
|
||||||
-- right-to-left on screen. Make the tray appear at the far right by placing
|
|
||||||
-- it first in the list. (On laptops: the battery/wifi stack is far right,
|
|
||||||
-- tray immediately left of it.)
|
|
||||||
let baseEndWidgets =
|
|
||||||
[ sniTrayWidget,
|
|
||||||
audioWidget,
|
|
||||||
openAIUsageWidget,
|
|
||||||
cpuWidget,
|
|
||||||
ramSwapWidget,
|
|
||||||
diskUsageWidget,
|
|
||||||
networkWidget,
|
|
||||||
sunLockWidget,
|
|
||||||
mprisWidget
|
|
||||||
]
|
|
||||||
laptopEndWidgets =
|
|
||||||
[ batteryNetworkWidget,
|
|
||||||
sniTrayWidget,
|
|
||||||
asusDiskUsageWidget,
|
|
||||||
audioBacklightWidget,
|
|
||||||
openAIUsageWidget,
|
|
||||||
cpuWidget,
|
|
||||||
ramSwapWidget,
|
|
||||||
sunLockWidget,
|
|
||||||
mprisWidget
|
|
||||||
]
|
|
||||||
in if hostName `elem` laptopHosts
|
|
||||||
then laptopEndWidgets
|
|
||||||
else baseEndWidgets
|
|
||||||
@@ -1,196 +0,0 @@
|
|||||||
{-# LANGUAGE OverloadedStrings #-}
|
|
||||||
|
|
||||||
module TaffybarConfig.Workspaces
|
|
||||||
( workspaceLabelSetter,
|
|
||||||
workspaceShowPredicate,
|
|
||||||
workspaceWindowIconGetter,
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Control.Monad.IO.Class (liftIO)
|
|
||||||
import Control.Monad.Trans.Reader (asks)
|
|
||||||
import Data.Int (Int32)
|
|
||||||
import Data.List (nub)
|
|
||||||
import qualified Data.Map as M
|
|
||||||
import Data.Maybe (fromMaybe, mapMaybe)
|
|
||||||
import Data.Text (Text)
|
|
||||||
import qualified Data.Text as T
|
|
||||||
import qualified GI.GdkPixbuf.Objects.Pixbuf as Gdk
|
|
||||||
import System.Taffybar.Context
|
|
||||||
( Backend (BackendX11),
|
|
||||||
TaffyIO,
|
|
||||||
backend,
|
|
||||||
runX11Def,
|
|
||||||
)
|
|
||||||
import System.Taffybar.Information.EWMHDesktopInfo (WorkspaceId (..))
|
|
||||||
import qualified System.Taffybar.Information.Workspaces.Model as WorkspaceModel
|
|
||||||
import System.Taffybar.Information.X11DesktopInfo
|
|
||||||
import System.Taffybar.Util (getPixbufFromFilePath, maybeTCombine, (<|||>))
|
|
||||||
import System.Taffybar.Widget.Util (loadPixbufByName)
|
|
||||||
import qualified System.Taffybar.Widget.Workspaces as Workspaces
|
|
||||||
import System.Taffybar.WindowIcon (pixBufFromColor)
|
|
||||||
import TaffybarConfig.ChromeFavicons
|
|
||||||
( ChromeFaviconConfig (..),
|
|
||||||
ChromeFaviconOverlayMode (..),
|
|
||||||
chromeFaviconIconGetter,
|
|
||||||
defaultChromeFaviconConfig,
|
|
||||||
)
|
|
||||||
|
|
||||||
chromeFaviconConfig :: ChromeFaviconConfig
|
|
||||||
chromeFaviconConfig =
|
|
||||||
defaultChromeFaviconConfig
|
|
||||||
{ chromeFaviconOverlayMode = FaviconWithChromeOverlay,
|
|
||||||
chromeFaviconOverlayRatio = 0.45,
|
|
||||||
chromeFaviconEnabled = True
|
|
||||||
}
|
|
||||||
|
|
||||||
x11FullWorkspaceNames :: X11Property [(WorkspaceId, String)]
|
|
||||||
x11FullWorkspaceNames =
|
|
||||||
go <$> readAsListOfString Nothing "_NET_DESKTOP_FULL_NAMES"
|
|
||||||
where
|
|
||||||
go = zip [WorkspaceId i | i <- [0 ..]]
|
|
||||||
|
|
||||||
remapNSP :: String -> String
|
|
||||||
remapNSP "NSP" = "S"
|
|
||||||
remapNSP n = n
|
|
||||||
|
|
||||||
workspaceIsMinimizedBucket :: WorkspaceModel.WorkspaceInfo -> Bool
|
|
||||||
workspaceIsMinimizedBucket workspace =
|
|
||||||
let name =
|
|
||||||
T.toLower $
|
|
||||||
WorkspaceModel.workspaceName $
|
|
||||||
WorkspaceModel.workspaceIdentity workspace
|
|
||||||
in name == "minimized" || name == "special:minimized"
|
|
||||||
|
|
||||||
workspaceShowPredicate :: WorkspaceModel.WorkspaceInfo -> Bool
|
|
||||||
workspaceShowPredicate workspace =
|
|
||||||
Workspaces.hideEmpty workspace
|
|
||||||
&& (not (WorkspaceModel.workspaceIsSpecial workspace) || workspaceIsMinimizedBucket workspace)
|
|
||||||
|
|
||||||
workspaceLabelSetter :: WorkspaceModel.WorkspaceInfo -> TaffyIO String
|
|
||||||
workspaceLabelSetter workspace = do
|
|
||||||
backendType <- asks backend
|
|
||||||
let identity = WorkspaceModel.workspaceIdentity workspace
|
|
||||||
fallbackLabel = remapNSP $ T.unpack (WorkspaceModel.workspaceName identity)
|
|
||||||
if workspaceIsMinimizedBucket workspace
|
|
||||||
then return "M"
|
|
||||||
else case (backendType, WorkspaceModel.workspaceNumericId identity) of
|
|
||||||
(BackendX11, Just workspaceId) -> do
|
|
||||||
fullNames <- runX11Def [] x11FullWorkspaceNames
|
|
||||||
return $ remapNSP $ fromMaybe fallbackLabel (lookup (WorkspaceId workspaceId) fullNames)
|
|
||||||
_ -> return fallbackLabel
|
|
||||||
|
|
||||||
iconRemap :: [(Text, [Text])]
|
|
||||||
iconRemap =
|
|
||||||
[ ("spotify", ["spotify-client", "spotify"])
|
|
||||||
]
|
|
||||||
|
|
||||||
iconRemapMap :: M.Map Text [Text]
|
|
||||||
iconRemapMap =
|
|
||||||
M.fromList [(T.toLower k, v) | (k, v) <- iconRemap]
|
|
||||||
|
|
||||||
lookupIconRemap :: Text -> [Text]
|
|
||||||
lookupIconRemap name = fromMaybe [] $ M.lookup (T.toLower name) iconRemapMap
|
|
||||||
|
|
||||||
iconNameVariants :: Text -> [Text]
|
|
||||||
iconNameVariants raw =
|
|
||||||
let lower = T.toLower raw
|
|
||||||
stripped = fromMaybe lower (T.stripSuffix ".desktop" lower)
|
|
||||||
suffixes = ["-gtk", "-client", "-desktop"]
|
|
||||||
stripSuffixes name =
|
|
||||||
let variants = mapMaybe (`T.stripSuffix` name) suffixes
|
|
||||||
in nub $ variants ++ [name]
|
|
||||||
baseNames = stripSuffixes stripped ++ [raw]
|
|
||||||
toDash c
|
|
||||||
| c == ' ' || c == '_' || c == '.' || c == '/' = '-'
|
|
||||||
| otherwise = c
|
|
||||||
toUnderscore c
|
|
||||||
| c == ' ' || c == '-' || c == '.' || c == '/' = '_'
|
|
||||||
| otherwise = c
|
|
||||||
variantsFor name =
|
|
||||||
let dotted =
|
|
||||||
case T.splitOn "." name of
|
|
||||||
[] -> name
|
|
||||||
xs -> last xs
|
|
||||||
dashed = T.map toDash name
|
|
||||||
dashedDotted = T.map toDash dotted
|
|
||||||
underscored = T.map toUnderscore name
|
|
||||||
underscoredDotted = T.map toUnderscore dotted
|
|
||||||
in [dotted, dashed, dashedDotted, underscored, underscoredDotted, name]
|
|
||||||
in nub $ concatMap variantsFor baseNames
|
|
||||||
|
|
||||||
workspaceIconCandidates :: WorkspaceModel.WindowInfo -> [Text]
|
|
||||||
workspaceIconCandidates windowData =
|
|
||||||
let baseNames = WorkspaceModel.windowClassHints windowData
|
|
||||||
remapped = concatMap lookupIconRemap baseNames
|
|
||||||
remappedExpanded = concatMap iconNameVariants remapped
|
|
||||||
baseExpanded = concatMap iconNameVariants baseNames
|
|
||||||
in nub (remappedExpanded ++ baseExpanded)
|
|
||||||
|
|
||||||
isPathCandidate :: Text -> Bool
|
|
||||||
isPathCandidate name =
|
|
||||||
T.isInfixOf "/" name
|
|
||||||
|| any (`T.isSuffixOf` name) [".png", ".svg", ".xpm"]
|
|
||||||
|
|
||||||
workspaceCandidateInfo :: Text -> WorkspaceModel.WindowInfo
|
|
||||||
workspaceCandidateInfo name =
|
|
||||||
WorkspaceModel.WindowInfo
|
|
||||||
{ WorkspaceModel.windowIdentity = WorkspaceModel.HyprlandWindowIdentity "",
|
|
||||||
WorkspaceModel.windowUpdateRevision = 0,
|
|
||||||
WorkspaceModel.windowTitle = "",
|
|
||||||
WorkspaceModel.windowClassHints = [name],
|
|
||||||
WorkspaceModel.windowPosition = Nothing,
|
|
||||||
WorkspaceModel.windowUrgent = False,
|
|
||||||
WorkspaceModel.windowActive = False,
|
|
||||||
WorkspaceModel.windowMinimized = False,
|
|
||||||
WorkspaceModel.windowPinned = False
|
|
||||||
}
|
|
||||||
|
|
||||||
workspaceIconFromCandidate :: Int32 -> Text -> TaffyIO (Maybe Gdk.Pixbuf)
|
|
||||||
workspaceIconFromCandidate size name
|
|
||||||
| isPathCandidate name =
|
|
||||||
liftIO $ getPixbufFromFilePath (T.unpack name)
|
|
||||||
| otherwise =
|
|
||||||
maybeTCombine
|
|
||||||
(Workspaces.getWindowIconPixbufFromDesktopEntry size (workspaceCandidateInfo name))
|
|
||||||
(liftIO $ loadPixbufByName size name)
|
|
||||||
|
|
||||||
workspaceManualIconGetter :: Workspaces.WindowIconPixbufGetter
|
|
||||||
workspaceManualIconGetter =
|
|
||||||
Workspaces.handleIconGetterException $ \size windowData ->
|
|
||||||
foldl maybeTCombine (return Nothing) $
|
|
||||||
map (workspaceIconFromCandidate size) (workspaceIconCandidates windowData)
|
|
||||||
|
|
||||||
fallbackIconPixbuf :: Int32 -> TaffyIO (Maybe Gdk.Pixbuf)
|
|
||||||
fallbackIconPixbuf size = do
|
|
||||||
let fallbackNames =
|
|
||||||
[ "application-x-executable",
|
|
||||||
"application",
|
|
||||||
"image-missing",
|
|
||||||
"gtk-missing-image",
|
|
||||||
"dialog-question",
|
|
||||||
"utilities-terminal",
|
|
||||||
"system-run",
|
|
||||||
"window"
|
|
||||||
]
|
|
||||||
tryNames =
|
|
||||||
foldl
|
|
||||||
maybeTCombine
|
|
||||||
(return Nothing)
|
|
||||||
(map (liftIO . loadPixbufByName size) fallbackNames)
|
|
||||||
result <- tryNames
|
|
||||||
case result of
|
|
||||||
Just _ -> return result
|
|
||||||
Nothing -> Just <$> pixBufFromColor size 0x5f5f5fff
|
|
||||||
|
|
||||||
workspaceFallbackIcon :: Workspaces.WindowIconPixbufGetter
|
|
||||||
workspaceFallbackIcon size _ =
|
|
||||||
fallbackIconPixbuf size
|
|
||||||
|
|
||||||
workspaceWindowIconGetter :: Workspaces.WindowIconPixbufGetter
|
|
||||||
workspaceWindowIconGetter =
|
|
||||||
chromeFaviconIconGetter chromeFaviconConfig
|
|
||||||
<|||> workspaceManualIconGetter
|
|
||||||
<|||> Workspaces.getWindowIconPixbufFromChrome
|
|
||||||
<|||> Workspaces.defaultGetWindowIconPixbuf
|
|
||||||
<|||> workspaceFallbackIcon
|
|
||||||
@@ -7,4 +7,3 @@ packages:
|
|||||||
taffybar/packages/status-notifier-item
|
taffybar/packages/status-notifier-item
|
||||||
taffybar/packages/dbus-menu
|
taffybar/packages/dbus-menu
|
||||||
taffybar/packages/dbus-hslogger
|
taffybar/packages/dbus-hslogger
|
||||||
taffybar/packages/gi-wireplumber
|
|
||||||
|
|||||||
18
dotfiles/config/taffybar/flake.lock
generated
18
dotfiles/config/taffybar/flake.lock
generated
@@ -136,17 +136,17 @@
|
|||||||
"xmonad-contrib": "xmonad-contrib"
|
"xmonad-contrib": "xmonad-contrib"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1778673962,
|
"lastModified": 1777319252,
|
||||||
"narHash": "sha256-GmHRMdrUIQpMf6k5gRjP9Mvx2WO0FvIEF1SPlxEpnas=",
|
"narHash": "sha256-mPft6i8ReJAvW2LdylFI6FF6NFGa1HMa3RNbisfAsbc=",
|
||||||
"owner": "taffybar",
|
"ref": "refs/heads/codex/fix-gdk-backend-strut-detection",
|
||||||
"repo": "taffybar",
|
"rev": "c2cee23fc57384cd322d589944129e6c31d4f0fd",
|
||||||
"rev": "08125b267c03232c560fce6259264cc9283d582e",
|
"revCount": 2288,
|
||||||
"type": "github"
|
"type": "git",
|
||||||
|
"url": "file:///home/imalison/dotfiles/dotfiles/config/taffybar/taffybar"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "taffybar",
|
"type": "git",
|
||||||
"repo": "taffybar",
|
"url": "file:///home/imalison/dotfiles/dotfiles/config/taffybar/taffybar"
|
||||||
"type": "github"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"weeder-nix": {
|
"weeder-nix": {
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
{
|
{
|
||||||
inputs = {
|
inputs = {
|
||||||
taffybar = {
|
taffybar = {
|
||||||
# Keep the default source usable in CI. Local iteration uses
|
# Use the local git checkout, not a raw path snapshot, so gitignored
|
||||||
# IMALISON_TAFFYBAR_LIVE_CHECKOUT below via `just switch-local-taffybar`.
|
# build artifacts like dist-newstyle/.worktrees/.direnv don't get copied
|
||||||
url = "github:taffybar/taffybar";
|
# into flake-input store sources.
|
||||||
|
url = "git+file:///home/imalison/dotfiles/dotfiles/config/taffybar/taffybar";
|
||||||
inputs.weeder-nix.inputs.pre-commit-hooks.inputs.nixpkgs.follows = "nixpkgs";
|
inputs.weeder-nix.inputs.pre-commit-hooks.inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
# Follow the vendored taffybar flake's pins so the config shell and the
|
# Follow the vendored taffybar flake's pins so the config shell and the
|
||||||
@@ -107,13 +108,6 @@
|
|||||||
{ })
|
{ })
|
||||||
(_: { doCheck = false; doHaddock = false; });
|
(_: { doCheck = false; doHaddock = false; });
|
||||||
|
|
||||||
gi-wireplumber =
|
|
||||||
pkgs.haskell.lib.overrideCabal
|
|
||||||
(hself.callCabal2nix "gi-wireplumber"
|
|
||||||
(localTaffybarSubdir "packages/gi-wireplumber")
|
|
||||||
{ })
|
|
||||||
(_: { doCheck = false; doHaddock = false; });
|
|
||||||
|
|
||||||
dbus-hslogger =
|
dbus-hslogger =
|
||||||
hself.callCabal2nix "dbus-hslogger"
|
hself.callCabal2nix "dbus-hslogger"
|
||||||
(localTaffybarSubdir "packages/dbus-hslogger")
|
(localTaffybarSubdir "packages/dbus-hslogger")
|
||||||
@@ -123,36 +117,25 @@
|
|||||||
# modules (e.g. System.Taffybar.Widget.ASUS) used by this config.
|
# modules (e.g. System.Taffybar.Widget.ASUS) used by this config.
|
||||||
taffybar = pkgs.haskell.lib.overrideCabal
|
taffybar = pkgs.haskell.lib.overrideCabal
|
||||||
(pkgs.haskell.lib.disableStaticLibraries
|
(pkgs.haskell.lib.disableStaticLibraries
|
||||||
(hself.callCabal2nix "taffybar" cleanedTaffybarSource {
|
(hself.callCabal2nix "taffybar" cleanedTaffybarSource { inherit (pkgs) gtk3; }))
|
||||||
inherit (pkgs) gtk3;
|
|
||||||
}))
|
|
||||||
(oa: {
|
(oa: {
|
||||||
doHaddock = false;
|
doHaddock = false;
|
||||||
doCheck = false;
|
doCheck = false;
|
||||||
# Needed for gi-gtk-layer-shell and gi-wireplumber introspection data.
|
# Legacy fix for older GHC (harmless on newer)
|
||||||
librarySystemDepends = (oa.librarySystemDepends or []) ++ [
|
postPatch = (oa.postPatch or "") + ''
|
||||||
pkgs.gtk-layer-shell
|
substituteInPlace src/System/Taffybar/DBus/Client/Util.hs \
|
||||||
pkgs.wireplumber
|
--replace-fail "import Control.Monad (forM)" \
|
||||||
];
|
"import Control.Monad (forM)
|
||||||
shellHook = ''
|
import Control.Applicative (liftA2)"
|
||||||
${oa.shellHook or ""}
|
|
||||||
export PKG_CONFIG_PATH="${pkgs.wireplumber.dev}/lib/pkgconfig:${pkgs.pipewire.dev}/lib/pkgconfig:''${PKG_CONFIG_PATH:-}"
|
|
||||||
export GI_GIR_PATH="${pkgs.wireplumber.dev}/share/gir-1.0:''${GI_GIR_PATH:-}"
|
|
||||||
export GI_TYPELIB_PATH="${pkgs.wireplumber}/lib/girepository-1.0:${pkgs.glib.out}/lib/girepository-1.0:''${GI_TYPELIB_PATH:-}"
|
|
||||||
export XDG_DATA_DIRS="${pkgs.wireplumber.dev}/share:''${XDG_DATA_DIRS:-}"
|
|
||||||
'';
|
'';
|
||||||
|
# Needed for gi-gtk-layer-shell (introspection data).
|
||||||
|
librarySystemDepends = (oa.librarySystemDepends or []) ++ [ pkgs.gtk-layer-shell ];
|
||||||
});
|
});
|
||||||
|
|
||||||
# gi-gtk-hs patching is now handled by taffybar's fixVersionNamePackages overlay
|
# gi-gtk-hs patching is now handled by taffybar's fixVersionNamePackages overlay
|
||||||
imalison-taffybar = pkgs.haskell.lib.overrideCabal
|
imalison-taffybar = pkgs.haskell.lib.addPkgconfigDepends (
|
||||||
(pkgs.haskell.lib.addPkgconfigDepends (
|
|
||||||
hself.callCabal2nix "imalison-taffybar"
|
hself.callCabal2nix "imalison-taffybar"
|
||||||
(pkgs.lib.sourceByRegex ./. [
|
(pkgs.lib.sourceByRegex ./. [ "taffybar.hs" "imalison-taffybar.cabal" ])
|
||||||
"taffybar.hs"
|
|
||||||
"imalison-taffybar.cabal"
|
|
||||||
"TaffybarConfig"
|
|
||||||
"TaffybarConfig/.*"
|
|
||||||
])
|
|
||||||
{ }
|
{ }
|
||||||
) [
|
) [
|
||||||
pkgs.util-linux.dev
|
pkgs.util-linux.dev
|
||||||
@@ -168,14 +151,7 @@
|
|||||||
pkgs.libxkbcommon.dev
|
pkgs.libxkbcommon.dev
|
||||||
pkgs.libepoxy.dev
|
pkgs.libepoxy.dev
|
||||||
pkgs.libxtst.out
|
pkgs.libxtst.out
|
||||||
])
|
|
||||||
(oa: {
|
|
||||||
configureFlags = (oa.configureFlags or []) ++ [
|
|
||||||
"--ghc-option=-optl-fuse-ld=bfd"
|
|
||||||
"--ld-option=-fuse-ld=bfd"
|
|
||||||
"--with-ld=ld.bfd"
|
|
||||||
];
|
];
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
# Avoid depending on xmonad.lib's helper functions, since parent flakes
|
# Avoid depending on xmonad.lib's helper functions, since parent flakes
|
||||||
@@ -203,7 +179,6 @@
|
|||||||
pkgs.librsvg
|
pkgs.librsvg
|
||||||
];
|
];
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
${hpkgs.taffybar.env.shellHook or ""}
|
|
||||||
# GHCi loads package DLL dependencies via the runtime linker, so it
|
# GHCi loads package DLL dependencies via the runtime linker, so it
|
||||||
# needs zlib on LD_LIBRARY_PATH in addition to the build-time -L flags.
|
# needs zlib on LD_LIBRARY_PATH in addition to the build-time -L flags.
|
||||||
export LD_LIBRARY_PATH="${pkgs.lib.makeLibraryPath [ pkgs.zlib ]}:''${LD_LIBRARY_PATH:-}"
|
export LD_LIBRARY_PATH="${pkgs.lib.makeLibraryPath [ pkgs.zlib ]}:''${LD_LIBRARY_PATH:-}"
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 174 148.18">
|
|
||||||
<path
|
|
||||||
fill="#d97757"
|
|
||||||
d="m 105.01,322.07 29.14,-16.35 0.49,-1.42 -0.49,-0.79 h -1.42 l -4.87,-0.3 -16.65,-0.45 -14.44,-0.6 -13.99,-0.75 -3.52,-0.75 -3.3,-4.35 0.34,-2.17 2.96,-1.99 4.24,0.37 9.37,0.64 14.06,0.97 10.2,0.6 15.11,1.57 h 2.4 l 0.34,-0.97 -0.82,-0.6 -0.64,-0.6 -14.55,-9.86 -15.75,-10.42 -8.25,-6 -4.46,-3.04 -2.25,-2.85 -0.97,-6.22 4.05,-4.46 5.44,0.37 1.39,0.37 5.51,4.24 11.77,9.11 15.37,11.32 2.25,1.87 0.9,-0.64 0.11,-0.45 -1.01,-1.69 -8.36,-15.11 -8.92,-15.37 -3.97,-6.37 -1.05,-3.82 c -0.37,-1.57 -0.64,-2.89 -0.64,-4.5 l 4.61,-6.26 2.55,-0.82 6.15,0.82 2.59,2.25 3.82,8.74 6.19,13.76 9.6,18.71 2.81,5.55 1.5,5.14 0.56,1.57 h 0.97 v -0.9 l 0.79,-10.54 1.46,-12.94 1.42,-16.65 0.49,-4.69 2.32,-5.62 4.61,-3.04 3.6,1.72 2.96,4.24 -0.41,2.74 -1.76,11.44 -3.45,17.92 -2.25,12 h 1.31 l 1.5,-1.5 6.07,-8.06 10.2,-12.75 4.5,-5.06 5.25,-5.59 3.37,-2.66 h 6.37 l 4.69,6.97 -2.1,7.2 -6.56,8.32 -5.44,7.05 -7.8,10.5 -4.87,8.4 0.45,0.67 1.16,-0.11 17.62,-3.75 9.52,-1.72 11.36,-1.95 5.14,2.4 0.56,2.44 -2.02,4.99 -12.15,3 -14.25,2.85 -21.22,5.02 -0.26,0.19 0.3,0.37 9.56,0.9 4.09,0.22 h 10.01 l 18.64,1.39 4.87,3.22 2.92,3.94 -0.49,3 -7.5,3.82 -10.12,-2.4 -23.62,-5.62 -8.1,-2.02 h -1.12 v 0.67 l 6.75,6.6 12.37,11.17 15.49,14.4 0.79,3.56 -1.99,2.81 -2.1,-0.3 -13.61,-10.24 -5.25,-4.61 -11.89,-10.01 h -0.79 v 1.05 l 2.74,4.01 14.47,21.75 0.75,6.67 -1.05,2.17 -3.75,1.31 -4.12,-0.75 -8.47,-11.89 -8.74,-13.39 -7.05,-12 -0.86,0.49 -4.16,44.81 -1.95,2.29 -4.5,1.72 -3.75,-2.85 -1.99,-4.61 1.99,-9.11 2.4,-11.89 1.95,-9.45 1.76,-11.74 1.05,-3.9 -0.07,-0.26 -0.86,0.11 -8.85,12.15 -13.46,18.19 -10.65,11.4 -2.55,1.01 -4.42,-2.29 0.41,-4.09 2.47,-3.64 14.74,-18.75 8.89,-11.62 5.74,-6.71 -0.04,-0.97 h -0.34 l -39.15,25.42 -6.97,0.9 -3,-2.81 0.37,-4.61 1.42,-1.5 11.77,-8.1 -0.04,0.04 z"
|
|
||||||
transform="translate(-75.96,-223.53)"
|
|
||||||
shape-rendering="optimizeQuality" />
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.9 KiB |
@@ -1,187 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
width="535"
|
|
||||||
height="535"
|
|
||||||
viewBox="0 0 501.56251 501.56249"
|
|
||||||
id="svg2"
|
|
||||||
version="1.1"
|
|
||||||
inkscape:version="1.3.2 (091e20ef0f, 2023-11-25)"
|
|
||||||
sodipodi:docname="nix-snowflake-colours.svg"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
|
||||||
xmlns:cc="http://creativecommons.org/ns#"
|
|
||||||
xmlns:dc="http://purl.org/dc/elements/1.1/">
|
|
||||||
<defs
|
|
||||||
id="defs4">
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
id="linearGradient5562">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#699ad7;stop-opacity:1"
|
|
||||||
offset="0"
|
|
||||||
id="stop5564" />
|
|
||||||
<stop
|
|
||||||
id="stop5566"
|
|
||||||
offset="0.24345198"
|
|
||||||
style="stop-color:#7eb1dd;stop-opacity:1" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#7ebae4;stop-opacity:1"
|
|
||||||
offset="1"
|
|
||||||
id="stop5568" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
id="linearGradient5053">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#415e9a;stop-opacity:1"
|
|
||||||
offset="0"
|
|
||||||
id="stop5055" />
|
|
||||||
<stop
|
|
||||||
id="stop5057"
|
|
||||||
offset="0.23168644"
|
|
||||||
style="stop-color:#4a6baf;stop-opacity:1" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#5277c3;stop-opacity:1"
|
|
||||||
offset="1"
|
|
||||||
id="stop5059" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient5562"
|
|
||||||
id="linearGradient4328"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="translate(70.650339,-1055.1511)"
|
|
||||||
x1="200.59668"
|
|
||||||
y1="351.41116"
|
|
||||||
x2="290.08701"
|
|
||||||
y2="506.18814" />
|
|
||||||
<linearGradient
|
|
||||||
inkscape:collect="always"
|
|
||||||
xlink:href="#linearGradient5053"
|
|
||||||
id="linearGradient4330"
|
|
||||||
gradientUnits="userSpaceOnUse"
|
|
||||||
gradientTransform="translate(864.69589,-1491.3405)"
|
|
||||||
x1="-584.19934"
|
|
||||||
y1="782.33563"
|
|
||||||
x2="-496.29703"
|
|
||||||
y2="937.71399" />
|
|
||||||
</defs>
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="base"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#666666"
|
|
||||||
borderopacity="1.0"
|
|
||||||
inkscape:pageopacity="0.0"
|
|
||||||
inkscape:pageshadow="2"
|
|
||||||
inkscape:zoom="0.70904368"
|
|
||||||
inkscape:cx="99.429699"
|
|
||||||
inkscape:cy="195.33352"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
inkscape:current-layer="layer3"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:window-width="1920"
|
|
||||||
inkscape:window-height="1050"
|
|
||||||
inkscape:window-x="1920"
|
|
||||||
inkscape:window-y="30"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:snap-global="true"
|
|
||||||
fit-margin-top="0"
|
|
||||||
fit-margin-left="0"
|
|
||||||
fit-margin-right="0"
|
|
||||||
fit-margin-bottom="0"
|
|
||||||
inkscape:showpageshadow="2"
|
|
||||||
inkscape:pagecheckerboard="0"
|
|
||||||
inkscape:deskcolor="#d1d1d1" />
|
|
||||||
<metadata
|
|
||||||
id="metadata7">
|
|
||||||
<rdf:RDF>
|
|
||||||
<cc:Work
|
|
||||||
rdf:about="">
|
|
||||||
<dc:format>image/svg+xml</dc:format>
|
|
||||||
<dc:type
|
|
||||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
|
||||||
</cc:Work>
|
|
||||||
</rdf:RDF>
|
|
||||||
</metadata>
|
|
||||||
<g
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer3"
|
|
||||||
inkscape:label="gradient-logo"
|
|
||||||
style="display:inline;opacity:1"
|
|
||||||
transform="translate(-156.41121,933.30685)">
|
|
||||||
<g
|
|
||||||
id="g2"
|
|
||||||
transform="matrix(0.99994059,0,0,0.99994059,-0.06321798,33.188377)"
|
|
||||||
style="stroke-width:1.00006">
|
|
||||||
<path
|
|
||||||
sodipodi:nodetypes="cccccccccc"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
id="path3336-6"
|
|
||||||
d="m 309.54892,-710.38827 122.19683,211.67512 -56.15706,0.5268 -32.6236,-56.8692 -32.85645,56.5653 -27.90237,-0.011 -14.29086,-24.6896 46.81047,-80.4901 -33.22946,-57.8257 z"
|
|
||||||
style="opacity:1;fill:url(#linearGradient4328);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3.00018;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
|
||||||
<use
|
|
||||||
height="100%"
|
|
||||||
width="100%"
|
|
||||||
transform="rotate(60,407.11155,-715.78724)"
|
|
||||||
id="use3439-6"
|
|
||||||
inkscape:transform-center-y="151.59082"
|
|
||||||
inkscape:transform-center-x="124.43045"
|
|
||||||
xlink:href="#path3336-6"
|
|
||||||
y="0"
|
|
||||||
x="0"
|
|
||||||
style="stroke-width:1.00006" />
|
|
||||||
<use
|
|
||||||
height="100%"
|
|
||||||
width="100%"
|
|
||||||
transform="rotate(-60,407.31177,-715.70016)"
|
|
||||||
id="use3445-0"
|
|
||||||
inkscape:transform-center-y="75.573958"
|
|
||||||
inkscape:transform-center-x="-168.20651"
|
|
||||||
xlink:href="#path3336-6"
|
|
||||||
y="0"
|
|
||||||
x="0"
|
|
||||||
style="stroke-width:1.00006" />
|
|
||||||
<use
|
|
||||||
height="100%"
|
|
||||||
width="100%"
|
|
||||||
transform="rotate(180,407.41868,-715.7565)"
|
|
||||||
id="use3449-5"
|
|
||||||
inkscape:transform-center-y="-139.94592"
|
|
||||||
inkscape:transform-center-x="59.669705"
|
|
||||||
xlink:href="#path3336-6"
|
|
||||||
y="0"
|
|
||||||
x="0"
|
|
||||||
style="stroke-width:1.00006" />
|
|
||||||
<path
|
|
||||||
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:url(#linearGradient4330);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:3.00018;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
|
||||||
d="m 309.54892,-710.38827 122.19683,211.67512 -56.15706,0.5268 -32.6236,-56.8692 -32.85645,56.5653 -27.90237,-0.011 -14.29086,-24.6896 46.81047,-80.4901 -33.22946,-57.8256 z"
|
|
||||||
id="path4260-0"
|
|
||||||
inkscape:connector-curvature="0"
|
|
||||||
sodipodi:nodetypes="cccccccccc" />
|
|
||||||
<use
|
|
||||||
height="100%"
|
|
||||||
width="100%"
|
|
||||||
transform="rotate(120,407.33916,-716.08356)"
|
|
||||||
id="use4354-5"
|
|
||||||
xlink:href="#path4260-0"
|
|
||||||
y="0"
|
|
||||||
x="0"
|
|
||||||
style="display:inline;stroke-width:1.00006" />
|
|
||||||
<use
|
|
||||||
height="100%"
|
|
||||||
width="100%"
|
|
||||||
transform="rotate(-120,407.28823,-715.86995)"
|
|
||||||
id="use4362-2"
|
|
||||||
xlink:href="#path4260-0"
|
|
||||||
y="0"
|
|
||||||
x="0"
|
|
||||||
style="display:inline;stroke-width:1.00006" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 6.4 KiB |
@@ -1,5 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 158.7128 157.296">
|
|
||||||
<!-- Generator: Adobe Illustrator 29.2.1, SVG Export Plug-In . SVG Version: 2.1.0 Build 116) -->
|
|
||||||
<path fill="#e7e4ee" d="M60.8734,57.2556v-14.9432c0-1.2586.4722-2.2029,1.5728-2.8314l30.0443-17.3023c4.0899-2.3593,8.9662-3.4599,13.9988-3.4599,18.8759,0,30.8307,14.6289,30.8307,30.2006,0,1.1007,0,2.3593-.158,3.6178l-31.1446-18.2467c-1.8872-1.1006-3.7754-1.1006-5.6629,0l-39.4812,22.9651ZM131.0276,115.4561v-35.7074c0-2.2028-.9446-3.7756-2.8318-4.8763l-39.481-22.9651,12.8982-7.3934c1.1007-.6285,2.0453-.6285,3.1458,0l30.0441,17.3024c8.6523,5.0341,14.4708,15.7296,14.4708,26.1107,0,11.9539-7.0769,22.965-18.2461,27.527v.0021ZM51.593,83.9964l-12.8982-7.5497c-1.1007-.6285-1.5728-1.5728-1.5728-2.8314v-34.6048c0-16.8303,12.8982-29.5722,30.3585-29.5722,6.607,0,12.7403,2.2029,17.9324,6.1349l-30.987,17.9324c-1.8871,1.1007-2.8314,2.6735-2.8314,4.8764v45.6159l-.0014-.0015ZM79.3562,100.0403l-18.4829-10.3811v-22.0209l18.4829-10.3811,18.4812,10.3811v22.0209l-18.4812,10.3811ZM91.2319,147.8591c-6.607,0-12.7403-2.2031-17.9324-6.1344l30.9866-17.9333c1.8872-1.1005,2.8318-2.6728,2.8318-4.8759v-45.616l13.0564,7.5498c1.1005.6285,1.5723,1.5728,1.5723,2.8314v34.6051c0,16.8297-13.0564,29.5723-30.5147,29.5723v.001ZM53.9522,112.7822l-30.0443-17.3024c-8.652-5.0343-14.471-15.7296-14.471-26.1107,0-12.1119,7.2356-22.9652,18.403-27.5272v35.8634c0,2.2028.9443,3.7756,2.8314,4.8763l39.3248,22.8068-12.8982,7.3938c-1.1007.6287-2.045.6287-3.1456,0ZM52.2229,138.5791c-17.7745,0-30.8306-13.3713-30.8306-29.8871,0-1.2585.1578-2.5169.3143-3.7754l30.987,17.9323c1.8871,1.1005,3.7757,1.1005,5.6628,0l39.4811-22.807v14.9435c0,1.2585-.4721,2.2021-1.5728,2.8308l-30.0443,17.3025c-4.0898,2.359-8.9662,3.4605-13.9989,3.4605h.0014ZM91.2319,157.296c19.0327,0,34.9188-13.5272,38.5383-31.4594,17.6164-4.562,28.9425-21.0779,28.9425-37.908,0-11.0112-4.719-21.7066-13.2133-29.4143.7867-3.3035,1.2595-6.607,1.2595-9.909,0-22.4929-18.2471-39.3247-39.3251-39.3247-4.2461,0-8.3363.6285-12.4262,2.045-7.0792-6.9213-16.8318-11.3254-27.5271-11.3254-19.0331,0-34.9191,13.5268-38.5384,31.4591C11.3255,36.0212,0,52.5373,0,69.3675c0,11.0112,4.7184,21.7065,13.2125,29.4142-.7865,3.3035-1.2586,6.6067-1.2586,9.9092,0,22.4923,18.2466,39.3241,39.3248,39.3241,4.2462,0,8.3362-.6277,12.426-2.0441,7.0776,6.921,16.8302,11.3251,27.5271,11.3251Z"/>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 2.4 KiB |
@@ -13,12 +13,6 @@ cabal-version: >=1.10
|
|||||||
executable taffybar
|
executable taffybar
|
||||||
hs-source-dirs: .
|
hs-source-dirs: .
|
||||||
main-is: taffybar.hs
|
main-is: taffybar.hs
|
||||||
other-modules: TaffybarConfig.Config
|
|
||||||
, TaffybarConfig.ChromeFavicons
|
|
||||||
, TaffybarConfig.Host
|
|
||||||
, TaffybarConfig.Widgets
|
|
||||||
, TaffybarConfig.WidgetUtil
|
|
||||||
, TaffybarConfig.Workspaces
|
|
||||||
ghc-options: -threaded -rtsopts -with-rtsopts=-N
|
ghc-options: -threaded -rtsopts -with-rtsopts=-N
|
||||||
ghc-prof-options: -fprof-auto
|
ghc-prof-options: -fprof-auto
|
||||||
build-depends: base
|
build-depends: base
|
||||||
|
|||||||
@@ -7,24 +7,20 @@ pkill -u "$USER" -x taffybar || true
|
|||||||
|
|
||||||
cd "$root"
|
cd "$root"
|
||||||
|
|
||||||
# Hyprland can restart and change the instance signature, and controller shells
|
# Hyprland can restart and change the instance signature, leaving old shells with
|
||||||
# can retain stale X11 session variables. Prefer the live Hyprland instance when
|
# a stale HYPRLAND_INSTANCE_SIGNATURE. Fix it before launching taffybar so any
|
||||||
# one is available so taffybar starts on the Wayland backend.
|
# `hyprctl` calls inside the bar work.
|
||||||
if command -v hyprctl >/dev/null 2>&1 && command -v jq >/dev/null 2>&1; then
|
if command -v hyprctl >/dev/null 2>&1 && command -v jq >/dev/null 2>&1; then
|
||||||
|
if ! hyprctl monitors -j >/dev/null 2>&1; then
|
||||||
instances_json="$(hyprctl instances -j 2>/dev/null || true)"
|
instances_json="$(hyprctl instances -j 2>/dev/null || true)"
|
||||||
if [[ -n "${instances_json:-}" ]]; then
|
|
||||||
if [[ -n "${WAYLAND_DISPLAY:-}" ]]; then
|
if [[ -n "${WAYLAND_DISPLAY:-}" ]]; then
|
||||||
inst_row="$(printf '%s\n' "$instances_json" | jq -r --arg sock "$WAYLAND_DISPLAY" '.[] | select(.instance and .wl_socket and .wl_socket == $sock) | [.instance, .wl_socket] | @tsv' 2>/dev/null | head -n1 || true)"
|
inst="$(printf '%s\n' "$instances_json" | jq -r --arg sock "$WAYLAND_DISPLAY" '.[] | select(.wl_socket == $sock) | .instance' 2>/dev/null | head -n1 || true)"
|
||||||
else
|
else
|
||||||
inst_row="$(printf '%s\n' "$instances_json" | jq -r '.[] | select(.instance and .wl_socket) | [.instance, .wl_socket] | @tsv' 2>/dev/null | head -n1 || true)"
|
inst="$(printf '%s\n' "$instances_json" | jq -r '.[0].instance // empty' 2>/dev/null || true)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n "${inst_row:-}" ]]; then
|
if [[ -n "${inst:-}" ]]; then
|
||||||
read -r inst wl_socket <<<"$inst_row"
|
|
||||||
export HYPRLAND_INSTANCE_SIGNATURE="$inst"
|
export HYPRLAND_INSTANCE_SIGNATURE="$inst"
|
||||||
export WAYLAND_DISPLAY="$wl_socket"
|
|
||||||
export XDG_SESSION_TYPE=wayland
|
|
||||||
export GDK_BACKEND=wayland
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -4,24 +4,20 @@ set -euo pipefail
|
|||||||
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
cd "$root"
|
cd "$root"
|
||||||
|
|
||||||
# Hyprland can restart and change the instance signature, and controller shells
|
# Hyprland can restart and change the instance signature, leaving old shells with
|
||||||
# can retain stale X11 session variables. Prefer the live Hyprland instance when
|
# a stale HYPRLAND_INSTANCE_SIGNATURE. Fix it before launching taffybar so any
|
||||||
# one is available so taffybar starts on the Wayland backend.
|
# `hyprctl` calls inside the bar work.
|
||||||
if command -v hyprctl >/dev/null 2>&1 && command -v jq >/dev/null 2>&1; then
|
if command -v hyprctl >/dev/null 2>&1 && command -v jq >/dev/null 2>&1; then
|
||||||
|
if ! hyprctl monitors -j >/dev/null 2>&1; then
|
||||||
instances_json="$(hyprctl instances -j 2>/dev/null || true)"
|
instances_json="$(hyprctl instances -j 2>/dev/null || true)"
|
||||||
if [[ -n "${instances_json:-}" ]]; then
|
|
||||||
if [[ -n "${WAYLAND_DISPLAY:-}" ]]; then
|
if [[ -n "${WAYLAND_DISPLAY:-}" ]]; then
|
||||||
inst_row="$(printf '%s\n' "$instances_json" | jq -r --arg sock "$WAYLAND_DISPLAY" '.[] | select(.instance and .wl_socket and .wl_socket == $sock) | [.instance, .wl_socket] | @tsv' 2>/dev/null | head -n1 || true)"
|
inst="$(printf '%s\n' "$instances_json" | jq -r --arg sock "$WAYLAND_DISPLAY" '.[] | select(.wl_socket == $sock) | .instance' 2>/dev/null | head -n1 || true)"
|
||||||
else
|
else
|
||||||
inst_row="$(printf '%s\n' "$instances_json" | jq -r '.[] | select(.instance and .wl_socket) | [.instance, .wl_socket] | @tsv' 2>/dev/null | head -n1 || true)"
|
inst="$(printf '%s\n' "$instances_json" | jq -r '.[0].instance // empty' 2>/dev/null || true)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n "${inst_row:-}" ]]; then
|
if [[ -n "${inst:-}" ]]; then
|
||||||
read -r inst wl_socket <<<"$inst_row"
|
|
||||||
export HYPRLAND_INSTANCE_SIGNATURE="$inst"
|
export HYPRLAND_INSTANCE_SIGNATURE="$inst"
|
||||||
export WAYLAND_DISPLAY="$wl_socket"
|
|
||||||
export XDG_SESSION_TYPE=wayland
|
|
||||||
export GDK_BACKEND=wayland
|
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -3,20 +3,18 @@ max_visible_icons: 0
|
|||||||
priorities:
|
priorities:
|
||||||
- key: item-id:nm-applet
|
- key: item-id:nm-applet
|
||||||
priority: 3
|
priority: 3
|
||||||
- key: icon-name:gitea
|
|
||||||
priority: 2
|
|
||||||
- key: icon-name:github
|
- key: icon-name:github
|
||||||
priority: 2
|
priority: 2
|
||||||
- key: icon-name:gmail
|
- key: icon-name:gitea
|
||||||
priority: 2
|
|
||||||
- key: icon-name:password
|
|
||||||
priority: 1
|
priority: 1
|
||||||
- key: icon-name:text-org
|
- key: icon-name:gmail
|
||||||
priority: 1
|
priority: 1
|
||||||
- key: item-id:git-sync-rs
|
- key: item-id:git-sync-rs
|
||||||
priority: 1
|
priority: 1
|
||||||
- key: process:slack
|
- key: process:slack
|
||||||
priority: 1
|
priority: 1
|
||||||
|
- key: icon-name:blueman-tray
|
||||||
|
priority: 0
|
||||||
- key: icon-name:kdeconnectindicatordark
|
- key: icon-name:kdeconnectindicatordark
|
||||||
priority: 0
|
priority: 0
|
||||||
- key: item-id:flameshot
|
- key: item-id:flameshot
|
||||||
@@ -25,12 +23,8 @@ priorities:
|
|||||||
priority: 0
|
priority: 0
|
||||||
- key: item-id:udiskie
|
- key: item-id:udiskie
|
||||||
priority: 0
|
priority: 0
|
||||||
- key: icon-name::1.89
|
|
||||||
priority: -1
|
|
||||||
- key: icon-name:audio-volume-low
|
- key: icon-name:audio-volume-low
|
||||||
priority: -1
|
priority: -1
|
||||||
- key: icon-name:blueman-tray
|
|
||||||
priority: -1
|
|
||||||
- key: item-id:blueman
|
- key: item-id:blueman
|
||||||
priority: -1
|
priority: -1
|
||||||
- key: item-id:chrome_status_icon_1
|
- key: item-id:chrome_status_icon_1
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
@import url("taffybar.css");
|
|
||||||
|
|
||||||
/* Host-specific density tweaks for strixi-minaj. */
|
|
||||||
.taffy-box {
|
|
||||||
font-size: 9.5pt;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.outer-pad,
|
|
||||||
.workspaces .outer-pad {
|
|
||||||
border-radius: 8px;
|
|
||||||
margin: 4px 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.inner-pad,
|
|
||||||
.workspaces .inner-pad {
|
|
||||||
border-radius: 7px;
|
|
||||||
padding-top: 2px;
|
|
||||||
padding-bottom: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.workspaces .contents {
|
|
||||||
border-radius: 7px;
|
|
||||||
padding: 0px 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.workspaces .overlay-box .workspace-label {
|
|
||||||
padding: 1px 4px 4px 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.visible .contents,
|
|
||||||
.workspaces .window-icon-container,
|
|
||||||
.workspaces .window-icon-container.active {
|
|
||||||
padding-top: 1px;
|
|
||||||
padding-bottom: 1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.auto-size-image,
|
|
||||||
.sni-tray {
|
|
||||||
padding-top: 2px;
|
|
||||||
padding-bottom: 2px;
|
|
||||||
}
|
|
||||||
Submodule dotfiles/config/taffybar/taffybar updated: 4c612d4457...59e3c75990
@@ -40,16 +40,6 @@
|
|||||||
-GtkLabel-justify: left;
|
-GtkLabel-justify: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Compact logo column for stacked AI usage sections. */
|
|
||||||
.usage-section.icon-label > .icon {
|
|
||||||
min-width: 22px;
|
|
||||||
padding-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.usage-section.icon-label > .label {
|
|
||||||
padding-right: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Compact two-line RAM/SWAP widget: reduce icon padding a bit. */
|
/* Compact two-line RAM/SWAP widget: reduce icon padding a bit. */
|
||||||
.ram-swap .icon-label > .icon {
|
.ram-swap .icon-label > .icon {
|
||||||
/* Different glyphs have different visual widths; fix the icon column width
|
/* Different glyphs have different visual widths; fix the icon column width
|
||||||
@@ -129,15 +119,6 @@
|
|||||||
padding: 0px;
|
padding: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This must outrank the end-widget nth-last-child color rotation. Otherwise
|
|
||||||
the collapsed MPRIS end widget can still render as a thin blue strip. */
|
|
||||||
.taffy-box > .outer-pad.end-widget.mpris.no-visible-children {
|
|
||||||
background-color: @transparent;
|
|
||||||
background-image: none;
|
|
||||||
box-shadow: none;
|
|
||||||
border-width: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Workspaces styling */
|
/* Workspaces styling */
|
||||||
|
|
||||||
/* Reset workspace .outer-pad pills to default styling so the nth-child color
|
/* Reset workspace .outer-pad pills to default styling so the nth-child color
|
||||||
@@ -249,27 +230,16 @@
|
|||||||
/* Don't give each window icon its own background/border; the workspace
|
/* Don't give each window icon its own background/border; the workspace
|
||||||
squircle is the background. */
|
squircle is the background. */
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border: 1px solid transparent;
|
border: 0px;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
padding: 2px 4px;
|
padding: 0px 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.workspaces .window-icon-container.active {
|
.workspaces .window-icon-container.active {
|
||||||
background-color: rgba(255, 255, 255, 0.10);
|
background-color: rgba(255, 255, 255, 0.10);
|
||||||
border-color: rgba(59, 130, 246, 0.76);
|
border: 1px solid rgba(255, 255, 255, 0.5);
|
||||||
border-radius: 7px;
|
border-radius: 7px;
|
||||||
}
|
padding: 2px 4px;
|
||||||
|
|
||||||
.workspaces .window-icon-container.pinned {
|
|
||||||
border-color: rgba(255, 77, 93, 0.74);
|
|
||||||
box-shadow: inset 0 -2px 0 rgba(255, 77, 93, 0.72);
|
|
||||||
}
|
|
||||||
|
|
||||||
.workspaces .window-icon-container.pinned.active {
|
|
||||||
border-color: rgba(237, 180, 67, 0.95);
|
|
||||||
box-shadow:
|
|
||||||
inset 0 -2px 0 rgba(237, 180, 67, 0.9),
|
|
||||||
0 0 0 1px rgba(237, 180, 67, 0.22);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.workspaces .active .contents,
|
.workspaces .active .contents,
|
||||||
|
|||||||
@@ -1,17 +1,667 @@
|
|||||||
|
{-# LANGUAGE DataKinds #-}
|
||||||
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
|
|
||||||
module Main (main) where
|
module Main (main) where
|
||||||
|
|
||||||
|
import Control.Concurrent (threadDelay)
|
||||||
|
import Control.Monad (void, when)
|
||||||
|
import Control.Monad.IO.Class (MonadIO, liftIO)
|
||||||
|
import Control.Monad.Trans.Reader (asks)
|
||||||
|
import Data.Char (toLower)
|
||||||
|
import Data.Foldable (for_)
|
||||||
|
import Data.GI.Base (castTo)
|
||||||
|
import Data.Int (Int32)
|
||||||
|
import Data.List (nub)
|
||||||
|
import qualified Data.Map as M
|
||||||
|
import Data.Maybe (fromMaybe, mapMaybe)
|
||||||
|
import Data.Ratio ((%))
|
||||||
|
import Data.Text (Text)
|
||||||
|
import qualified Data.Text as T
|
||||||
|
import qualified GI.Gdk as Gdk
|
||||||
|
import qualified GI.GdkPixbuf.Objects.Pixbuf as Gdk
|
||||||
|
import qualified GI.Gtk as Gtk
|
||||||
|
import qualified GI.Pango as Pango
|
||||||
import Network.HostName (getHostName)
|
import Network.HostName (getHostName)
|
||||||
|
import qualified StatusNotifier.Tray as SNITray
|
||||||
|
import System.Environment (lookupEnv)
|
||||||
import System.Environment.XDG.BaseDir (getUserConfigFile)
|
import System.Environment.XDG.BaseDir (getUserConfigFile)
|
||||||
import System.Log.Logger (Priority (WARNING), rootLoggerName, setLevel, updateGlobalLogger)
|
import System.Log.Logger (Priority (WARNING), rootLoggerName, setLevel, updateGlobalLogger)
|
||||||
|
import System.Process (spawnCommand)
|
||||||
import System.Taffybar (startTaffybar)
|
import System.Taffybar (startTaffybar)
|
||||||
import System.Taffybar.Context (appendHook, detectBackend)
|
import System.Taffybar.Context
|
||||||
|
( Backend (BackendWayland, BackendX11),
|
||||||
|
TaffyIO,
|
||||||
|
backend,
|
||||||
|
detectBackend,
|
||||||
|
runX11Def,
|
||||||
|
)
|
||||||
import System.Taffybar.DBus
|
import System.Taffybar.DBus
|
||||||
import System.Taffybar.DBus.Toggle
|
import System.Taffybar.DBus.Toggle
|
||||||
import System.Taffybar.Hooks (withLogLevels)
|
import System.Taffybar.Hooks (withLogLevels)
|
||||||
import System.Taffybar.Information.ChromeWindowInfo (registerChromeWindowInfoRefreshRequests)
|
import System.Taffybar.Information.EWMHDesktopInfo (WorkspaceId (..))
|
||||||
import System.Taffybar.SimpleConfig (toTaffybarConfig)
|
import System.Taffybar.Information.Memory (MemoryInfo (..), parseMeminfo)
|
||||||
import TaffybarConfig.Config (mkSimpleTaffyConfig)
|
import qualified System.Taffybar.Information.Workspaces.Model as WorkspaceModel
|
||||||
import TaffybarConfig.Host (cssFilesForHost)
|
import System.Taffybar.Information.X11DesktopInfo
|
||||||
|
import System.Taffybar.SimpleConfig
|
||||||
|
import System.Taffybar.Util (getPixbufFromFilePath, maybeTCombine, postGUIASync, (<|||>))
|
||||||
|
import System.Taffybar.Widget
|
||||||
|
import qualified System.Taffybar.Widget.ASUS as ASUS
|
||||||
|
import System.Taffybar.Widget.AnthropicUsage (anthropicUsageStackNew)
|
||||||
|
import System.Taffybar.Widget.CPUMonitor (cpuMonitorNew)
|
||||||
|
import System.Taffybar.Widget.Generic.Graph (GraphConfig (..), GraphDirection (..), GraphStyle (..), defaultGraphConfig)
|
||||||
|
import qualified System.Taffybar.Widget.NetworkManager as NetworkManager
|
||||||
|
import System.Taffybar.Widget.OpenAIUsage (openAIUsageStackNew)
|
||||||
|
import qualified System.Taffybar.Widget.PulseAudio as PulseAudio
|
||||||
|
import System.Taffybar.Widget.SNIMenu (withNmAppletMenu)
|
||||||
|
import System.Taffybar.Widget.SNITray
|
||||||
|
( CollapsibleSNITrayParams (..),
|
||||||
|
SNITrayConfig (..),
|
||||||
|
defaultCollapsibleSNITrayParams,
|
||||||
|
defaultSNITrayConfig,
|
||||||
|
)
|
||||||
|
import System.Taffybar.Widget.SNITray.PrioritizedCollapsible
|
||||||
|
( PrioritizedCollapsibleSNITrayParams (..),
|
||||||
|
defaultPrioritizedCollapsibleSNITrayParams,
|
||||||
|
sniTrayPrioritizedCollapsibleNewFromParams,
|
||||||
|
)
|
||||||
|
import qualified System.Taffybar.Widget.ScreenLock as ScreenLock
|
||||||
|
import System.Taffybar.Widget.Util (backgroundLoop, buildContentsBox, buildIconLabelBox, loadPixbufByName, widgetSetClassGI)
|
||||||
|
import qualified System.Taffybar.Widget.Wlsunset as Wlsunset
|
||||||
|
import qualified System.Taffybar.Widget.Workspaces as Workspaces
|
||||||
|
import System.Taffybar.WindowIcon (pixBufFromColor)
|
||||||
|
import Text.Printf (printf)
|
||||||
|
import Text.Read (readMaybe)
|
||||||
|
|
||||||
|
-- | Wrap the widget in a "TaffyBox" (via 'buildContentsBox') and add a CSS class.
|
||||||
|
decorateWithClassAndBox :: (MonadIO m) => Text -> Gtk.Widget -> m Gtk.Widget
|
||||||
|
decorateWithClassAndBox klass widget = do
|
||||||
|
boxed <- buildContentsBox widget
|
||||||
|
widgetSetClassGI boxed klass
|
||||||
|
|
||||||
|
decorateWithClassAndBoxM :: (MonadIO m) => Text -> m Gtk.Widget -> m Gtk.Widget
|
||||||
|
decorateWithClassAndBoxM klass builder =
|
||||||
|
builder >>= decorateWithClassAndBox klass
|
||||||
|
|
||||||
|
forEachLabelRecursively :: Gtk.Widget -> (Gtk.Label -> IO ()) -> IO ()
|
||||||
|
forEachLabelRecursively widget action = do
|
||||||
|
maybeLabel <- castTo Gtk.Label widget
|
||||||
|
for_ maybeLabel action
|
||||||
|
|
||||||
|
maybeContainer <- castTo Gtk.Container widget
|
||||||
|
case maybeContainer of
|
||||||
|
Just container ->
|
||||||
|
Gtk.containerGetChildren container >>= mapM_ (`forEachLabelRecursively` action)
|
||||||
|
Nothing -> pure ()
|
||||||
|
|
||||||
|
setLabelAlignmentRecursively :: Float -> Gtk.Justification -> Gtk.Widget -> IO ()
|
||||||
|
setLabelAlignmentRecursively xalign justify widget =
|
||||||
|
forEachLabelRecursively widget $ \label -> do
|
||||||
|
Gtk.labelSetXalign label xalign
|
||||||
|
Gtk.labelSetJustify label justify
|
||||||
|
|
||||||
|
setFixedLabelWidth :: Int32 -> Gtk.Label -> IO ()
|
||||||
|
setFixedLabelWidth width label = do
|
||||||
|
Gtk.labelSetWidthChars label width
|
||||||
|
Gtk.labelSetMaxWidthChars label width
|
||||||
|
Gtk.labelSetEllipsize label Pango.EllipsizeModeEnd
|
||||||
|
|
||||||
|
-- ** X11 Workspaces
|
||||||
|
|
||||||
|
x11FullWorkspaceNames :: X11Property [(WorkspaceId, String)]
|
||||||
|
x11FullWorkspaceNames =
|
||||||
|
go <$> readAsListOfString Nothing "_NET_DESKTOP_FULL_NAMES"
|
||||||
|
where
|
||||||
|
go = zip [WorkspaceId i | i <- [0 ..]]
|
||||||
|
|
||||||
|
remapNSP :: String -> String
|
||||||
|
remapNSP "NSP" = "S"
|
||||||
|
remapNSP n = n
|
||||||
|
|
||||||
|
workspaceLabelSetter :: WorkspaceModel.WorkspaceInfo -> TaffyIO String
|
||||||
|
workspaceLabelSetter workspace = do
|
||||||
|
backendType <- asks backend
|
||||||
|
let identity = WorkspaceModel.workspaceIdentity workspace
|
||||||
|
fallbackLabel = remapNSP $ T.unpack (WorkspaceModel.workspaceName identity)
|
||||||
|
case (backendType, WorkspaceModel.workspaceNumericId identity) of
|
||||||
|
(BackendX11, Just workspaceId) -> do
|
||||||
|
fullNames <- runX11Def [] x11FullWorkspaceNames
|
||||||
|
return $ remapNSP $ fromMaybe fallbackLabel (lookup (WorkspaceId workspaceId) fullNames)
|
||||||
|
_ -> return fallbackLabel
|
||||||
|
|
||||||
|
-- ** Logging
|
||||||
|
|
||||||
|
-- ** Hyprland Icon Finding
|
||||||
|
|
||||||
|
iconRemap :: [(Text, [Text])]
|
||||||
|
iconRemap =
|
||||||
|
[ ("spotify", ["spotify-client", "spotify"])
|
||||||
|
]
|
||||||
|
|
||||||
|
iconRemapMap :: M.Map Text [Text]
|
||||||
|
iconRemapMap =
|
||||||
|
M.fromList [(T.toLower k, v) | (k, v) <- iconRemap]
|
||||||
|
|
||||||
|
lookupIconRemap :: Text -> [Text]
|
||||||
|
lookupIconRemap name = fromMaybe [] $ M.lookup (T.toLower name) iconRemapMap
|
||||||
|
|
||||||
|
iconNameVariants :: Text -> [Text]
|
||||||
|
iconNameVariants raw =
|
||||||
|
let lower = T.toLower raw
|
||||||
|
stripped = fromMaybe lower (T.stripSuffix ".desktop" lower)
|
||||||
|
suffixes = ["-gtk", "-client", "-desktop"]
|
||||||
|
stripSuffixes name =
|
||||||
|
let variants = mapMaybe (`T.stripSuffix` name) suffixes
|
||||||
|
in nub $ variants ++ [name]
|
||||||
|
baseNames = stripSuffixes stripped ++ [raw]
|
||||||
|
toDash c
|
||||||
|
| c == ' ' || c == '_' || c == '.' || c == '/' = '-'
|
||||||
|
| otherwise = c
|
||||||
|
toUnderscore c
|
||||||
|
| c == ' ' || c == '-' || c == '.' || c == '/' = '_'
|
||||||
|
| otherwise = c
|
||||||
|
variantsFor name =
|
||||||
|
let dotted =
|
||||||
|
case T.splitOn "." name of
|
||||||
|
[] -> name
|
||||||
|
xs -> last xs
|
||||||
|
dashed = T.map toDash name
|
||||||
|
dashedDotted = T.map toDash dotted
|
||||||
|
underscored = T.map toUnderscore name
|
||||||
|
underscoredDotted = T.map toUnderscore dotted
|
||||||
|
in [dotted, dashed, dashedDotted, underscored, underscoredDotted, name]
|
||||||
|
in nub $ concatMap variantsFor baseNames
|
||||||
|
|
||||||
|
workspaceIconCandidates :: WorkspaceModel.WindowInfo -> [Text]
|
||||||
|
workspaceIconCandidates windowData =
|
||||||
|
let baseNames = WorkspaceModel.windowClassHints windowData
|
||||||
|
remapped = concatMap lookupIconRemap baseNames
|
||||||
|
remappedExpanded = concatMap iconNameVariants remapped
|
||||||
|
baseExpanded = concatMap iconNameVariants baseNames
|
||||||
|
in nub (remappedExpanded ++ baseExpanded)
|
||||||
|
|
||||||
|
isPathCandidate :: Text -> Bool
|
||||||
|
isPathCandidate name =
|
||||||
|
T.isInfixOf "/" name
|
||||||
|
|| any (`T.isSuffixOf` name) [".png", ".svg", ".xpm"]
|
||||||
|
|
||||||
|
workspaceCandidateInfo :: Text -> WorkspaceModel.WindowInfo
|
||||||
|
workspaceCandidateInfo name =
|
||||||
|
WorkspaceModel.WindowInfo
|
||||||
|
{ WorkspaceModel.windowIdentity = WorkspaceModel.HyprlandWindowIdentity "",
|
||||||
|
WorkspaceModel.windowTitle = "",
|
||||||
|
WorkspaceModel.windowClassHints = [name],
|
||||||
|
WorkspaceModel.windowPosition = Nothing,
|
||||||
|
WorkspaceModel.windowUrgent = False,
|
||||||
|
WorkspaceModel.windowActive = False,
|
||||||
|
WorkspaceModel.windowMinimized = False
|
||||||
|
}
|
||||||
|
|
||||||
|
workspaceIconFromCandidate :: Int32 -> Text -> TaffyIO (Maybe Gdk.Pixbuf)
|
||||||
|
workspaceIconFromCandidate size name
|
||||||
|
| isPathCandidate name =
|
||||||
|
liftIO $ getPixbufFromFilePath (T.unpack name)
|
||||||
|
| otherwise =
|
||||||
|
maybeTCombine
|
||||||
|
(Workspaces.getWindowIconPixbufFromDesktopEntry size (workspaceCandidateInfo name))
|
||||||
|
(liftIO $ loadPixbufByName size name)
|
||||||
|
|
||||||
|
workspaceManualIconGetter :: Workspaces.WindowIconPixbufGetter
|
||||||
|
workspaceManualIconGetter =
|
||||||
|
Workspaces.handleIconGetterException $ \size windowData ->
|
||||||
|
foldl maybeTCombine (return Nothing) $
|
||||||
|
map (workspaceIconFromCandidate size) (workspaceIconCandidates windowData)
|
||||||
|
|
||||||
|
fallbackIconPixbuf :: Int32 -> TaffyIO (Maybe Gdk.Pixbuf)
|
||||||
|
fallbackIconPixbuf size = do
|
||||||
|
let fallbackNames =
|
||||||
|
[ "application-x-executable",
|
||||||
|
"application",
|
||||||
|
"image-missing",
|
||||||
|
"gtk-missing-image",
|
||||||
|
"dialog-question",
|
||||||
|
"utilities-terminal",
|
||||||
|
"system-run",
|
||||||
|
"window"
|
||||||
|
]
|
||||||
|
tryNames =
|
||||||
|
foldl
|
||||||
|
maybeTCombine
|
||||||
|
(return Nothing)
|
||||||
|
(map (liftIO . loadPixbufByName size) fallbackNames)
|
||||||
|
result <- tryNames
|
||||||
|
case result of
|
||||||
|
Just _ -> return result
|
||||||
|
Nothing -> Just <$> pixBufFromColor size 0x5f5f5fff
|
||||||
|
|
||||||
|
workspaceFallbackIcon :: Workspaces.WindowIconPixbufGetter
|
||||||
|
workspaceFallbackIcon size _ =
|
||||||
|
fallbackIconPixbuf size
|
||||||
|
|
||||||
|
workspaceWindowIconGetter :: Workspaces.WindowIconPixbufGetter
|
||||||
|
workspaceWindowIconGetter =
|
||||||
|
workspaceManualIconGetter
|
||||||
|
<|||> Workspaces.getWindowIconPixbufFromChrome
|
||||||
|
<|||> Workspaces.defaultGetWindowIconPixbuf
|
||||||
|
<|||> workspaceFallbackIcon
|
||||||
|
|
||||||
|
-- ** Host Overrides
|
||||||
|
|
||||||
|
-- NOTE: Keep `cssPaths` to a single entrypoint file per host. GTK's
|
||||||
|
-- `cssProviderLoadFromPath` clears the provider before loading, so handing
|
||||||
|
-- Taffybar multiple files here causes only the last file to take effect.
|
||||||
|
defaultCssFiles :: [FilePath]
|
||||||
|
defaultCssFiles = ["taffybar.css"]
|
||||||
|
|
||||||
|
cssFilesByHostname :: [(String, [FilePath])]
|
||||||
|
cssFilesByHostname =
|
||||||
|
[("ryzen-shine", ["ryzen-shine.css"])]
|
||||||
|
|
||||||
|
laptopHosts :: [String]
|
||||||
|
laptopHosts =
|
||||||
|
[ "adell",
|
||||||
|
"stevie-nixos",
|
||||||
|
"strixi-minaj",
|
||||||
|
"jay-lenovo"
|
||||||
|
]
|
||||||
|
|
||||||
|
cssFilesForHost :: String -> [FilePath]
|
||||||
|
cssFilesForHost hostName =
|
||||||
|
fromMaybe defaultCssFiles $ lookup hostName cssFilesByHostname
|
||||||
|
|
||||||
|
-- ** Widgets
|
||||||
|
|
||||||
|
audioWidget :: TaffyIO Gtk.Widget
|
||||||
|
audioWidget =
|
||||||
|
decorateWithClassAndBoxM "audio" PulseAudio.pulseAudioNew
|
||||||
|
|
||||||
|
networkInnerWidget :: TaffyIO Gtk.Widget
|
||||||
|
networkInnerWidget =
|
||||||
|
withNmAppletMenu NetworkManager.networkManagerWifiIconLabelNew
|
||||||
|
>>= flip widgetSetClassGI "network"
|
||||||
|
|
||||||
|
networkWidget :: TaffyIO Gtk.Widget
|
||||||
|
networkWidget =
|
||||||
|
decorateWithClassAndBoxM "network" networkInnerWidget
|
||||||
|
|
||||||
|
layoutWidget :: TaffyIO Gtk.Widget
|
||||||
|
layoutWidget =
|
||||||
|
decorateWithClassAndBoxM "layout" (layoutNew defaultLayoutConfig)
|
||||||
|
|
||||||
|
windowsWidget :: TaffyIO Gtk.Widget
|
||||||
|
windowsWidget =
|
||||||
|
decorateWithClassAndBoxM
|
||||||
|
"windows"
|
||||||
|
( windowsNew
|
||||||
|
defaultWindowsConfig
|
||||||
|
{ getActiveLabel = truncatedGetActiveLabel 28,
|
||||||
|
configureActiveLabel = liftIO . setFixedLabelWidth 28
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
workspacesWidget :: TaffyIO Gtk.Widget
|
||||||
|
workspacesWidget = Workspaces.workspacesNew cfg
|
||||||
|
where
|
||||||
|
cfg =
|
||||||
|
Workspaces.defaultWorkspacesConfig
|
||||||
|
{ Workspaces.widgetGap = 0,
|
||||||
|
Workspaces.minIcons = 1,
|
||||||
|
Workspaces.getWindowIconPixbuf = workspaceWindowIconGetter,
|
||||||
|
Workspaces.labelSetter = workspaceLabelSetter,
|
||||||
|
Workspaces.showWorkspaceFn =
|
||||||
|
\workspace ->
|
||||||
|
Workspaces.hideEmpty workspace
|
||||||
|
&& not (WorkspaceModel.workspaceIsSpecial workspace)
|
||||||
|
}
|
||||||
|
|
||||||
|
clockWidget :: TaffyIO Gtk.Widget
|
||||||
|
clockWidget = do
|
||||||
|
clock <-
|
||||||
|
textClockNewWith
|
||||||
|
defaultClockConfig
|
||||||
|
{ clockUpdateStrategy = RoundedTargetInterval 60 0.0,
|
||||||
|
clockFormatString = "%a %b %_d\n%I:%M %p"
|
||||||
|
}
|
||||||
|
liftIO $ setLabelAlignmentRecursively 0.5 Gtk.JustificationCenter clock
|
||||||
|
decorateWithClassAndBox "clock" clock
|
||||||
|
|
||||||
|
singleLineMprisLabel :: Text -> Text
|
||||||
|
singleLineMprisLabel =
|
||||||
|
T.replace "\n" " " . T.replace "\r" " "
|
||||||
|
|
||||||
|
stackedMprisLabel :: Text -> Text
|
||||||
|
stackedMprisLabel raw =
|
||||||
|
let normalized = singleLineMprisLabel raw
|
||||||
|
(top, rest) = T.breakOn " - " normalized
|
||||||
|
in if T.null rest
|
||||||
|
then normalized
|
||||||
|
else top <> "\n" <> T.drop 3 rest
|
||||||
|
|
||||||
|
mprisWidget :: TaffyIO Gtk.Widget
|
||||||
|
mprisWidget =
|
||||||
|
mpris2NewWithConfig
|
||||||
|
MPRIS2Config
|
||||||
|
{ mprisWidgetWrapper = decorateWithClassAndBox "mpris",
|
||||||
|
updatePlayerWidget =
|
||||||
|
simplePlayerWidget
|
||||||
|
defaultPlayerConfig
|
||||||
|
{ setNowPlayingLabel =
|
||||||
|
fmap stackedMprisLabel . playingText 20 20,
|
||||||
|
setupPlayerLabel = setFixedLabelWidth 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
batteryInnerWidget :: TaffyIO Gtk.Widget
|
||||||
|
batteryInnerWidget = do
|
||||||
|
iconWidget <- batteryTextIconNew
|
||||||
|
labelWidget <- textBatteryNew "$percentage$%"
|
||||||
|
liftIO (buildIconLabelBox iconWidget labelWidget) >>= flip widgetSetClassGI "battery"
|
||||||
|
|
||||||
|
batteryWidget :: TaffyIO Gtk.Widget
|
||||||
|
batteryWidget =
|
||||||
|
decorateWithClassAndBoxM "battery" batteryInnerWidget
|
||||||
|
|
||||||
|
backlightWidget :: TaffyIO Gtk.Widget
|
||||||
|
backlightWidget =
|
||||||
|
decorateWithClassAndBoxM
|
||||||
|
"backlight"
|
||||||
|
( backlightLabelNewChanWith
|
||||||
|
defaultBacklightWidgetConfig
|
||||||
|
{ backlightFormat = "☀ $percent$%",
|
||||||
|
backlightUnknownFormat = "☀ n/a",
|
||||||
|
backlightTooltipFormat =
|
||||||
|
Just "Device: $device$\nBrightness: $brightness$/$max$ ($percent$%)"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
diskUsageInnerWidget :: TaffyIO Gtk.Widget
|
||||||
|
diskUsageInnerWidget =
|
||||||
|
diskUsageNew >>= flip widgetSetClassGI "disk-usage"
|
||||||
|
|
||||||
|
diskUsageWidget :: TaffyIO Gtk.Widget
|
||||||
|
diskUsageWidget =
|
||||||
|
decorateWithClassAndBoxM "disk-usage" diskUsageInnerWidget
|
||||||
|
|
||||||
|
stackInPill :: Text -> [TaffyIO Gtk.Widget] -> TaffyIO Gtk.Widget
|
||||||
|
stackInPill klass builders =
|
||||||
|
decorateWithClassAndBoxM klass $ do
|
||||||
|
widgets <- sequence builders
|
||||||
|
liftIO $ do
|
||||||
|
box <- Gtk.boxNew Gtk.OrientationVertical 0
|
||||||
|
mapM_ (\w -> Gtk.boxPackStart box w False False 0) widgets
|
||||||
|
Gtk.widgetShowAll box
|
||||||
|
Gtk.toWidget box
|
||||||
|
|
||||||
|
meminfoPercentRowWidget ::
|
||||||
|
Text ->
|
||||||
|
Text ->
|
||||||
|
(MemoryInfo -> Maybe Double) ->
|
||||||
|
(MemoryInfo -> T.Text) ->
|
||||||
|
TaffyIO Gtk.Widget
|
||||||
|
meminfoPercentRowWidget rowClass iconText getRatio tooltipText =
|
||||||
|
liftIO $ do
|
||||||
|
iconW <- Gtk.toWidget =<< Gtk.labelNew (Just iconText)
|
||||||
|
valueLabel <- Gtk.labelNew (Just "")
|
||||||
|
valueW <- Gtk.toWidget valueLabel
|
||||||
|
row <- buildIconLabelBox iconW valueW
|
||||||
|
_ <- widgetSetClassGI row rowClass
|
||||||
|
|
||||||
|
let fmtPercent :: Double -> T.Text
|
||||||
|
fmtPercent r = T.pack (printf "%.0f%%" (max 0 r * 100))
|
||||||
|
updateOnce :: IO ()
|
||||||
|
updateOnce = do
|
||||||
|
info <- parseMeminfo
|
||||||
|
let valueText = maybe "n/a" fmtPercent (getRatio info)
|
||||||
|
postGUIASync $ do
|
||||||
|
Gtk.labelSetText valueLabel valueText
|
||||||
|
Gtk.widgetSetTooltipText row (Just (tooltipText info))
|
||||||
|
threadDelay (2 * 1000000)
|
||||||
|
|
||||||
|
_ <- Gtk.onWidgetRealize row $ backgroundLoop updateOnce
|
||||||
|
pure row
|
||||||
|
|
||||||
|
ramRowWidget :: TaffyIO Gtk.Widget
|
||||||
|
ramRowWidget =
|
||||||
|
meminfoPercentRowWidget
|
||||||
|
"ram-row"
|
||||||
|
"\xF538" -- Font Awesome: memory
|
||||||
|
(Just . memoryUsedRatio)
|
||||||
|
(\info -> "RAM " <> showMemoryInfo "$used$/$total$" 2 info)
|
||||||
|
|
||||||
|
swapRowWidget :: TaffyIO Gtk.Widget
|
||||||
|
swapRowWidget =
|
||||||
|
meminfoPercentRowWidget
|
||||||
|
"swap-row"
|
||||||
|
"\xF0EC" -- Font Awesome: exchange (swap-ish)
|
||||||
|
(\info -> if memorySwapTotal info <= 0 then Nothing else Just (memorySwapUsedRatio info))
|
||||||
|
(\info -> "SWAP " <> showMemoryInfo "$swapUsed$/$swapTotal$" 2 info)
|
||||||
|
|
||||||
|
ramSwapWidget :: TaffyIO Gtk.Widget
|
||||||
|
ramSwapWidget =
|
||||||
|
stackInPill "ram-swap" [ramRowWidget, swapRowWidget]
|
||||||
|
|
||||||
|
audioBacklightWidget :: TaffyIO Gtk.Widget
|
||||||
|
audioBacklightWidget =
|
||||||
|
stackInPill
|
||||||
|
"audio-backlight"
|
||||||
|
[ PulseAudio.pulseAudioNew,
|
||||||
|
backlightNewChanWith
|
||||||
|
defaultBacklightWidgetConfig
|
||||||
|
{ backlightFormat = "$percent$%",
|
||||||
|
backlightUnknownFormat = "n/a",
|
||||||
|
backlightTooltipFormat =
|
||||||
|
Just "Device: $device$\nBrightness: $brightness$/$max$ ($percent$%)"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
asusInnerWidget :: TaffyIO Gtk.Widget
|
||||||
|
asusInnerWidget = ASUS.asusWidgetNew
|
||||||
|
|
||||||
|
asusWidget :: TaffyIO Gtk.Widget
|
||||||
|
asusWidget =
|
||||||
|
decorateWithClassAndBoxM "asus-profile" asusInnerWidget
|
||||||
|
|
||||||
|
batteryNetworkWidget :: TaffyIO Gtk.Widget
|
||||||
|
batteryNetworkWidget =
|
||||||
|
stackInPill "battery-network" [batteryInnerWidget, networkInnerWidget]
|
||||||
|
|
||||||
|
asusDiskUsageWidget :: TaffyIO Gtk.Widget
|
||||||
|
asusDiskUsageWidget =
|
||||||
|
stackInPill "asus-disk-usage" [diskUsageInnerWidget, asusInnerWidget]
|
||||||
|
|
||||||
|
screenLockWidget :: TaffyIO Gtk.Widget
|
||||||
|
screenLockWidget =
|
||||||
|
decorateWithClassAndBoxM "screen-lock" $
|
||||||
|
ScreenLock.screenLockNewWithConfig
|
||||||
|
ScreenLock.defaultScreenLockConfig
|
||||||
|
{ ScreenLock.screenLockIcon = T.pack "\xF023" <> " Lock"
|
||||||
|
}
|
||||||
|
|
||||||
|
wlsunsetWidget :: TaffyIO Gtk.Widget
|
||||||
|
wlsunsetWidget =
|
||||||
|
decorateWithClassAndBoxM "wlsunset" $
|
||||||
|
Wlsunset.wlsunsetNewWithConfig
|
||||||
|
Wlsunset.defaultWlsunsetWidgetConfig
|
||||||
|
{ Wlsunset.wlsunsetWidgetIcon = T.pack "\xF0599" <> " Sun"
|
||||||
|
}
|
||||||
|
|
||||||
|
simplifiedScreenLockWidget :: TaffyIO Gtk.Widget
|
||||||
|
simplifiedScreenLockWidget =
|
||||||
|
-- Inner widget: no extra pill wrapping (the combiner provides that).
|
||||||
|
ScreenLock.screenLockNewWithConfig
|
||||||
|
ScreenLock.defaultScreenLockConfig
|
||||||
|
{ ScreenLock.screenLockIcon = T.pack "\xF023" <> " Lock"
|
||||||
|
}
|
||||||
|
|
||||||
|
simplifiedScreensaverWidget :: TaffyIO Gtk.Widget
|
||||||
|
simplifiedScreensaverWidget =
|
||||||
|
liftIO $ do
|
||||||
|
label <- Gtk.labelNew (Just (T.pack "\xF108" <> " Saver"))
|
||||||
|
ebox <- Gtk.eventBoxNew
|
||||||
|
Gtk.containerAdd ebox label
|
||||||
|
_ <- widgetSetClassGI ebox "screensaver"
|
||||||
|
Gtk.widgetSetTooltipText ebox (Just "Left click: toggle screensaver\nRight click: stop screensaver")
|
||||||
|
void $ Gtk.onWidgetButtonPressEvent ebox $ \event -> do
|
||||||
|
eventType <- Gdk.getEventButtonType event
|
||||||
|
button <- Gdk.getEventButtonButton event
|
||||||
|
if eventType /= Gdk.EventTypeButtonPress
|
||||||
|
then return False
|
||||||
|
else case button of
|
||||||
|
1 -> do
|
||||||
|
void $ spawnCommand "hypr-screensaver toggle >/dev/null 2>&1"
|
||||||
|
return True
|
||||||
|
3 -> do
|
||||||
|
void $ spawnCommand "hypr-screensaver stop >/dev/null 2>&1"
|
||||||
|
return True
|
||||||
|
_ -> return False
|
||||||
|
Gtk.widgetShowAll ebox
|
||||||
|
Gtk.toWidget ebox
|
||||||
|
|
||||||
|
screensaverWidget :: TaffyIO Gtk.Widget
|
||||||
|
screensaverWidget =
|
||||||
|
decorateWithClassAndBoxM "screensaver" simplifiedScreensaverWidget
|
||||||
|
|
||||||
|
simplifiedWlsunsetWidget :: TaffyIO Gtk.Widget
|
||||||
|
simplifiedWlsunsetWidget =
|
||||||
|
-- Inner widget: no extra pill wrapping (the combiner provides that).
|
||||||
|
Wlsunset.wlsunsetNewWithConfig
|
||||||
|
Wlsunset.defaultWlsunsetWidgetConfig
|
||||||
|
{ Wlsunset.wlsunsetWidgetIcon = T.pack "\xF0599" <> " Sun"
|
||||||
|
}
|
||||||
|
|
||||||
|
sunLockWidget :: TaffyIO Gtk.Widget
|
||||||
|
sunLockWidget =
|
||||||
|
stackInPill "sun-lock" [simplifiedWlsunsetWidget, simplifiedScreenLockWidget]
|
||||||
|
|
||||||
|
cpuWidget :: TaffyIO Gtk.Widget
|
||||||
|
cpuWidget =
|
||||||
|
decorateWithClassAndBoxM "cpu" $
|
||||||
|
cpuMonitorNew
|
||||||
|
defaultGraphConfig
|
||||||
|
{ graphDataColors = [(0, 1, 0.5, 0.8), (1, 0, 0, 0.5)],
|
||||||
|
graphBackgroundColor = (0, 0, 0, 0),
|
||||||
|
graphBorderWidth = 0,
|
||||||
|
graphLabel = Just "CPU",
|
||||||
|
graphWidth = 50,
|
||||||
|
graphDirection = LEFT_TO_RIGHT
|
||||||
|
}
|
||||||
|
1.0
|
||||||
|
"cpu"
|
||||||
|
|
||||||
|
wakeupDebugWidget :: TaffyIO Gtk.Widget
|
||||||
|
wakeupDebugWidget =
|
||||||
|
decorateWithClassAndBoxM "wakeup-debug" wakeupDebugWidgetNew
|
||||||
|
|
||||||
|
openAIUsageWidget :: TaffyIO Gtk.Widget
|
||||||
|
openAIUsageWidget =
|
||||||
|
decorateWithClassAndBoxM "openai-usage" openAIUsageStackNew
|
||||||
|
|
||||||
|
anthropicUsageWidget :: TaffyIO Gtk.Widget
|
||||||
|
anthropicUsageWidget =
|
||||||
|
decorateWithClassAndBoxM "anthropic-usage" anthropicUsageStackNew
|
||||||
|
|
||||||
|
sniPriorityVisibilityThresholdDefault :: Int
|
||||||
|
sniPriorityVisibilityThresholdDefault = 0
|
||||||
|
|
||||||
|
sniTrayWidget :: TaffyIO Gtk.Widget
|
||||||
|
sniTrayWidget = do
|
||||||
|
-- If the Haskell backend regresses, flip at runtime:
|
||||||
|
-- TAFFYBAR_SNI_MENU_BACKEND=lib
|
||||||
|
backendEnv <- liftIO (lookupEnv "TAFFYBAR_SNI_MENU_BACKEND")
|
||||||
|
thresholdEnv <- liftIO (lookupEnv "TAFFYBAR_SNI_PRIORITY_THRESHOLD")
|
||||||
|
let menuBackend =
|
||||||
|
case fmap (map toLower) backendEnv of
|
||||||
|
Just "lib" -> SNITray.LibDBusMenu
|
||||||
|
_ -> SNITray.HaskellDBusMenu
|
||||||
|
visibilityThreshold =
|
||||||
|
fromMaybe
|
||||||
|
sniPriorityVisibilityThresholdDefault
|
||||||
|
(thresholdEnv >>= readMaybe)
|
||||||
|
trayParams =
|
||||||
|
SNITray.defaultTrayParams
|
||||||
|
{ SNITray.trayMenuBackend = menuBackend,
|
||||||
|
SNITray.trayOverlayScale = 1 % 3,
|
||||||
|
SNITray.trayEventHooks = SNITray.defaultTrayEventHooks
|
||||||
|
}
|
||||||
|
sniTrayConfig =
|
||||||
|
defaultSNITrayConfig
|
||||||
|
{ sniTrayTrayParams = trayParams
|
||||||
|
}
|
||||||
|
collapsibleParams =
|
||||||
|
defaultCollapsibleSNITrayParams
|
||||||
|
{ collapsibleSNITrayConfig = sniTrayConfig
|
||||||
|
}
|
||||||
|
prioritizedParams =
|
||||||
|
defaultPrioritizedCollapsibleSNITrayParams
|
||||||
|
{ prioritizedCollapsibleSNITrayParams = collapsibleParams,
|
||||||
|
prioritizedCollapsibleSNITrayVisibilityThreshold = Just visibilityThreshold
|
||||||
|
}
|
||||||
|
decorateWithClassAndBoxM
|
||||||
|
"sni-tray"
|
||||||
|
(sniTrayPrioritizedCollapsibleNewFromParams prioritizedParams)
|
||||||
|
|
||||||
|
-- ** Layout
|
||||||
|
|
||||||
|
startWidgetsForBackend :: Backend -> [TaffyIO Gtk.Widget]
|
||||||
|
startWidgetsForBackend backend =
|
||||||
|
case backend of
|
||||||
|
BackendX11 -> [workspacesWidget, layoutWidget, windowsWidget]
|
||||||
|
-- These Wayland widgets are Hyprland-specific.
|
||||||
|
BackendWayland -> [workspacesWidget, windowsWidget]
|
||||||
|
|
||||||
|
endWidgetsForHost :: String -> [TaffyIO Gtk.Widget]
|
||||||
|
endWidgetsForHost hostName =
|
||||||
|
-- NOTE: end widgets are packed with Gtk.boxPackEnd, so the list order is
|
||||||
|
-- right-to-left on screen. Make the tray appear at the far right by placing
|
||||||
|
-- it first in the list. (On laptops: the battery/wifi stack is far right,
|
||||||
|
-- tray immediately left of it.)
|
||||||
|
let baseEndWidgets =
|
||||||
|
[ sniTrayWidget,
|
||||||
|
audioWidget,
|
||||||
|
anthropicUsageWidget,
|
||||||
|
openAIUsageWidget,
|
||||||
|
cpuWidget,
|
||||||
|
ramSwapWidget,
|
||||||
|
diskUsageWidget,
|
||||||
|
networkWidget,
|
||||||
|
screensaverWidget,
|
||||||
|
sunLockWidget,
|
||||||
|
mprisWidget
|
||||||
|
]
|
||||||
|
laptopEndWidgets =
|
||||||
|
[ batteryNetworkWidget,
|
||||||
|
sniTrayWidget,
|
||||||
|
asusDiskUsageWidget,
|
||||||
|
audioBacklightWidget,
|
||||||
|
anthropicUsageWidget,
|
||||||
|
openAIUsageWidget,
|
||||||
|
cpuWidget,
|
||||||
|
ramSwapWidget,
|
||||||
|
screensaverWidget,
|
||||||
|
sunLockWidget,
|
||||||
|
mprisWidget
|
||||||
|
]
|
||||||
|
in if hostName `elem` laptopHosts
|
||||||
|
then laptopEndWidgets
|
||||||
|
else baseEndWidgets
|
||||||
|
|
||||||
|
mkSimpleTaffyConfig :: String -> Backend -> [FilePath] -> SimpleTaffyConfig
|
||||||
|
mkSimpleTaffyConfig hostName backend cssFiles =
|
||||||
|
defaultSimpleTaffyConfig
|
||||||
|
{ startWidgets = startWidgetsForBackend backend,
|
||||||
|
centerWidgets = [clockWidget],
|
||||||
|
endWidgets = endWidgetsForHost hostName,
|
||||||
|
barLevels = Nothing,
|
||||||
|
barPosition = Top,
|
||||||
|
widgetSpacing = 0,
|
||||||
|
barPadding = if hostName == "ryzen-shine" then 2 else 4,
|
||||||
|
barHeight =
|
||||||
|
if hostName == "ryzen-shine"
|
||||||
|
then ScreenRatio $ 1 / 40
|
||||||
|
else ScreenRatio $ 1 / 33,
|
||||||
|
cssPaths = cssFiles
|
||||||
|
}
|
||||||
|
|
||||||
|
-- ** Entry Point
|
||||||
|
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = do
|
main = do
|
||||||
@@ -26,5 +676,4 @@ main = do
|
|||||||
withLogServer $
|
withLogServer $
|
||||||
withLogLevels $
|
withLogLevels $
|
||||||
withToggleServer $
|
withToggleServer $
|
||||||
appendHook registerChromeWindowInfoRefreshRequests $
|
|
||||||
toTaffybarConfig simpleTaffyConfig
|
toTaffybarConfig simpleTaffyConfig
|
||||||
|
|||||||
@@ -108,7 +108,7 @@ myConfig = def
|
|||||||
, borderWidth = 0
|
, borderWidth = 0
|
||||||
, logHook
|
, logHook
|
||||||
= updatePointer (0.5, 0.5) (0, 0)
|
= updatePointer (0.5, 0.5) (0, 0)
|
||||||
<> toggleFadeInactiveLogHook 0.65
|
<> toggleFadeInactiveLogHook 0.9
|
||||||
<> workspaceHistoryHook
|
<> workspaceHistoryHook
|
||||||
<> setWorkspaceNames
|
<> setWorkspaceNames
|
||||||
<> logHook def
|
<> logHook def
|
||||||
@@ -232,29 +232,40 @@ getWorkspaceDmenu = myDmenu (workspaces myConfig)
|
|||||||
|
|
||||||
-- Selectors
|
-- Selectors
|
||||||
|
|
||||||
|
isGmailTitle t = isInfixOf "@gmail.com" t && isInfixOf "Gmail" t
|
||||||
|
isMessagesTitle = isPrefixOf "Messages"
|
||||||
isChromeClass = isInfixOf "chrome"
|
isChromeClass = isInfixOf "chrome"
|
||||||
|
noSpecialChromeTitles = helper <$> title
|
||||||
|
where helper t = not $ any ($ t) [isGmailTitle, isMessagesTitle]
|
||||||
chromeSelectorBase = isChromeClass <$> className
|
chromeSelectorBase = isChromeClass <$> className
|
||||||
|
|
||||||
chromeSelector = chromeSelectorBase
|
chromeSelector = chromeSelectorBase <&&> noSpecialChromeTitles
|
||||||
codexSelector = className =? "codex-desktop"
|
|
||||||
elementSelector = className =? "Element"
|
elementSelector = className =? "Element"
|
||||||
emacsSelector = className =? "Emacs"
|
emacsSelector = className =? "Emacs"
|
||||||
|
gmailSelector = chromeSelectorBase <&&> fmap isGmailTitle title
|
||||||
|
messagesSelector = chromeSelectorBase <&&> isMessagesTitle <$> title
|
||||||
slackSelector = className =? "Slack"
|
slackSelector = className =? "Slack"
|
||||||
spotifySelector = className =? "Spotify"
|
spotifySelector = className =? "Spotify"
|
||||||
transmissionSelector = fmap (isPrefixOf "Transmission") title
|
transmissionSelector = fmap (isPrefixOf "Transmission") title
|
||||||
volumeSelector = className =? "Pavucontrol"
|
volumeSelector = className =? "Pavucontrol"
|
||||||
|
|
||||||
virtualClasses =
|
virtualClasses =
|
||||||
[ (chromeSelector, "Chrome")
|
[ (gmailSelector, "Gmail")
|
||||||
|
, (messagesSelector, "Messages")
|
||||||
|
, (chromeSelector, "Chrome")
|
||||||
, (transmissionSelector, "Transmission")
|
, (transmissionSelector, "Transmission")
|
||||||
]
|
]
|
||||||
|
|
||||||
-- Commands
|
-- Commands
|
||||||
|
|
||||||
codexCommand = "codex_desktop_scratchpad"
|
chromeCommand = "google-chrome-stable"
|
||||||
elementCommand = "element-desktop"
|
elementCommand = "element-desktop"
|
||||||
emacsCommand = "emacsclient -c"
|
emacsCommand = "emacsclient -c"
|
||||||
|
gmailCommand =
|
||||||
|
"google-chrome-stable --new-window https://mail.google.com/mail/u/0/#inbox"
|
||||||
htopCommand = "ghostty --title=htop -e htop"
|
htopCommand = "ghostty --title=htop -e htop"
|
||||||
|
messagesCommand =
|
||||||
|
"google-chrome-stable --new-window https://messages.google.com/web/conversations"
|
||||||
slackCommand = "slack"
|
slackCommand = "slack"
|
||||||
spotifyCommand = "spotify"
|
spotifyCommand = "spotify"
|
||||||
transmissionCommand = "transmission-gtk"
|
transmissionCommand = "transmission-gtk"
|
||||||
@@ -801,9 +812,10 @@ nearFullFloat = customFloating $ W.RationalRect l t w h
|
|||||||
|
|
||||||
|
|
||||||
scratchpads =
|
scratchpads =
|
||||||
[ NS "codex" codexCommand codexSelector nearFullFloat
|
[ NS "element" elementCommand elementSelector nearFullFloat
|
||||||
, NS "element" elementCommand elementSelector nearFullFloat
|
, NS "gmail" gmailCommand gmailSelector nearFullFloat
|
||||||
, NS "htop" htopCommand (title =? "htop") nearFullFloat
|
, NS "htop" htopCommand (title =? "htop") nearFullFloat
|
||||||
|
, NS "messages" messagesCommand messagesSelector nearFullFloat
|
||||||
, NS "slack" slackCommand slackSelector nearFullFloat
|
, NS "slack" slackCommand slackSelector nearFullFloat
|
||||||
, NS "spotify" spotifyCommand spotifySelector nearFullFloat
|
, NS "spotify" spotifyCommand spotifySelector nearFullFloat
|
||||||
, NS "transmission" transmissionCommand transmissionSelector nearFullFloat
|
, NS "transmission" transmissionCommand transmissionSelector nearFullFloat
|
||||||
@@ -1006,10 +1018,16 @@ addKeys conf@XConfig { modMask = modm } =
|
|||||||
buildDirectionalBindings
|
buildDirectionalBindings
|
||||||
(hyper .|. shiftMask) (followingWindow . (`screenSwap` True)) ++
|
(hyper .|. shiftMask) (followingWindow . (`screenSwap` True)) ++
|
||||||
|
|
||||||
|
-- Specific program spawning
|
||||||
|
bindBringAndRaiseMany
|
||||||
|
[ (modalt, xK_c, spawn chromeCommand, chromeSelector)
|
||||||
|
] ++
|
||||||
|
|
||||||
-- ScratchPads
|
-- ScratchPads
|
||||||
[ ((modalt, xK_c), doScratchpad "codex")
|
[ ((modalt, xK_e), doScratchpad "element")
|
||||||
, ((modalt, xK_e), doScratchpad "element")
|
, ((modalt, xK_g), doScratchpad "gmail")
|
||||||
, ((modalt, xK_h), doScratchpad "htop")
|
, ((modalt, xK_h), doScratchpad "htop")
|
||||||
|
, ((modalt, xK_m), doScratchpad "messages")
|
||||||
, ((modalt, xK_k), doScratchpad "slack")
|
, ((modalt, xK_k), doScratchpad "slack")
|
||||||
, ((modalt, xK_s), doScratchpad "spotify")
|
, ((modalt, xK_s), doScratchpad "spotify")
|
||||||
, ((modalt, xK_t), doScratchpad "transmission")
|
, ((modalt, xK_t), doScratchpad "transmission")
|
||||||
@@ -1029,7 +1047,7 @@ addKeys conf@XConfig { modMask = modm } =
|
|||||||
, ((modm, xK_m), withFocused minimizeWindow)
|
, ((modm, xK_m), withFocused minimizeWindow)
|
||||||
, ((modm .|. shiftMask, xK_m),
|
, ((modm .|. shiftMask, xK_m),
|
||||||
deactivateFullOr $ withLastMinimized maximizeWindowAndFocus)
|
deactivateFullOr $ withLastMinimized maximizeWindowAndFocus)
|
||||||
, ((modm, xK_x), spawn "rofi_command.sh")
|
, ((modm, xK_x), addHiddenWorkspace "NSP" >> windows (W.shift "NSP"))
|
||||||
, ((modalt, xK_space), deactivateFullOr restoreOrMinimizeOtherClasses)
|
, ((modalt, xK_space), deactivateFullOr restoreOrMinimizeOtherClasses)
|
||||||
, ((modalt, xK_Return), deactivateFullAnd restoreAllMinimized)
|
, ((modalt, xK_Return), deactivateFullAnd restoreAllMinimized)
|
||||||
, ((hyper, xK_g), gatherThisClass)
|
, ((hyper, xK_g), gatherThisClass)
|
||||||
@@ -1069,22 +1087,20 @@ addKeys conf@XConfig { modMask = modm } =
|
|||||||
, ((modm, xK_v), spawn "xclip -o | xdotool type --file -")
|
, ((modm, xK_v), spawn "xclip -o | xdotool type --file -")
|
||||||
, ((hyper, xK_v), spawn "rofi -modi 'clipboard:greenclip print' -show clipboard")
|
, ((hyper, xK_v), spawn "rofi -modi 'clipboard:greenclip print' -show clipboard")
|
||||||
, ((hyper, xK_p), spawn "rofi-pass")
|
, ((hyper, xK_p), spawn "rofi-pass")
|
||||||
, ((0, xK_Print), spawn "flameshot gui")
|
, ((hyper, xK_h), spawn "rofi_shutter")
|
||||||
, ((hyper, xK_h), spawn "flameshot gui")
|
, ((hyper, xK_c), spawn "shell_command.sh")
|
||||||
, ((hyper, xK_c), spawn "rofi_tmcodex.sh")
|
, ((hyper, xK_x), spawn "rofi_command.sh")
|
||||||
, ((hyper .|. shiftMask, xK_c), spawn "rofi_tmcodex.sh resume")
|
|
||||||
, ((hyper .|. shiftMask, xK_l), spawn "dm-tool lock")
|
, ((hyper .|. shiftMask, xK_l), spawn "dm-tool lock")
|
||||||
, ((hyper, xK_l), selectLayout)
|
, ((hyper, xK_l), selectLayout)
|
||||||
, ((hyper, xK_k), spawn "rofi_kill_process.sh")
|
, ((hyper, xK_k), spawn "rofi_kill_process.sh")
|
||||||
, ((hyper .|. shiftMask, xK_k), spawn "rofi_kill_all.sh")
|
, ((hyper .|. shiftMask, xK_k), spawn "rofi_kill_all.sh")
|
||||||
, ((hyper, xK_r), spawn "rofi_systemd_mono")
|
, ((hyper, xK_r), spawn "rofi-systemd")
|
||||||
, ((hyper, xK_9), spawn "start_synergy.sh")
|
, ((hyper, xK_9), spawn "start_synergy.sh")
|
||||||
, ((hyper, xK_slash), spawn "toggle_taffybar")
|
, ((hyper, xK_slash), spawn "toggle_taffybar")
|
||||||
, ((hyper, xK_backslash), spawn "$HOME/dotfiles/dotfiles/lib/functions/mpg341cx_input toggle")
|
, ((hyper, xK_backslash), spawn "$HOME/dotfiles/dotfiles/lib/functions/mpg341cx_input toggle")
|
||||||
, ((hyper, xK_space), spawn "skippy-xd")
|
, ((hyper, xK_space), spawn "skippy-xd")
|
||||||
, ((hyper, xK_i), spawn "rofi_select_input.hs")
|
, ((hyper, xK_i), spawn "rofi_select_input.hs")
|
||||||
, ((hyper, xK_o), spawn "rofi_paswitch")
|
, ((hyper, xK_o), spawn "rofi_paswitch")
|
||||||
, ((hyper .|. shiftMask, xK_o), spawn "$HOME/dotfiles/dotfiles/lib/bin/kef-optical")
|
|
||||||
, ((hyper, xK_comma), spawn "rofi_wallpaper.sh")
|
, ((hyper, xK_comma), spawn "rofi_wallpaper.sh")
|
||||||
, ((hyper, xK_y), spawn "rofi_agentic_skill")
|
, ((hyper, xK_y), spawn "rofi_agentic_skill")
|
||||||
, ((modm, xK_e), spawn "emacsclient --eval '(emacs-everywhere)'")
|
, ((modm, xK_e), spawn "emacsclient --eval '(emacs-everywhere)'")
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
keybinds {
|
|
||||||
// Keep Ctrl-p available for readline/history/up in shells and editors.
|
|
||||||
unbind "Ctrl p"
|
|
||||||
|
|
||||||
shared_except "locked" "pane" {
|
|
||||||
bind "Ctrl Space" { SwitchToMode "Pane"; }
|
|
||||||
}
|
|
||||||
|
|
||||||
pane {
|
|
||||||
bind "Ctrl Space" { SwitchToMode "Normal"; }
|
|
||||||
}
|
|
||||||
|
|
||||||
tmux {
|
|
||||||
// Ctrl-b C: start a Codex pane from the current zellij tab.
|
|
||||||
bind "C" {
|
|
||||||
Run "codex" "--dangerously-bypass-approvals-and-sandbox" {
|
|
||||||
name "codex"
|
|
||||||
}
|
|
||||||
SwitchToMode "Normal"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default_mode "locked"
|
|
||||||
@@ -58,8 +58,8 @@ think its pretty awesome!
|
|||||||
([[https://github.com/IvanMalison/emit#compose][README]])
|
([[https://github.com/IvanMalison/emit#compose][README]])
|
||||||
+ [[Add a blacklist to a major mode]]
|
+ [[Add a blacklist to a major mode]]
|
||||||
** Configuration of My Own Packages
|
** Configuration of My Own Packages
|
||||||
- [[term-project][term-project]] and [[term-manager][term-manager]]
|
- [[term-projectile][term-projectile]] and [[term-manager][term-manager]]
|
||||||
- [[org-project-capture][org-project-capture]]
|
- [[org-projectile][org-projectile]]
|
||||||
- [[multi-line][multi-line]]
|
- [[multi-line][multi-line]]
|
||||||
- [[github-search][github-search]]
|
- [[github-search][github-search]]
|
||||||
- [[flimenu][flimenu]]
|
- [[flimenu][flimenu]]
|
||||||
@@ -737,15 +737,10 @@ aren't visiting a file but are associated with a directory."
|
|||||||
(imalison:copy-buffer-file-path-builder imalison:copy-buffer-file-path-full)
|
(imalison:copy-buffer-file-path-builder imalison:copy-buffer-file-path-full)
|
||||||
(imalison:copy-buffer-file-path-builder imalison:copy-buffer-file-name
|
(imalison:copy-buffer-file-path-builder imalison:copy-buffer-file-name
|
||||||
file-name-nondirectory)
|
file-name-nondirectory)
|
||||||
(defun imalison:buffer-file-project-relative-name ()
|
(imalison:copy-buffer-file-path-builder imalison:copy-buffer-file-path
|
||||||
"Return the current buffer file or directory relative to its project root."
|
car
|
||||||
(let ((filename (imalison:buffer-file-name-or-directory)))
|
projectile-make-relative-to-root
|
||||||
(file-relative-name filename
|
list)
|
||||||
(imalison:project-root
|
|
||||||
(file-name-directory filename)))))
|
|
||||||
|
|
||||||
(imalison:compose-copy-builder imalison:copy-buffer-file-path
|
|
||||||
imalison:buffer-file-project-relative-name)
|
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
*** Copy the current branch using magit
|
*** Copy the current branch using magit
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
@@ -991,12 +986,7 @@ I keep it around just in case I need it.
|
|||||||
(shell-command (format "grownotify -t %s -m %s" title message)))
|
(shell-command (format "grownotify -t %s -m %s" title message)))
|
||||||
|
|
||||||
(defun notify-send (title message)
|
(defun notify-send (title message)
|
||||||
(when-let ((program (executable-find "notify-send")))
|
(shell-command (format "notify-send -u critical %s %s" title message)))
|
||||||
(let ((process-connection-type nil))
|
|
||||||
(start-process "notify-send" nil program
|
|
||||||
"-u" "critical"
|
|
||||||
(or title "No title")
|
|
||||||
(or message "No message")))))
|
|
||||||
|
|
||||||
(defvar notify-function
|
(defvar notify-function
|
||||||
(cond ((eq system-type 'darwin) 'notification-center)
|
(cond ((eq system-type 'darwin) 'notification-center)
|
||||||
@@ -1325,7 +1315,6 @@ Paradox is a package.el extension. I have no use for it now that I use straight.
|
|||||||
** gcmh
|
** gcmh
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
(use-package gcmh
|
(use-package gcmh
|
||||||
:defer 5
|
|
||||||
:config (gcmh-mode 1))
|
:config (gcmh-mode 1))
|
||||||
#+end_src
|
#+end_src
|
||||||
** diminish
|
** diminish
|
||||||
@@ -1344,33 +1333,7 @@ Paradox is a package.el extension. I have no use for it now that I use straight.
|
|||||||
** emacs-everywhere
|
** emacs-everywhere
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
(use-package emacs-everywhere
|
(use-package emacs-everywhere
|
||||||
:commands emacs-everywhere
|
:commands emacs-everywhere)
|
||||||
:config
|
|
||||||
(progn
|
|
||||||
(defun imalison:emacs-everywhere-app-info-hyprland ()
|
|
||||||
(require 'json)
|
|
||||||
(let* ((window (json-parse-string
|
|
||||||
(shell-command-to-string "hyprctl -j activewindow")
|
|
||||||
:object-type 'alist
|
|
||||||
:array-type 'list))
|
|
||||||
(address (alist-get 'address window))
|
|
||||||
(class (or (alist-get 'initialClass window)
|
|
||||||
(alist-get 'class window)
|
|
||||||
""))
|
|
||||||
(title (or (alist-get 'title window) "")))
|
|
||||||
(unless address
|
|
||||||
(user-error "Unable to determine active Hyprland window"))
|
|
||||||
(make-emacs-everywhere-app
|
|
||||||
:id address
|
|
||||||
:class class
|
|
||||||
:title title)))
|
|
||||||
|
|
||||||
(add-to-list 'emacs-everywhere-system-configs
|
|
||||||
'((wayland . Hyprland)
|
|
||||||
:focus-command ("hyprctl" "dispatch" "focuswindow" "address:%w")
|
|
||||||
:info-function imalison:emacs-everywhere-app-info-hyprland)
|
|
||||||
t)
|
|
||||||
(setq emacs-everywhere--system-configured nil)))
|
|
||||||
#+end_src
|
#+end_src
|
||||||
** atomic-chrome
|
** atomic-chrome
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
@@ -1420,10 +1383,8 @@ The file server file for this emacs instance no longer exists.")
|
|||||||
(defun imalison:get-this-server-filepath ()
|
(defun imalison:get-this-server-filepath ()
|
||||||
(let ((server-dir (if server-use-tcp server-auth-dir server-socket-dir)))
|
(let ((server-dir (if server-use-tcp server-auth-dir server-socket-dir)))
|
||||||
(expand-file-name server-name server-dir)))
|
(expand-file-name server-name server-dir)))
|
||||||
(unless (daemonp)
|
(when (equal nil (server-running-p)) (server-start)
|
||||||
(when (equal nil (server-running-p))
|
(imalison:make-main-emacs-server))))
|
||||||
(server-start)
|
|
||||||
(imalison:make-main-emacs-server)))))
|
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
** list-environment
|
** list-environment
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
@@ -1431,8 +1392,7 @@ The file server file for this emacs instance no longer exists.")
|
|||||||
#+END_SRC
|
#+END_SRC
|
||||||
** bug-hunter
|
** bug-hunter
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
(use-package bug-hunter
|
(use-package bug-hunter)
|
||||||
:defer t)
|
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
** shackle
|
** shackle
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
@@ -1670,7 +1630,7 @@ out how to detect that a buffer is a man mode buffer.
|
|||||||
(defhydra imalison:hydra-yank
|
(defhydra imalison:hydra-yank
|
||||||
nil
|
nil
|
||||||
"Yank text"
|
"Yank text"
|
||||||
("p" imalison:copy-buffer-file-path "Project path")
|
("p" imalison:copy-buffer-file-path "Projectile path")
|
||||||
("b" imalison:copy-current-buffer-name "Buffer Name")
|
("b" imalison:copy-current-buffer-name "Buffer Name")
|
||||||
("f" imalison:copy-buffer-file-path-full "Full path")
|
("f" imalison:copy-buffer-file-path-full "Full path")
|
||||||
("n" imalison:copy-buffer-file-name "File name")
|
("n" imalison:copy-buffer-file-name "File name")
|
||||||
@@ -1681,7 +1641,7 @@ out how to detect that a buffer is a man mode buffer.
|
|||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
(defun imalison:make-test ()
|
(defun imalison:make-test ()
|
||||||
(interactive)
|
(interactive)
|
||||||
(let ((default-directory (imalison:project-root)))
|
(let ((default-directory (projectile-project-root)))
|
||||||
(imalison:named-compile "make test")))
|
(imalison:named-compile "make test")))
|
||||||
|
|
||||||
(defun imalison:glide-up ()
|
(defun imalison:glide-up ()
|
||||||
@@ -1778,41 +1738,71 @@ bind-key and global-set-key forms.
|
|||||||
(setq zop-to-char-kill-keys '(?\C-k ?\C-w))
|
(setq zop-to-char-kill-keys '(?\C-k ?\C-w))
|
||||||
(setq zop-to-char-quit-at-point-keys '(?\r))))
|
(setq zop-to-char-quit-at-point-keys '(?\r))))
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
** project
|
** projectile
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
|
|
||||||
(use-package project
|
(use-package projectile
|
||||||
:ensure nil
|
|
||||||
:demand t
|
:demand t
|
||||||
:bind-keymap
|
:bind (:map projectile-mode-map
|
||||||
("C-c p" . project-prefix-map)
|
("C-c p f" . imalison:projectile-find-file)
|
||||||
|
("C-c p" . projectile-command-map)
|
||||||
|
("C-c p s" . imalison:do-rg)
|
||||||
|
("C-c p f" . imalison:projectile-find-file))
|
||||||
:custom
|
:custom
|
||||||
((project-vc-merge-submodules nil)
|
((projectile-require-project-root nil)
|
||||||
(project-vc-extra-root-markers '(".project" ".projectile"))
|
(projectile-enable-caching nil)
|
||||||
(project-switch-commands 'imalison:project-find-file))
|
(projectile-git-submodule-command nil)
|
||||||
|
(projectile-git-use-fd t)
|
||||||
|
(project-vc-merge-submodules nil)
|
||||||
|
(projectile-current-project-on-switch 'keep))
|
||||||
:config
|
:config
|
||||||
(progn
|
(progn
|
||||||
(defun imalison:project-root (&optional directory)
|
(defmacro imalison:projectile-do-in-project (project-dir &rest forms)
|
||||||
"Return DIRECTORY's project root, falling back to DIRECTORY itself."
|
|
||||||
(let* ((default-directory (file-name-as-directory
|
|
||||||
(expand-file-name
|
|
||||||
(or directory default-directory))))
|
|
||||||
(project (project-current nil default-directory)))
|
|
||||||
(file-name-as-directory
|
|
||||||
(expand-file-name
|
|
||||||
(if project
|
|
||||||
(project-root project)
|
|
||||||
default-directory)))))
|
|
||||||
|
|
||||||
(defmacro imalison:project-do-in-project (project-dir &rest forms)
|
|
||||||
`(imalison:with-default-directory ,project-dir
|
`(imalison:with-default-directory ,project-dir
|
||||||
(let ((project-current-directory-override ,project-dir))
|
(noflet ((projectile-project-root (&rest args) ,project-dir))
|
||||||
,@forms)))
|
,@forms)))
|
||||||
|
|
||||||
(defmacro imalison:with-default-directory (directory &rest forms)
|
(defmacro imalison:with-default-directory (directory &rest forms)
|
||||||
`(let ((default-directory ,directory))
|
`(let ((default-directory ,directory))
|
||||||
,@forms))
|
,@forms))
|
||||||
|
|
||||||
|
(defvar imalison:projectile-find-ignore-file ".projectile-find-ignore"
|
||||||
|
"Project-local fd ignore file used only for Projectile file finding.")
|
||||||
|
|
||||||
|
(defun imalison:projectile-fd-ignore-file-arg ()
|
||||||
|
"Return a shell fragment that adds `imalison:projectile-find-ignore-file' when present."
|
||||||
|
(let ((ignore-file (shell-quote-argument imalison:projectile-find-ignore-file)))
|
||||||
|
(format "$(test -f %s && printf -- '--ignore-file %s')" ignore-file ignore-file)))
|
||||||
|
|
||||||
|
(defun imalison:projectile-fd-command (&optional pattern)
|
||||||
|
"Build an fd command for Projectile file finding.
|
||||||
|
The command includes ignored and hidden files by default, then applies
|
||||||
|
`imalison:projectile-find-ignore-file' when that file exists in the project
|
||||||
|
root. PATTERN is passed as fd's search pattern when non-nil."
|
||||||
|
(mapconcat
|
||||||
|
#'identity
|
||||||
|
(delq nil
|
||||||
|
(list projectile-fd-executable
|
||||||
|
pattern
|
||||||
|
"-H"
|
||||||
|
"--no-ignore"
|
||||||
|
(imalison:projectile-fd-ignore-file-arg)
|
||||||
|
"-0"
|
||||||
|
"-E .git"
|
||||||
|
"-tf"
|
||||||
|
"--strip-cwd-prefix"
|
||||||
|
"-c never"))
|
||||||
|
" "))
|
||||||
|
|
||||||
|
(when projectile-fd-executable
|
||||||
|
(setq projectile-git-fd-args
|
||||||
|
(replace-regexp-in-string
|
||||||
|
(concat "^" (regexp-quote projectile-fd-executable) " ")
|
||||||
|
""
|
||||||
|
(imalison:projectile-fd-command)))
|
||||||
|
(setq projectile-generic-command
|
||||||
|
(imalison:projectile-fd-command ".")))
|
||||||
|
|
||||||
(defun imalison:do-rg-default-directory (&rest args)
|
(defun imalison:do-rg-default-directory (&rest args)
|
||||||
(interactive)
|
(interactive)
|
||||||
(let ((consult-ripgrep-args (concat consult-ripgrep-args " --no-ignore" " --hidden")))
|
(let ((consult-ripgrep-args (concat consult-ripgrep-args " --no-ignore" " --hidden")))
|
||||||
@@ -1822,35 +1812,24 @@ bind-key and global-set-key forms.
|
|||||||
consult-ripgrep
|
consult-ripgrep
|
||||||
imalison:do-rg-default-directory)
|
imalison:do-rg-default-directory)
|
||||||
|
|
||||||
(defun imalison:project-find-file ()
|
(emit-prefix-selector imalison:projectile-find-file
|
||||||
"Find a file in the current `project.el' project."
|
projectile-find-file
|
||||||
(interactive)
|
projectile-find-file-other-window)
|
||||||
(let* ((project-root (file-name-as-directory
|
|
||||||
(expand-file-name
|
|
||||||
(or project-current-directory-override
|
|
||||||
(imalison:project-root)))))
|
|
||||||
(default-directory project-root)
|
|
||||||
(project-current-directory-override project-root))
|
|
||||||
(call-interactively #'project-find-file)))
|
|
||||||
|
|
||||||
(defun imalison:project-switch-project ()
|
|
||||||
"Switch projects using `project.el'."
|
|
||||||
(interactive)
|
|
||||||
(call-interactively #'project-switch-project))
|
|
||||||
|
|
||||||
(define-key project-prefix-map (kbd "f") #'imalison:project-find-file)
|
|
||||||
(define-key project-prefix-map (kbd "s") #'imalison:do-rg)
|
|
||||||
|
|
||||||
(imalison:let-around imalison:set-options-do-rg
|
(imalison:let-around imalison:set-options-do-rg
|
||||||
imalison:do-rg)
|
imalison:do-rg)
|
||||||
|
|
||||||
(defun imalison:project-make-all-subdirs-projects (directory)
|
(defun imalison:projectile-make-all-subdirs-projects (directory)
|
||||||
(cl-loop for file-info in (directory-files-and-attributes directory)
|
(cl-loop for file-info in (directory-files-and-attributes directory)
|
||||||
do (when (nth 1 file-info)
|
do (when (nth 1 file-info)
|
||||||
(write-region "" nil
|
(write-region "" nil
|
||||||
(expand-file-name
|
(expand-file-name
|
||||||
(concat directory "/"
|
(concat directory "/"
|
||||||
(nth 0 file-info) "/.project"))))))))
|
(nth 0 file-info) "/.projectile")))))))
|
||||||
|
:config
|
||||||
|
(progn
|
||||||
|
(projectile-global-mode)
|
||||||
|
(diminish 'projectile-mode)))
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
** ido
|
** ido
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
@@ -2005,38 +1984,37 @@ bind-key and global-set-key forms.
|
|||||||
:config
|
:config
|
||||||
(progn
|
(progn
|
||||||
(setq embark-mixed-indicator-delay 1.0)
|
(setq embark-mixed-indicator-delay 1.0)
|
||||||
(defmacro imalison:embark-project-act-for-file (file &rest forms)
|
(defmacro imalison:embark-projectile-act-for-file (file &rest forms)
|
||||||
`(let ((default-directory
|
`(let ((default-directory (projectile-project-root ,file)))
|
||||||
(imalison:project-root (file-name-directory ,file))))
|
(imalison:projectile-do-in-project default-directory ,@forms)))
|
||||||
(imalison:project-do-in-project default-directory ,@forms)))
|
|
||||||
|
|
||||||
(defmacro imalison:build-embark-project-for-file (command)
|
(defmacro imalison:build-embark-projectile-for-file (command)
|
||||||
`(defun ,(intern (concat "imalison:embark-project-file-" (symbol-name command))) (filepath)
|
`(defun ,(intern (concat "imalison:embark-projectile-file-" (symbol-name command))) (filepath)
|
||||||
(imalison:embark-project-act-for-file filepath (,command))))
|
(imalison:embark-projectile-act-for-file filepath (,command))))
|
||||||
|
|
||||||
(imalison:build-embark-project-for-file term-project-forward)
|
(imalison:build-embark-projectile-for-file term-projectile-switch)
|
||||||
(imalison:build-embark-project-for-file magit-status)
|
(imalison:build-embark-projectile-for-file magit-status)
|
||||||
(imalison:build-embark-project-for-file consult-ripgrep)
|
(imalison:build-embark-projectile-for-file consult-ripgrep)
|
||||||
|
|
||||||
(setq embark-prompter #'embark-keymap-prompter)
|
(setq embark-prompter #'embark-keymap-prompter)
|
||||||
|
|
||||||
(define-key embark-general-map (kbd "t")
|
(define-key embark-general-map (kbd "t")
|
||||||
#'imalison:embark-project-file-term-project-forward)
|
#'imalison:embark-projectile-file-term-projectile-switch)
|
||||||
|
|
||||||
(define-key embark-general-map (kbd "m")
|
(define-key embark-general-map (kbd "m")
|
||||||
#'imalison:embark-project-file-magit-status)
|
#'imalison:embark-projectile-file-magit-status)
|
||||||
|
|
||||||
(define-key embark-general-map (kbd "g")
|
(define-key embark-general-map (kbd "g")
|
||||||
#'imalison:embark-project-file-magit-status)
|
#'imalison:embark-projectile-file-magit-status)
|
||||||
|
|
||||||
(define-key embark-general-map (kbd "s")
|
(define-key embark-general-map (kbd "s")
|
||||||
#'imalison:embark-project-file-consult-ripgrep)
|
#'imalison:embark-projectile-file-consult-ripgrep)
|
||||||
|
|
||||||
(defvar-keymap imalison:project-embark-map
|
(defvar-keymap imalison:projectile-embark-map
|
||||||
:doc "Keymap for actions on projects"
|
:doc "Keymap for actions on projectile projects"
|
||||||
"m" #'imalison:embark-project-file-magit-status
|
"m" #'imalison:embark-projectile-file-magit-status
|
||||||
"t" #'imalison:embark-project-file-term-project-forward
|
"t" #'imalison:embark-projectile-file-term-projectile-switch
|
||||||
"s" #'imalison:embark-project-file-consult-ripgrep)))
|
"s" #'imalison:embark-projectile-file-consult-ripgrep)))
|
||||||
|
|
||||||
(use-package embark-consult
|
(use-package embark-consult
|
||||||
:hook
|
:hook
|
||||||
@@ -2051,7 +2029,7 @@ bind-key and global-set-key forms.
|
|||||||
("C-x C-i" . consult-imenu))
|
("C-x C-i" . consult-imenu))
|
||||||
:config
|
:config
|
||||||
(progn
|
(progn
|
||||||
(setq consult-project-function #'imalison:project-root)))
|
(setq consult-project-function 'projectile-project-root)))
|
||||||
#+end_src
|
#+end_src
|
||||||
** company
|
** company
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
@@ -2144,7 +2122,8 @@ bind-key and global-set-key forms.
|
|||||||
** multi-line
|
** multi-line
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
(use-package multi-line
|
(use-package multi-line
|
||||||
:commands multi-line
|
;; Demand multi-line to avoid failure to load mode specific strategies
|
||||||
|
:demand t
|
||||||
:bind ("C-c d" . multi-line)
|
:bind ("C-c d" . multi-line)
|
||||||
:config
|
:config
|
||||||
(progn
|
(progn
|
||||||
@@ -2209,10 +2188,11 @@ bind-key and global-set-key forms.
|
|||||||
** yasnippet
|
** yasnippet
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
(use-package yasnippet
|
(use-package yasnippet
|
||||||
:commands (yas-expand yas-global-mode yas-insert-snippet yas-minor-mode)
|
:defer 5
|
||||||
:hook ((prog-mode text-mode conf-mode) . yas-minor-mode)
|
:commands (yas-global-mode)
|
||||||
:config
|
:config
|
||||||
(progn
|
(progn
|
||||||
|
(yas-global-mode)
|
||||||
(diminish 'yas-minor-mode)
|
(diminish 'yas-minor-mode)
|
||||||
(add-hook 'term-mode-hook (lambda() (yas-minor-mode -1)))
|
(add-hook 'term-mode-hook (lambda() (yas-minor-mode -1)))
|
||||||
(setq yas-prompt-functions
|
(setq yas-prompt-functions
|
||||||
@@ -2220,7 +2200,8 @@ bind-key and global-set-key forms.
|
|||||||
(cl-delete 'yas-ido-prompt yas-prompt-functions)))))
|
(cl-delete 'yas-ido-prompt yas-prompt-functions)))))
|
||||||
|
|
||||||
(use-package yasnippet-snippets
|
(use-package yasnippet-snippets
|
||||||
:after yasnippet)
|
:after yasnippet
|
||||||
|
:demand t)
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
** align
|
** align
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
@@ -2341,7 +2322,7 @@ bind-key and global-set-key forms.
|
|||||||
(defvar imalison:use-lsp-go t)
|
(defvar imalison:use-lsp-go t)
|
||||||
|
|
||||||
(defun imalison:glide-novendor ()
|
(defun imalison:glide-novendor ()
|
||||||
(let ((default-directory (imalison:project-root)))
|
(projectile-with-default-dir (projectile-project-root)
|
||||||
(shell-command-to-string "glide novendor")))
|
(shell-command-to-string "glide novendor")))
|
||||||
|
|
||||||
(defun imalison:go-mode-create-imenu-index ()
|
(defun imalison:go-mode-create-imenu-index ()
|
||||||
@@ -2366,7 +2347,7 @@ bind-key and global-set-key forms.
|
|||||||
(nconc type-index (list (cons "func" func-index)))))
|
(nconc type-index (list (cons "func" func-index)))))
|
||||||
|
|
||||||
(defun imalison:go-workspace-path ()
|
(defun imalison:go-workspace-path ()
|
||||||
(file-relative-name (imalison:project-root)
|
(file-relative-name (projectile-project-root)
|
||||||
(concat (file-name-as-directory
|
(concat (file-name-as-directory
|
||||||
(imalison:get-go-path)) "src")))
|
(imalison:get-go-path)) "src")))
|
||||||
|
|
||||||
@@ -2399,10 +2380,12 @@ bind-key and global-set-key forms.
|
|||||||
(if (executable-find "goimports") "goimports" "gofmt"))
|
(if (executable-find "goimports") "goimports" "gofmt"))
|
||||||
(setq-local imenu-create-index-function
|
(setq-local imenu-create-index-function
|
||||||
#'imalison:go-mode-create-imenu-index)
|
#'imalison:go-mode-create-imenu-index)
|
||||||
|
(make-local-variable 'projectile-globally-ignored-files)
|
||||||
(add-hook 'after-save-hook 'imalison:install-current-go-project nil
|
(add-hook 'after-save-hook 'imalison:install-current-go-project nil
|
||||||
'yes-do-local)
|
'yes-do-local)
|
||||||
(add-hook 'before-save-hook 'gofmt-before-save nil 'yes-do-local)
|
(add-hook 'before-save-hook 'gofmt-before-save nil 'yes-do-local)
|
||||||
(setq-local project-vc-ignores (cons "vendor/" project-vc-ignores))
|
(add-to-list 'projectile-globally-ignored-files
|
||||||
|
"vendor")
|
||||||
(when (and imalison:use-lsp-go
|
(when (and imalison:use-lsp-go
|
||||||
(fboundp 'lsp-deferred)
|
(fboundp 'lsp-deferred)
|
||||||
(or (executable-find "gopls")
|
(or (executable-find "gopls")
|
||||||
@@ -2914,14 +2897,11 @@ The following is taken from [[https://github.com/syl20bnr/spacemacs/blob/a650877
|
|||||||
#+END_SRC
|
#+END_SRC
|
||||||
*** swift
|
*** swift
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
(use-package swift-mode
|
(use-package swift-mode)
|
||||||
:mode "\\.swift\\'")
|
|
||||||
#+end_src
|
#+end_src
|
||||||
*** groovy
|
*** groovy
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
(use-package groovy-mode
|
(use-package groovy-mode)
|
||||||
:mode (("\\.groovy\\'" . groovy-mode)
|
|
||||||
("\\.gradle\\'" . groovy-mode)))
|
|
||||||
#+end_src
|
#+end_src
|
||||||
*** vala
|
*** vala
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
@@ -2971,8 +2951,7 @@ The following is taken from [[https://github.com/syl20bnr/spacemacs/blob/a650877
|
|||||||
#+END_SRC
|
#+END_SRC
|
||||||
*** graphql
|
*** graphql
|
||||||
#+begin_src emacs-lisp
|
#+begin_src emacs-lisp
|
||||||
(use-package graphql-mode
|
(use-package graphql-mode)
|
||||||
:mode "\\.graphql\\'")
|
|
||||||
#+end_src
|
#+end_src
|
||||||
*** json-mode
|
*** json-mode
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
@@ -3496,20 +3475,18 @@ in term-mode. This makes term-mode 1000% more useful
|
|||||||
(advice-add
|
(advice-add
|
||||||
'term-manager-default-build-term :after 'imalison:set-escape-char)))
|
'term-manager-default-build-term :after 'imalison:set-escape-char)))
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
** term-project
|
** term-projectile
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
(use-package term-project
|
(use-package term-projectile
|
||||||
:ensure (term-project :files ("term-project.el")
|
|
||||||
:host github :repo "colonelpanic8/term-manager")
|
|
||||||
:bind ("C-c 7" . imalison:term-hydra-global/body)
|
:bind ("C-c 7" . imalison:term-hydra-global/body)
|
||||||
:commands
|
:commands
|
||||||
(term-project-backward
|
(term-projectile-backward
|
||||||
term-project-create-new
|
term-projectile-create-new
|
||||||
term-project-default-directory-backward
|
term-projectile-create-new-default-directory
|
||||||
term-project-default-directory-create-new
|
term-projectile-default-directory-backward
|
||||||
term-project-default-directory-forward
|
term-projectile-default-directory-forward
|
||||||
term-project-forward
|
term-projectile-forward
|
||||||
term-project-switch)
|
term-projectile-switch)
|
||||||
:config
|
:config
|
||||||
(progn
|
(progn
|
||||||
(use-package term-manager-eat
|
(use-package term-manager-eat
|
||||||
@@ -3517,51 +3494,37 @@ in term-mode. This makes term-mode 1000% more useful
|
|||||||
:ensure
|
:ensure
|
||||||
(term-manager-eat :files ("term-manager-eat.el")
|
(term-manager-eat :files ("term-manager-eat.el")
|
||||||
:host github :repo "colonelpanic8/term-manager"))
|
:host github :repo "colonelpanic8/term-manager"))
|
||||||
(require 'term-manager-eat)
|
(setq term-projectile-term-manager (term-projectile :build-term 'term-manager-eat-build-term))
|
||||||
(defun term-project-get-symbol-for-buffer (buffer)
|
|
||||||
"Get the project root symbol for BUFFER, falling back to its directory."
|
|
||||||
(term-project-maybe-intern
|
|
||||||
(with-current-buffer buffer
|
|
||||||
(if (or (derived-mode-p 'term-mode)
|
|
||||||
(derived-mode-p 'eat-mode))
|
|
||||||
default-directory
|
|
||||||
(imalison:project-root)))))
|
|
||||||
|
|
||||||
(setq term-project-term-manager
|
|
||||||
(term-project :build-term 'term-manager-eat-build-term))
|
|
||||||
(term-manager-enable-eat-buffer-renaming-and-reindexing
|
|
||||||
term-project-term-manager)
|
|
||||||
|
|
||||||
(emit-prefix-selector imalison:term
|
(emit-prefix-selector imalison:term
|
||||||
term-project-forward
|
term-projectile-forward
|
||||||
term-project-create-new)
|
term-projectile-create-new)
|
||||||
|
|
||||||
(defvar imalison:term-hydra-original-default-directory)
|
(defvar imalison:term-hydra-original-default-directory)
|
||||||
|
|
||||||
(defhydra imalison:term-hydra-default-directory
|
(defhydra imalison:term-hydra-default-directory
|
||||||
(:body-pre
|
(:body-pre
|
||||||
(term-project-default-directory-forward-restored))
|
(term-projectile-default-directory-forward-restored))
|
||||||
"term - default-directory"
|
"term - default-directory"
|
||||||
("s" term-project-switch-to "Switch to existing")
|
("s" term-projectile-switch-to "Switch to existing")
|
||||||
("f" term-project-default-directory-forward-restored "Forward for current directory terminals")
|
("f" term-projectile-default-directory-forward-restored "Forward for current directory terminals")
|
||||||
("b" term-project-default-directory-backward-restored "Backward for current directory terminals")
|
("b" term-projectile-default-directory-backward-restored "Backward for current directory terminals")
|
||||||
("c" term-project-default-directory-create-new-restored "Create new current directory terminal")
|
("c" term-projectile-default-directory-create-new-restored "Create new current directory terminal")
|
||||||
("d" term-project-default-directory-forward-restored "Switch/Create default directory terminal")
|
("d" term-projectile-default-directory-forward-restored "Switch/Create default directory terminal")
|
||||||
("g" imalison:term-hydra-global/body-restored "Switch/Create global terminal" :exit t)
|
("g" imalison:term-hydra-global/body-restored "Switch/Create global terminal" :exit t)
|
||||||
("p" imalison:term-hydra-project/body-restored "Switch/Create project terminal" :exit t))
|
("p" imalison:term-hydra-projectile/body-restored "Switch/Create project terminal" :exit t))
|
||||||
|
|
||||||
(defhydra imalison:term-hydra-project
|
(defhydra imalison:term-hydra-projectile
|
||||||
(:body-pre
|
(:body-pre
|
||||||
(progn
|
(progn
|
||||||
(term-project-forward-restored)))
|
(term-projectile-forward-restored)))
|
||||||
"term - project"
|
"term - projectile"
|
||||||
("s" term-project-switch-to "Switch to existing")
|
("s" term-projectile-switch-to "Switch to existing")
|
||||||
("f" term-project-forward-restored "Forward for project terminals")
|
("f" term-projectile-forward-restored "Forward for project terminals")
|
||||||
("b" term-project-backward-restored "Backward for project terminals")
|
("b" term-projectile-backward-restored "Backward for project terminals")
|
||||||
("c" term-project-create-new-restored "Create new project terminal")
|
("c" term-projectile-create-new-restored "Create new project terminal")
|
||||||
("d" imalison:term-hydra-default-directory/body-restored "Switch/Create default directory terminal" :exit t)
|
("d" imalison:term-hydra-default-directory/body-restored "Switch/Create default directory terminal" :exit t)
|
||||||
("g" imalison:term-hydra-global/body-restored "Switch/Create global terminal" :exit t)
|
("g" imalison:term-hydra-global/body-restored "Switch/Create global terminal" :exit t)
|
||||||
("p" term-project-forward-restored "Switch/Create project terminal"))
|
("p" term-projectile-forward-restored "Switch/Create project terminal"))
|
||||||
|
|
||||||
|
|
||||||
(defhydra imalison:term-hydra-global
|
(defhydra imalison:term-hydra-global
|
||||||
@@ -3569,31 +3532,31 @@ in term-mode. This makes term-mode 1000% more useful
|
|||||||
(progn (setq imalison:term-hydra-original-default-directory
|
(progn (setq imalison:term-hydra-original-default-directory
|
||||||
default-directory)))
|
default-directory)))
|
||||||
"term - global"
|
"term - global"
|
||||||
("s" term-project-switch-to "Switch to existing")
|
("s" term-projectile-switch-to "Switch to existing")
|
||||||
("f" term-project-global-forward-restored "Forward for project terminals")
|
("f" term-projectile-global-forward-restored "Forward for project terminals")
|
||||||
("b" term-project-global-backward-restored "Backward for project terminals")
|
("b" term-projectile-global-backward-restored "Backward for project terminals")
|
||||||
("c" term-project-global-create-new-restored "Create new project terminal")
|
("c" term-projectile-global-create-new-restored "Create new project terminal")
|
||||||
("d" imalison:term-hydra-default-directory/body-restored "Switch/Create default directory terminal" :exit t)
|
("d" imalison:term-hydra-default-directory/body-restored "Switch/Create default directory terminal" :exit t)
|
||||||
("g" term-project-global-forward-restored "Switch/Create global terminal")
|
("g" term-projectile-global-forward-restored "Switch/Create global terminal")
|
||||||
("p" imalison:term-hydra-project/body-restored "Switch/Create project terminal" :exit t))
|
("p" imalison:term-hydra-projectile/body-restored "Switch/Create project terminal" :exit t))
|
||||||
|
|
||||||
(mapcar (lambda (term-project-function)
|
(mapcar (lambda (term-projectile-function)
|
||||||
(defalias (imalison:concat-symbols term-project-function '-restored)
|
(defalias (imalison:concat-symbols term-projectile-function '-restored)
|
||||||
(lambda (&rest args)
|
(lambda (&rest args)
|
||||||
(interactive)
|
(interactive)
|
||||||
(let ((default-directory imalison:term-hydra-original-default-directory))
|
(let ((default-directory imalison:term-hydra-original-default-directory))
|
||||||
(apply term-project-function args)))))
|
(apply term-projectile-function args)))))
|
||||||
'(term-project-default-directory-forward
|
'(term-projectile-default-directory-forward
|
||||||
term-project-default-directory-backward
|
term-projectile-default-directory-backward
|
||||||
term-project-default-directory-create-new
|
term-projectile-default-directory-create-new
|
||||||
term-project-forward
|
term-projectile-forward
|
||||||
term-project-backward
|
term-projectile-backward
|
||||||
term-project-create-new
|
term-projectile-create-new
|
||||||
term-project-global-forward
|
term-projectile-global-forward
|
||||||
term-project-global-backward
|
term-projectile-global-backward
|
||||||
term-project-global-create-new
|
term-projectile-global-create-new
|
||||||
imalison:term-hydra-global/body
|
imalison:term-hydra-global/body
|
||||||
imalison:term-hydra-project/body
|
imalison:term-hydra-projectile/body
|
||||||
imalison:term-hydra-default-directory/body))))
|
imalison:term-hydra-default-directory/body))))
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
** crux
|
** crux
|
||||||
@@ -3675,28 +3638,8 @@ I don't use iedit directly, but it is used by [[*emr][emr]] and I need to disabl
|
|||||||
(use-package tramp
|
(use-package tramp
|
||||||
:ensure nil
|
:ensure nil
|
||||||
:commands tramp
|
:commands tramp
|
||||||
:init
|
|
||||||
;; Avoid TRAMP's GVFS backend entirely. It depends on D-Bus/GVFS desktop
|
|
||||||
;; services, while this config uses ordinary TRAMP methods.
|
|
||||||
(setq tramp-gvfs-methods nil)
|
|
||||||
:config
|
:config
|
||||||
(setq tramp-default-method "scp")
|
(setq tramp-default-method "scp"))
|
||||||
(setq tramp-methods
|
|
||||||
(seq-remove
|
|
||||||
(lambda (method)
|
|
||||||
(member (car method)
|
|
||||||
'("afp" "dav" "davs" "gdrive" "mtp" "nextcloud" "sftp")))
|
|
||||||
tramp-methods))
|
|
||||||
(when (boundp 'tramp-foreign-file-name-handler-alist)
|
|
||||||
(setq tramp-foreign-file-name-handler-alist
|
|
||||||
(assq-delete-all 'tramp-gvfs-file-name-p
|
|
||||||
tramp-foreign-file-name-handler-alist))))
|
|
||||||
#+END_SRC
|
|
||||||
** dbus
|
|
||||||
#+BEGIN_SRC emacs-lisp
|
|
||||||
;; D-Bus is disabled by lisp/dbus.el, which shadows Emacs' built-in dbus.el.
|
|
||||||
;; Keep the event hook empty in case another environment loads the real library.
|
|
||||||
(setq dbus-event-error-functions nil)
|
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
** narrow-indirect
|
** narrow-indirect
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
@@ -3763,9 +3706,7 @@ I had to disable this mode because something that it does messes with coding set
|
|||||||
:demand t
|
:demand t
|
||||||
:config
|
:config
|
||||||
(progn
|
(progn
|
||||||
(setq recentf-initialize-file-name-history nil
|
(setq recentf-max-saved-items 1000
|
||||||
recentf-auto-cleanup 'never
|
|
||||||
recentf-max-saved-items 1000
|
|
||||||
recentf-max-menu-items 1000)
|
recentf-max-menu-items 1000)
|
||||||
(advice-add 'recentf-cleanup :around 'imalison:shut-up-around)
|
(advice-add 'recentf-cleanup :around 'imalison:shut-up-around)
|
||||||
(recentf-mode +1)))
|
(recentf-mode +1)))
|
||||||
@@ -3787,8 +3728,8 @@ I have currently disabled key-chord because it may cause typing lag.
|
|||||||
(key-chord-mode 1)
|
(key-chord-mode 1)
|
||||||
(advice-add 'imalison:avy :around 'imalison:disable-keychord-around)
|
(advice-add 'imalison:avy :around 'imalison:disable-keychord-around)
|
||||||
(key-chord-define-global "tg" 'imalison:term-hydra/body)
|
(key-chord-define-global "tg" 'imalison:term-hydra/body)
|
||||||
(key-chord-define-global "pj" 'imalison:project-find-file)
|
(key-chord-define-global "pj" 'imalison:projectile-find-file)
|
||||||
(key-chord-define-global "p[" 'imalison:project-switch-project)
|
(key-chord-define-global "p[" 'projectile-switch-project)
|
||||||
(key-chord-define-global "fj" 'imalison:do-ag)
|
(key-chord-define-global "fj" 'imalison:do-ag)
|
||||||
(key-chord-define-global "jh" 'imalison:avy)))
|
(key-chord-define-global "jh" 'imalison:avy)))
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
@@ -3890,36 +3831,13 @@ This is useful with server mode when editing gmail messages. I think that it is
|
|||||||
(use-package alert
|
(use-package alert
|
||||||
:defer t
|
:defer t
|
||||||
:preface
|
:preface
|
||||||
(defun imalison:linux-notifications-available-p ()
|
|
||||||
(and (eq system-type 'gnu/linux)
|
|
||||||
(executable-find "notify-send")))
|
|
||||||
(defun imalison:notify-send-urgency (severity)
|
|
||||||
(pcase severity
|
|
||||||
((or 'urgent 'high) "critical")
|
|
||||||
('low "low")
|
|
||||||
(_ "normal")))
|
|
||||||
(defun imalison:alert-notify-send (info)
|
|
||||||
(when-let ((program (executable-find "notify-send")))
|
|
||||||
(let ((process-connection-type nil))
|
|
||||||
(start-process "alert-notify-send" nil program
|
|
||||||
"-u" (imalison:notify-send-urgency
|
|
||||||
(plist-get info :severity))
|
|
||||||
(or (plist-get info :title) "Emacs")
|
|
||||||
(or (plist-get info :message) "")))))
|
|
||||||
(defun imalison:windows-toast-notify (info)
|
(defun imalison:windows-toast-notify (info)
|
||||||
(let ((message (plist-get info :message))
|
(let ((message (plist-get info :message))
|
||||||
(title (plist-get info :title)))
|
(title (plist-get info :title)))
|
||||||
(shell-command (format "windows_toast '%s' '%s'" (or title "No title") (or message "No message")))))
|
(shell-command (format "windows_toast '%s' '%s'" (or title "No title") (or message "No message")))))
|
||||||
:config
|
:config
|
||||||
(progn
|
(progn
|
||||||
(alert-define-style
|
(setq alert-default-style 'libnotify)
|
||||||
'notify-send
|
|
||||||
:title "notify-send"
|
|
||||||
:notifier 'imalison:alert-notify-send)
|
|
||||||
(setq alert-default-style
|
|
||||||
(if (imalison:linux-notifications-available-p)
|
|
||||||
'notify-send
|
|
||||||
'message))
|
|
||||||
(when (not (string-empty-p (shell-command-to-string "grep -i microsoft /proc/version")))
|
(when (not (string-empty-p (shell-command-to-string "grep -i microsoft /proc/version")))
|
||||||
(alert-define-style
|
(alert-define-style
|
||||||
'windows-toast
|
'windows-toast
|
||||||
@@ -4135,9 +4053,6 @@ Ensure all themes that I use are installed:
|
|||||||
:commands doom-modeline-mode
|
:commands doom-modeline-mode
|
||||||
:custom
|
:custom
|
||||||
(doom-modeline-height 40))
|
(doom-modeline-height 40))
|
||||||
|
|
||||||
(defvar imalison:enable-doom-modeline-on-startup t
|
|
||||||
"Non-nil means enable `doom-modeline-mode' during startup.")
|
|
||||||
#+end_src
|
#+end_src
|
||||||
** page-break-lines
|
** page-break-lines
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
@@ -4288,24 +4203,11 @@ load-theme hook (See the heading below).
|
|||||||
(load-theme imalison:dark-theme t))
|
(load-theme imalison:dark-theme t))
|
||||||
(apply 'imalison:appearance args)
|
(apply 'imalison:appearance args)
|
||||||
(message "running appearance")
|
(message "running appearance")
|
||||||
(when (and imalison:enable-doom-modeline-on-startup
|
(doom-modeline-mode +1)
|
||||||
(fboundp 'doom-modeline-mode))
|
|
||||||
(doom-modeline-mode +1))
|
|
||||||
(setq imalison:default-font-size-pt (face-attribute 'default :height))
|
(setq imalison:default-font-size-pt (face-attribute 'default :height))
|
||||||
(setq imalison:appearance-setup-done t)))
|
(setq imalison:appearance-setup-done t)))
|
||||||
|
|
||||||
(defun imalison:daemon-startup-p ()
|
(add-hook 'elpaca-after-init-hook 'imalison:appearance-setup-hook)
|
||||||
(or (daemonp)
|
|
||||||
(seq-some (lambda (arg)
|
|
||||||
(or (string= arg "--daemon")
|
|
||||||
(string= arg "--bg-daemon")
|
|
||||||
(string-prefix-p "--daemon=" arg)
|
|
||||||
(string-prefix-p "--bg-daemon=" arg)))
|
|
||||||
command-line-args)))
|
|
||||||
|
|
||||||
(if (imalison:daemon-startup-p)
|
|
||||||
(add-hook 'server-after-make-frame-hook 'imalison:appearance-setup-hook)
|
|
||||||
(add-hook 'elpaca-after-init-hook 'imalison:appearance-setup-hook))
|
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
* Post Init Custom
|
* Post Init Custom
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
|
|||||||
@@ -186,9 +186,5 @@
|
|||||||
(let ((load-source-file-function nil)) (load autoloads))))
|
(let ((load-source-file-function nil)) (load autoloads))))
|
||||||
(require 'elpaca)
|
(require 'elpaca)
|
||||||
(setq elpaca-log-functions '(elpaca-log-command-query))
|
(setq elpaca-log-functions '(elpaca-log-command-query))
|
||||||
(if (daemonp)
|
(add-hook 'after-init-hook #'elpaca-process-queues)
|
||||||
(add-hook 'after-init-hook
|
|
||||||
(lambda ()
|
|
||||||
(run-with-idle-timer 1 nil #'elpaca-process-queues)))
|
|
||||||
(add-hook 'after-init-hook #'elpaca-process-queues))
|
|
||||||
(elpaca `(,@elpaca-order))
|
(elpaca `(,@elpaca-order))
|
||||||
|
|||||||
@@ -10,15 +10,6 @@
|
|||||||
(defun emacs-directory-filepath (filename)
|
(defun emacs-directory-filepath (filename)
|
||||||
(expand-file-name filename user-emacs-directory))
|
(expand-file-name filename user-emacs-directory))
|
||||||
|
|
||||||
(add-to-list 'load-path (emacs-directory-filepath "lisp"))
|
|
||||||
|
|
||||||
;; Treat this Emacs as if it was built without D-Bus. The local
|
|
||||||
;; lisp/dbus.el shim prevents `require' from loading the built-in dbus.el,
|
|
||||||
;; whose top-level form eagerly opens system and session bus connections.
|
|
||||||
(setq features (delq 'dbusbind features))
|
|
||||||
(setq dbus-compiled-version nil
|
|
||||||
dbus-runtime-version nil)
|
|
||||||
|
|
||||||
(load-file (expand-file-name "elpaca-installer.el" user-emacs-directory))
|
(load-file (expand-file-name "elpaca-installer.el" user-emacs-directory))
|
||||||
|
|
||||||
;; Elpaca's initial queue logger can fire during self-bootstrap before its
|
;; Elpaca's initial queue logger can fire during self-bootstrap before its
|
||||||
@@ -96,14 +87,6 @@
|
|||||||
(setq custom-file "~/.emacs.d/custom-before.el")
|
(setq custom-file "~/.emacs.d/custom-before.el")
|
||||||
(setq load-prefer-newer t)
|
(setq load-prefer-newer t)
|
||||||
|
|
||||||
;; Magit 4.5 and Vertico 2.8 use `set-local', which is native in Emacs 31
|
|
||||||
;; and provided by recent compat releases. Keep Emacs 30 usable even if
|
|
||||||
;; package bytecode is stale or compat has not been activated yet.
|
|
||||||
(unless (fboundp 'set-local)
|
|
||||||
(defun set-local (variable value)
|
|
||||||
"Make VARIABLE buffer-local and set it to VALUE."
|
|
||||||
(set (make-local-variable variable) value)))
|
|
||||||
|
|
||||||
;; If this isn't here and there's a problem with init, graphical emacs
|
;; If this isn't here and there's a problem with init, graphical emacs
|
||||||
;; is super annoying.
|
;; is super annoying.
|
||||||
(when (equal system-type 'darwin)
|
(when (equal system-type 'darwin)
|
||||||
@@ -122,22 +105,10 @@
|
|||||||
:config
|
:config
|
||||||
(progn (dash-enable-font-lock)))
|
(progn (dash-enable-font-lock)))
|
||||||
|
|
||||||
;; Emacs 30 ships an older `compat' as a core library. Load Elpaca's newer
|
|
||||||
;; package explicitly so packages compiled against Compat 31 do not silently
|
|
||||||
;; see the built-in library.
|
|
||||||
(elpaca `(compat :host github :repo "emacs-compat/compat" :wait t))
|
|
||||||
(elpaca-wait)
|
|
||||||
(let ((compat-build-dir (expand-file-name "compat" elpaca-builds-directory)))
|
|
||||||
(when (file-directory-p compat-build-dir)
|
|
||||||
(add-to-list 'load-path compat-build-dir)
|
|
||||||
(load (expand-file-name "compat" compat-build-dir) nil 'nomessage)))
|
|
||||||
|
|
||||||
;; Some split packages fall through the active menus in this config. Give
|
;; Some split packages fall through the active menus in this config. Give
|
||||||
;; Elpaca an explicit source so startup doesn't get stuck on recipe lookup or
|
;; Elpaca an explicit source so startup doesn't get stuck on recipe lookup or
|
||||||
;; stale branch-mapped clones.
|
;; stale branch-mapped clones.
|
||||||
(elpaca `(queue :host github :repo "emacs-straight/queue"))
|
(elpaca `(queue :host github :repo "emacs-straight/queue"))
|
||||||
(elpaca `(with-editor :host github :repo "magit/with-editor"
|
|
||||||
:branch "main"))
|
|
||||||
(elpaca `(git-commit :host github :repo "magit/magit"
|
(elpaca `(git-commit :host github :repo "magit/magit"
|
||||||
:files ("lisp/git-commit.el" "lisp/git-commit-pkg.el")))
|
:files ("lisp/git-commit.el" "lisp/git-commit-pkg.el")))
|
||||||
(elpaca `(magit-section :host github :repo "magit/magit"
|
(elpaca `(magit-section :host github :repo "magit/magit"
|
||||||
@@ -214,7 +185,7 @@
|
|||||||
(unless (boundp 'overriding-text-conversion-style)
|
(unless (boundp 'overriding-text-conversion-style)
|
||||||
(defvar overriding-text-conversion-style nil))
|
(defvar overriding-text-conversion-style nil))
|
||||||
(use-package transient
|
(use-package transient
|
||||||
:ensure (:host github :repo "magit/transient" :branch "main" :wait t)
|
:ensure (:host github :repo "magit/transient" :wait t)
|
||||||
:demand t)
|
:demand t)
|
||||||
(elpaca-wait)
|
(elpaca-wait)
|
||||||
|
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
;;; dbus.el --- Disabled D-Bus shim -*- lexical-binding: t; -*-
|
|
||||||
|
|
||||||
;;; Commentary:
|
|
||||||
;; This file intentionally shadows Emacs' built-in net/dbus.el. Loading the
|
|
||||||
;; built-in library initializes D-Bus connections at top level, which is noisy
|
|
||||||
;; and fragile in this configuration.
|
|
||||||
|
|
||||||
;;; Code:
|
|
||||||
|
|
||||||
(setq features (delq 'dbusbind features))
|
|
||||||
(setq dbus-compiled-version nil
|
|
||||||
dbus-runtime-version nil)
|
|
||||||
|
|
||||||
(unless (boundp 'dbus-error)
|
|
||||||
(define-error 'dbus-error "D-Bus disabled in this Emacs config"))
|
|
||||||
|
|
||||||
(defvar dbus-debug nil)
|
|
||||||
(defvar dbus-event-error-functions nil)
|
|
||||||
(defvar dbus-registered-objects-table (make-hash-table :test #'equal))
|
|
||||||
|
|
||||||
(defconst dbus-service-dbus "org.freedesktop.DBus")
|
|
||||||
(defconst dbus-path-dbus "/org/freedesktop/DBus")
|
|
||||||
(defconst dbus-path-local "/org/freedesktop/DBus/Local")
|
|
||||||
(defconst dbus-interface-dbus "org.freedesktop.DBus")
|
|
||||||
(defconst dbus-interface-peer "org.freedesktop.DBus.Peer")
|
|
||||||
(defconst dbus-interface-introspectable "org.freedesktop.DBus.Introspectable")
|
|
||||||
(defconst dbus-interface-properties "org.freedesktop.DBus.Properties")
|
|
||||||
(defconst dbus-interface-objectmanager "org.freedesktop.DBus.ObjectManager")
|
|
||||||
(defconst dbus-interface-monitoring "org.freedesktop.DBus.Monitoring")
|
|
||||||
(defconst dbus-interface-local "org.freedesktop.DBus.Local")
|
|
||||||
(defconst dbus-service-emacs "org.gnu.Emacs")
|
|
||||||
(defconst dbus-path-emacs "/org/gnu/Emacs")
|
|
||||||
(defconst dbus-interface-emacs "org.gnu.Emacs")
|
|
||||||
|
|
||||||
(defconst dbus-error-dbus "org.freedesktop.DBus.Error")
|
|
||||||
(defconst dbus-error-failed "org.freedesktop.DBus.Error.Failed")
|
|
||||||
(defconst dbus-error-service-unknown "org.freedesktop.DBus.Error.ServiceUnknown")
|
|
||||||
|
|
||||||
(defmacro dbus-ignore-errors (&rest body)
|
|
||||||
"Execute BODY, suppressing `dbus-error' unless `dbus-debug' is non-nil."
|
|
||||||
(declare (indent 0) (debug t))
|
|
||||||
`(condition-case err
|
|
||||||
(progn ,@body)
|
|
||||||
(dbus-error (when dbus-debug (signal (car err) (cdr err))))))
|
|
||||||
|
|
||||||
(defun imalison:dbus-disabled (&rest _args)
|
|
||||||
"Signal that D-Bus is intentionally unavailable in this config."
|
|
||||||
(signal 'dbus-error '("D-Bus disabled in this Emacs config")))
|
|
||||||
|
|
||||||
(dolist (function
|
|
||||||
'(dbus-call-method
|
|
||||||
dbus-call-method-asynchronously
|
|
||||||
dbus-send-signal
|
|
||||||
dbus-method-return-internal
|
|
||||||
dbus-method-error-internal
|
|
||||||
dbus-register-service
|
|
||||||
dbus-unregister-service
|
|
||||||
dbus-register-signal
|
|
||||||
dbus-register-method
|
|
||||||
dbus-unregister-object
|
|
||||||
dbus-register-property
|
|
||||||
dbus-register-monitor
|
|
||||||
dbus-init-bus
|
|
||||||
dbus-ping
|
|
||||||
dbus-introspect
|
|
||||||
dbus-introspect-xml
|
|
||||||
dbus-get-property
|
|
||||||
dbus-set-property
|
|
||||||
dbus-get-all-properties
|
|
||||||
dbus-get-all-managed-objects))
|
|
||||||
(defalias function #'imalison:dbus-disabled))
|
|
||||||
|
|
||||||
(defun dbus-list-activatable-names (&optional _bus) nil)
|
|
||||||
(defun dbus-list-names (&optional _bus) nil)
|
|
||||||
(defun dbus-list-known-names (&optional _bus) nil)
|
|
||||||
(defun dbus-list-queued-owners (&rest _args) nil)
|
|
||||||
(defun dbus-get-name-owner (&rest _args) nil)
|
|
||||||
(defun dbus-list-hash-table () nil)
|
|
||||||
|
|
||||||
(defun dbus-event-bus-name (_event) nil)
|
|
||||||
(defun dbus-event-message-type (_event) nil)
|
|
||||||
(defun dbus-event-serial-number (_event) nil)
|
|
||||||
(defun dbus-event-service-name (_event) nil)
|
|
||||||
(defun dbus-event-destination-name (_event) nil)
|
|
||||||
(defun dbus-event-path-name (_event) nil)
|
|
||||||
(defun dbus-event-interface-name (_event) nil)
|
|
||||||
(defun dbus-event-member-name (_event) nil)
|
|
||||||
(defun dbus-event-handler (_event) nil)
|
|
||||||
(defun dbus-event-arguments (_event) nil)
|
|
||||||
|
|
||||||
(provide 'dbus)
|
|
||||||
|
|
||||||
;;; dbus.el ends here
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
;;; tramp-gvfs.el --- Disabled TRAMP GVFS backend -*- lexical-binding: t; -*-
|
|
||||||
|
|
||||||
;;; Commentary:
|
|
||||||
;; This file intentionally shadows Emacs' built-in net/tramp-gvfs.el. The real
|
|
||||||
;; backend depends on D-Bus and GVFS desktop services; this config uses ordinary
|
|
||||||
;; TRAMP methods instead.
|
|
||||||
|
|
||||||
;;; Code:
|
|
||||||
|
|
||||||
(require 'tramp)
|
|
||||||
|
|
||||||
(defconst tramp-gvfs-enabled nil
|
|
||||||
"Non-nil when the disabled GVFS backend is available.")
|
|
||||||
|
|
||||||
(defvar tramp-gvfs-methods nil
|
|
||||||
"Disabled list of TRAMP methods handled through GVFS.")
|
|
||||||
(setq tramp-gvfs-methods nil)
|
|
||||||
|
|
||||||
(defconst tramp-goa-methods nil
|
|
||||||
"Disabled list of GNOME Online Accounts TRAMP methods.")
|
|
||||||
|
|
||||||
(defvar tramp-media-methods nil
|
|
||||||
"Disabled list of media-device TRAMP methods.")
|
|
||||||
(setq tramp-media-methods nil)
|
|
||||||
|
|
||||||
(defvar tramp-gvfs-file-name-handler-alist nil
|
|
||||||
"Disabled GVFS file name handler alist.")
|
|
||||||
|
|
||||||
(defun tramp-gvfs-file-name-p (_filename)
|
|
||||||
"Return nil because the GVFS backend is disabled."
|
|
||||||
nil)
|
|
||||||
|
|
||||||
(defun tramp-gvfs-file-name-handler (operation &rest args)
|
|
||||||
"Signal an unsupported OPERATION for the disabled GVFS backend."
|
|
||||||
(signal 'file-error
|
|
||||||
(list "TRAMP GVFS backend disabled in this Emacs config"
|
|
||||||
operation args)))
|
|
||||||
|
|
||||||
(provide 'tramp-gvfs)
|
|
||||||
|
|
||||||
;;; tramp-gvfs.el ends here
|
|
||||||
@@ -979,8 +979,11 @@ alphanumeric characters only."
|
|||||||
:defer 2
|
:defer 2
|
||||||
:config
|
:config
|
||||||
(progn
|
(progn
|
||||||
|
(use-package org-projectile
|
||||||
|
:demand t
|
||||||
|
:config
|
||||||
(setq org-project-capture-default-backend
|
(setq org-project-capture-default-backend
|
||||||
(make-instance 'org-project-capture-project-backend))
|
(make-instance 'org-project-capture-projectile-backend)))
|
||||||
(setq org-project-capture-strategy
|
(setq org-project-capture-strategy
|
||||||
(make-instance 'org-project-capture-combine-strategies
|
(make-instance 'org-project-capture-combine-strategies
|
||||||
:strategies (list (make-instance 'org-project-capture-single-file-strategy)
|
:strategies (list (make-instance 'org-project-capture-single-file-strategy)
|
||||||
@@ -1114,11 +1117,7 @@ alphanumeric characters only."
|
|||||||
(org-wild-notifier--apply-whitelist)
|
(org-wild-notifier--apply-whitelist)
|
||||||
(org-wild-notifier--apply-blacklist)
|
(org-wild-notifier--apply-blacklist)
|
||||||
(-map 'org-wild-notifier--gather-info))))
|
(-map 'org-wild-notifier--gather-info))))
|
||||||
(condition-case err
|
|
||||||
(org-wild-notifier-mode +1)
|
(org-wild-notifier-mode +1)
|
||||||
(error
|
|
||||||
(message "org-wild-notifier disabled during startup: %s"
|
|
||||||
(error-message-string err))))
|
|
||||||
(defun org-wild-notify-check-at-time ()
|
(defun org-wild-notify-check-at-time ()
|
||||||
(interactive)
|
(interactive)
|
||||||
(imalison:org-at-time
|
(imalison:org-at-time
|
||||||
|
|||||||
@@ -102,10 +102,10 @@
|
|||||||
required = true
|
required = true
|
||||||
[credential "https://github.com"]
|
[credential "https://github.com"]
|
||||||
helper =
|
helper =
|
||||||
helper = !/usr/bin/env gh auth git-credential
|
helper = !/run/current-system/sw/bin/gh auth git-credential
|
||||||
[credential "https://gist.github.com"]
|
[credential "https://gist.github.com"]
|
||||||
helper =
|
helper =
|
||||||
helper = !/usr/bin/env gh auth git-credential
|
helper = !/run/current-system/sw/bin/gh auth git-credential
|
||||||
[includeIf "gitdir:~/Projects/org-agenda-api/"]
|
[includeIf "gitdir:~/Projects/org-agenda-api/"]
|
||||||
path = ~/.gitconfig.org-agenda-api
|
path = ~/.gitconfig.org-agenda-api
|
||||||
[includeIf "gitdir:~/Projects/dotfiles/org-agenda-api/"]
|
[includeIf "gitdir:~/Projects/dotfiles/org-agenda-api/"]
|
||||||
|
|||||||
@@ -10,17 +10,6 @@ end)
|
|||||||
local config = {
|
local config = {
|
||||||
gap = 8,
|
gap = 8,
|
||||||
autoColumns = false,
|
autoColumns = false,
|
||||||
widgets = {
|
|
||||||
disk = {
|
|
||||||
enabled = true,
|
|
||||||
interval = 60,
|
|
||||||
volume = "/",
|
|
||||||
},
|
|
||||||
memory = {
|
|
||||||
enabled = true,
|
|
||||||
interval = 10,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
local retileTimer = nil
|
local retileTimer = nil
|
||||||
@@ -230,96 +219,6 @@ local function moveFocusedToScreen(direction)
|
|||||||
tileWindows(columnWindows(target))
|
tileWindows(columnWindows(target))
|
||||||
end
|
end
|
||||||
|
|
||||||
local function userSpacesForScreen(screen)
|
|
||||||
local spaces, err = hs.spaces.spacesForScreen(screen)
|
|
||||||
if not spaces then
|
|
||||||
return nil, err
|
|
||||||
end
|
|
||||||
|
|
||||||
local userSpaces = {}
|
|
||||||
for _, space in ipairs(spaces) do
|
|
||||||
if hs.spaces.spaceType(space) == "user" then
|
|
||||||
table.insert(userSpaces, space)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return userSpaces
|
|
||||||
end
|
|
||||||
|
|
||||||
local function currentSpaceForScreen(screen)
|
|
||||||
local activeSpaces = hs.spaces.activeSpaces()
|
|
||||||
if activeSpaces and screen.getUUID then
|
|
||||||
local uuid = screen:getUUID()
|
|
||||||
if uuid and activeSpaces[uuid] then
|
|
||||||
return activeSpaces[uuid]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return hs.spaces.focusedSpace()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function nextUserSpaceForScreen(screen, currentSpace)
|
|
||||||
local spaces, err = userSpacesForScreen(screen)
|
|
||||||
if not spaces then
|
|
||||||
return nil, err
|
|
||||||
end
|
|
||||||
|
|
||||||
if #spaces < 2 then
|
|
||||||
return nil, "no other Desktop on this screen"
|
|
||||||
end
|
|
||||||
|
|
||||||
for index, space in ipairs(spaces) do
|
|
||||||
if space == currentSpace then
|
|
||||||
return spaces[(index % #spaces) + 1]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return spaces[1]
|
|
||||||
end
|
|
||||||
|
|
||||||
local function containsValue(values, target)
|
|
||||||
if not values then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, value in ipairs(values) do
|
|
||||||
if value == target then
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local function moveFocusedToNextDesktop()
|
|
||||||
local focused = hs.window.focusedWindow()
|
|
||||||
if not focused then
|
|
||||||
notify("No focused window")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local screen = focused:screen()
|
|
||||||
local targetSpace, err = nextUserSpaceForScreen(screen, currentSpaceForScreen(screen))
|
|
||||||
if not targetSpace then
|
|
||||||
notify("Desktop move failed: " .. tostring(err))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local ok, moveErr = hs.spaces.moveWindowToSpace(focused, targetSpace, true)
|
|
||||||
if not ok then
|
|
||||||
notify("Desktop move failed: " .. tostring(moveErr))
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
hs.timer.doAfter(0.2, function()
|
|
||||||
if containsValue(hs.spaces.windowSpaces(focused), targetSpace) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
notify("Desktop move blocked by macOS")
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function scheduleRetile()
|
local function scheduleRetile()
|
||||||
if arranging or not config.autoColumns then
|
if arranging or not config.autoColumns then
|
||||||
return
|
return
|
||||||
@@ -365,7 +264,6 @@ wf:subscribe({
|
|||||||
hs.hotkey.bind(hyper, "c", tileFocusedScreen)
|
hs.hotkey.bind(hyper, "c", tileFocusedScreen)
|
||||||
hs.hotkey.bind(hyper, "v", toggleAutoColumns)
|
hs.hotkey.bind(hyper, "v", toggleAutoColumns)
|
||||||
hs.hotkey.bind(hyper, "\\", toggleMonitorInput)
|
hs.hotkey.bind(hyper, "\\", toggleMonitorInput)
|
||||||
hs.hotkey.bind(hyper, "h", moveFocusedToNextDesktop)
|
|
||||||
|
|
||||||
hs.hotkey.bind(hyper, "a", function()
|
hs.hotkey.bind(hyper, "a", function()
|
||||||
focusWindow("left")
|
focusWindow("left")
|
||||||
@@ -473,7 +371,6 @@ end)
|
|||||||
bindRgui("c", tileFocusedScreen)
|
bindRgui("c", tileFocusedScreen)
|
||||||
bindRgui("v", toggleAutoColumns)
|
bindRgui("v", toggleAutoColumns)
|
||||||
bindRgui("\\", toggleMonitorInput)
|
bindRgui("\\", toggleMonitorInput)
|
||||||
bindRgui("h", moveFocusedToNextDesktop)
|
|
||||||
|
|
||||||
bindRgui("m", function()
|
bindRgui("m", function()
|
||||||
placeFocused(1, 1, 1)
|
placeFocused(1, 1, 1)
|
||||||
@@ -527,7 +424,7 @@ local rguiTap = hs.eventtap.new({
|
|||||||
elseif not rightCommandUsed then
|
elseif not rightCommandUsed then
|
||||||
hs.eventtap.keyStroke({}, "escape", 0)
|
hs.eventtap.keyStroke({}, "escape", 0)
|
||||||
end
|
end
|
||||||
return true
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
if eventType ~= hs.eventtap.event.types.keyDown or not rightCommandDown then
|
if eventType ~= hs.eventtap.event.types.keyDown or not rightCommandDown then
|
||||||
@@ -536,8 +433,7 @@ local rguiTap = hs.eventtap.new({
|
|||||||
|
|
||||||
local binding = rguiBindings[keyCode]
|
local binding = rguiBindings[keyCode]
|
||||||
if not binding then
|
if not binding then
|
||||||
rightCommandUsed = true
|
return false
|
||||||
return true
|
|
||||||
end
|
end
|
||||||
|
|
||||||
rightCommandUsed = true
|
rightCommandUsed = true
|
||||||
@@ -552,121 +448,4 @@ end)
|
|||||||
|
|
||||||
rguiTap:start()
|
rguiTap:start()
|
||||||
|
|
||||||
local menuWidgets = {}
|
|
||||||
local widgetTimers = {}
|
|
||||||
|
|
||||||
local function round(number)
|
|
||||||
return math.floor(number + 0.5)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function formatBytes(bytes)
|
|
||||||
local units = { "B", "K", "M", "G", "T" }
|
|
||||||
local value = bytes
|
|
||||||
local unitIndex = 1
|
|
||||||
|
|
||||||
while value >= 1024 and unitIndex < #units do
|
|
||||||
value = value / 1024
|
|
||||||
unitIndex = unitIndex + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if unitIndex <= 2 then
|
|
||||||
return string.format("%d%s", round(value), units[unitIndex])
|
|
||||||
end
|
|
||||||
|
|
||||||
return string.format("%.1f%s", value, units[unitIndex])
|
|
||||||
end
|
|
||||||
|
|
||||||
local function formatGb(bytes)
|
|
||||||
return string.format("%.1fGB", bytes / 1024 / 1024 / 1024)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function formatCompactGb(bytes, decimals)
|
|
||||||
return string.format("%." .. decimals .. "f", bytes / 1024 / 1024 / 1024)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function updateDiskWidget()
|
|
||||||
local widget = menuWidgets.disk
|
|
||||||
local widgetConfig = config.widgets.disk
|
|
||||||
if not widget then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local output, success = hs.execute(string.format(
|
|
||||||
"/bin/df -k %q | /usr/bin/awk 'NR==2 {print $2, $3, $4, $5}'",
|
|
||||||
widgetConfig.volume
|
|
||||||
))
|
|
||||||
local totalKb, usedKb, availableKb, capacity = output:match("(%d+)%s+(%d+)%s+(%d+)%s+(%d+%%)")
|
|
||||||
|
|
||||||
if not success or not totalKb then
|
|
||||||
widget:setTitle("Disk ?")
|
|
||||||
widget:setTooltip("Disk usage unavailable")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
widget:setTitle(string.format(
|
|
||||||
"D %s/%sGB",
|
|
||||||
formatCompactGb(tonumber(availableKb) * 1024, 0),
|
|
||||||
formatCompactGb(tonumber(totalKb) * 1024, 0)
|
|
||||||
))
|
|
||||||
widget:setTooltip(string.format(
|
|
||||||
"%s used, %s available on %s (%s full)",
|
|
||||||
formatBytes(tonumber(usedKb) * 1024),
|
|
||||||
formatBytes(tonumber(availableKb) * 1024),
|
|
||||||
widgetConfig.volume,
|
|
||||||
capacity
|
|
||||||
))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function updateMemoryWidget()
|
|
||||||
local widget = menuWidgets.memory
|
|
||||||
if not widget then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local stats = hs.host.vmStat()
|
|
||||||
local pageSize = stats.pageSize
|
|
||||||
local usedBytes = (
|
|
||||||
stats.anonymousPages
|
|
||||||
+ stats.pagesWiredDown
|
|
||||||
+ stats.pagesUsedByVMCompressor
|
|
||||||
) * pageSize
|
|
||||||
local totalBytes = stats.memSize
|
|
||||||
local availableBytes = totalBytes - usedBytes
|
|
||||||
local cacheBytes = stats.fileBackedPages * pageSize
|
|
||||||
local freeBytes = (stats.pagesFree + stats.pagesSpeculative) * pageSize
|
|
||||||
|
|
||||||
widget:setTitle(string.format(
|
|
||||||
"R %s/%sGB",
|
|
||||||
formatCompactGb(availableBytes, 1),
|
|
||||||
formatCompactGb(totalBytes, 1)
|
|
||||||
))
|
|
||||||
widget:setTooltip(string.format(
|
|
||||||
"%s used, %s available of %s\n%s cached, %s free",
|
|
||||||
formatBytes(usedBytes),
|
|
||||||
formatBytes(availableBytes),
|
|
||||||
formatBytes(totalBytes),
|
|
||||||
formatBytes(cacheBytes),
|
|
||||||
formatBytes(freeBytes)
|
|
||||||
))
|
|
||||||
end
|
|
||||||
|
|
||||||
local function createMenuWidget(name, update, interval)
|
|
||||||
menuWidgets[name] = hs.menubar.new()
|
|
||||||
menuWidgets[name]:setClickCallback(update)
|
|
||||||
update()
|
|
||||||
widgetTimers[name] = hs.timer.doEvery(interval, update)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function startMenuWidgets()
|
|
||||||
if config.widgets.disk.enabled then
|
|
||||||
createMenuWidget("disk", updateDiskWidget, config.widgets.disk.interval)
|
|
||||||
end
|
|
||||||
|
|
||||||
if config.widgets.memory.enabled then
|
|
||||||
createMenuWidget("memory", updateMemoryWidget, config.widgets.memory.interval)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
startMenuWidgets()
|
|
||||||
|
|
||||||
notify("Hammerspoon loaded")
|
notify("Hammerspoon loaded")
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
app_id=codex-desktop
|
|
||||||
state_dir="${XDG_STATE_HOME:-$HOME/.local/state}/$app_id"
|
|
||||||
runtime_dir="${XDG_RUNTIME_DIR:-$state_dir}/$app_id"
|
|
||||||
pid_file="$state_dir/app.pid"
|
|
||||||
socket_path="$runtime_dir/launch-action.sock"
|
|
||||||
|
|
||||||
pid_is_alive() {
|
|
||||||
local pid="${1:-}"
|
|
||||||
|
|
||||||
case "$pid" in
|
|
||||||
""|*[!0-9]*) return 1 ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
[ -e "/proc/$pid/exe" ]
|
|
||||||
}
|
|
||||||
|
|
||||||
running_app_is_alive() {
|
|
||||||
local pid
|
|
||||||
|
|
||||||
[ -r "$pid_file" ] || return 1
|
|
||||||
pid="$(cat "$pid_file" 2>/dev/null || true)"
|
|
||||||
pid_is_alive "$pid"
|
|
||||||
}
|
|
||||||
|
|
||||||
send_launch_action() {
|
|
||||||
[ -S "$socket_path" ] || return 1
|
|
||||||
running_app_is_alive || return 1
|
|
||||||
|
|
||||||
python3 - "$socket_path" "$@" <<'PY'
|
|
||||||
import json
|
|
||||||
import socket
|
|
||||||
import sys
|
|
||||||
|
|
||||||
socket_path = sys.argv[1]
|
|
||||||
argv = sys.argv[2:]
|
|
||||||
payload = json.dumps({"argv": argv}, separators=(",", ":")).encode("utf-8") + b"\n"
|
|
||||||
|
|
||||||
client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
||||||
client.settimeout(1.0)
|
|
||||||
try:
|
|
||||||
client.connect(socket_path)
|
|
||||||
client.sendall(payload)
|
|
||||||
response = client.recv(16)
|
|
||||||
finally:
|
|
||||||
client.close()
|
|
||||||
|
|
||||||
if not response.startswith(b"ok"):
|
|
||||||
raise SystemExit(1)
|
|
||||||
PY
|
|
||||||
}
|
|
||||||
|
|
||||||
if send_launch_action "$@"; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
exec codex-desktop "$@"
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
state_file="${IM_DESKTOP_SHELL_UI_STATE:-${XDG_STATE_HOME:-$HOME/.local/state}/imalison/desktop-shell-ui}"
|
|
||||||
default_shell_ui="${IM_HYPRLAND_SHELL_UI:-taffybar}"
|
|
||||||
|
|
||||||
normalize_shell_ui() {
|
|
||||||
case "${1:-}" in
|
|
||||||
taffybar|rofi)
|
|
||||||
printf '%s\n' "taffybar"
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
current_shell_ui() {
|
|
||||||
local configured=""
|
|
||||||
|
|
||||||
if [[ -r "$state_file" ]]; then
|
|
||||||
IFS= read -r configured < "$state_file" || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
normalize_shell_ui "$configured" 2>/dev/null \
|
|
||||||
|| normalize_shell_ui "$default_shell_ui" 2>/dev/null \
|
|
||||||
|| printf '%s\n' "taffybar"
|
|
||||||
}
|
|
||||||
|
|
||||||
write_shell_ui() {
|
|
||||||
local shell_ui="$1"
|
|
||||||
mkdir -p "$(dirname "$state_file")"
|
|
||||||
printf '%s\n' "$shell_ui" > "$state_file"
|
|
||||||
}
|
|
||||||
|
|
||||||
apply_shell_ui() {
|
|
||||||
local shell_ui="$1"
|
|
||||||
|
|
||||||
export IM_HYPRLAND_SHELL_UI="$shell_ui"
|
|
||||||
systemctl --user import-environment IM_HYPRLAND_SHELL_UI 2>/dev/null || true
|
|
||||||
|
|
||||||
case "$shell_ui" in
|
|
||||||
taffybar)
|
|
||||||
systemctl --user start taffybar.service
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
set_shell_ui() {
|
|
||||||
local shell_ui
|
|
||||||
shell_ui="$(normalize_shell_ui "${1:-}")" || {
|
|
||||||
echo "usage: desktop_shell_ui set taffybar" >&2
|
|
||||||
exit 2
|
|
||||||
}
|
|
||||||
|
|
||||||
write_shell_ui "$shell_ui"
|
|
||||||
apply_shell_ui "$shell_ui"
|
|
||||||
}
|
|
||||||
|
|
||||||
case "${1:-current}" in
|
|
||||||
current)
|
|
||||||
current_shell_ui
|
|
||||||
;;
|
|
||||||
set)
|
|
||||||
set_shell_ui "${2:-}"
|
|
||||||
;;
|
|
||||||
toggle)
|
|
||||||
set_shell_ui taffybar
|
|
||||||
;;
|
|
||||||
apply)
|
|
||||||
apply_shell_ui "$(current_shell_ui)"
|
|
||||||
;;
|
|
||||||
exec-condition)
|
|
||||||
[[ "$(current_shell_ui)" == "$(normalize_shell_ui "${2:-}")" ]]
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "usage: desktop_shell_ui {current|set|toggle|apply|exec-condition}" >&2
|
|
||||||
exit 2
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user