summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--emacs/init.el317
-rw-r--r--emacs/user-lisp/modeline.el140
-rw-r--r--niri/config.kdl266
-rwxr-xr-xnvim/init.lua17
-rwxr-xr-xnvim/lazy-lock.json42
-rwxr-xr-xnvim/lua/config.lua89
-rwxr-xr-xnvim/lua/plugins/catppuccin/init.lua90
-rw-r--r--nvim/lua/plugins/conform/init.lua17
-rwxr-xr-xnvim/lua/plugins/lazy/init.lua17
-rwxr-xr-xnvim/lua/plugins/lazy/plugins.lua144
-rwxr-xr-xnvim/lua/plugins/lsp/init.lua305
-rwxr-xr-xnvim/lua/plugins/lualine/init.lua50
-rwxr-xr-xnvim/lua/plugins/nvim-tree/init.lua2
-rwxr-xr-xnvim/lua/plugins/render-markdown/init.lua139
-rwxr-xr-xnvim/lua/plugins/telescope/init.lua3
-rwxr-xr-xnvim/lua/plugins/treesitter/init.lua26
-rwxr-xr-xnvim/lua/plugins/whichkey/init.lua2
-rwxr-xr-xnvim/lua/status_line/init.lua102
-rw-r--r--quickshell/LICENSE674
-rw-r--r--quickshell/config.js66
-rw-r--r--quickshell/modules/bar/Bar.qml171
-rw-r--r--quickshell/modules/bar/Music.qml237
-rw-r--r--quickshell/modules/bar/QuickControl.qml246
-rw-r--r--quickshell/modules/bar/Time.qml144
-rw-r--r--quickshell/modules/bar/Workspaces.qml177
-rw-r--r--quickshell/modules/desktop/Wallpaper.qml180
-rw-r--r--quickshell/modules/notifications/NotificationCard.qml257
-rw-r--r--quickshell/modules/notifications/NotificationFocus.qml239
-rw-r--r--quickshell/modules/notifications/Notifications.qml125
-rw-r--r--quickshell/shell.qml63
-rw-r--r--zathura/zathurarc4
32 files changed, 4352 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0d09d41
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+nvim/undo/
diff --git a/emacs/init.el b/emacs/init.el
new file mode 100644
index 0000000..9e26acd
--- /dev/null
+++ b/emacs/init.el
@@ -0,0 +1,317 @@
+;; -*- lexical-binding: t; -*-
+
+(require 'package)
+(setq package-check-signature nil
+ package-archives '(("gnu" . "https://elpa.gnu.org/packages/")
+ ("nongnu" . "https://elpa.nongnu.org/packages/")
+ ("melpa" . "https://melpa.org/packages/")))
+(package-initialize)
+(unless package-archive-contents
+ (package-refresh-contents))
+
+(require 'use-package)
+(require 'seq)
+(require 'treesit)
+
+(setq use-package-always-ensure t
+ inhibit-startup-screen t
+ initial-scratch-message nil
+ initial-buffer-choice nil
+ ring-bell-function #'ignore
+ use-short-answers t
+ create-lockfiles nil
+ make-backup-files nil
+ auto-save-default nil
+ select-enable-clipboard t
+ select-enable-primary nil
+ save-interprogram-paste-before-kill nil
+ confirm-nonexistent-file-or-buffer nil
+ switch-to-buffer-obey-display-actions t
+ custom-file (expand-file-name "custom.el" user-emacs-directory)
+ display-line-numbers-type 'relative
+ mouse-wheel-scroll-amount '(3 ((shift) . 1))
+ mouse-wheel-progressive-speed nil
+ mouse-wheel-scroll-amount-horizontal '(3 . 1)
+ pixel-scroll-precision-mode nil
+ split-height-threshold nil
+ split-width-threshold nil)
+
+(setq initial-major-mode 'text-mode)
+(setq initial-buffer-choice t)
+
+(setq-default indent-tabs-mode nil
+ tab-width 4)
+
+(menu-bar-mode -1)
+
+(when (file-exists-p custom-file)
+ (load custom-file))
+
+(global-display-line-numbers-mode 1)
+(column-number-mode 1)
+(electric-pair-mode 1)
+(show-paren-mode 1)
+(save-place-mode 1)
+(recentf-mode 1)
+(savehist-mode 1)
+(xterm-mouse-mode 1)
+
+(use-package vertico
+ :init
+ (vertico-mode 1))
+
+(use-package marginalia
+ :init
+ (marginalia-mode 1))
+
+(use-package which-key
+ :init
+ (which-key-mode 1))
+
+(use-package orderless
+ :custom
+ (completion-styles '(orderless basic)))
+
+(use-package consult
+ :bind (("C-x b" . consult-buffer)
+ ("M-s r" . consult-ripgrep)
+ ("M-s l" . consult-line)))
+
+(use-package corfu
+ :custom
+ (corfu-auto t)
+ (corfu-auto-prefix 2)
+ :init
+ (global-corfu-mode 1))
+
+(use-package evil
+ :init
+ (setq evil-want-keybinding nil
+ evil-want-C-u-scroll t
+ evil-want-Y-yank-to-eol t
+ evil-undo-system 'undo-redo
+ evil-shift-width 4
+ evil-visual-x-select-clipboard nil)
+ :config
+ (evil-mode 1)
+ (setq evil-echo-state nil))
+
+(use-package evil-collection
+ :after evil
+ :config
+ (evil-collection-init))
+
+(use-package evil-surround
+ :after evil
+ :config
+ (global-evil-surround-mode 1))
+
+(use-package evil-commentary
+ :after evil
+ :config
+ (evil-commentary-mode 1))
+
+(make-directory "~/notes/" t)
+
+(use-package vterm)
+
+(use-package elcord
+ :config
+ (elcord-mode 1))
+
+(use-package org
+ :ensure nil
+ :custom
+ (org-directory "~/notes/")
+ (org-agenda-files '("~/notes/"))
+ (org-default-notes-file "~/notes/inbox.org")
+ (org-startup-indented t)
+ (org-log-done 'time)
+ (org-todo-keywords
+ '((sequence "TODO(t)" "NEXT(n)" "WAIT(w)" "|" "DONE(d)" "CANC(c)")))
+ (org-capture-templates
+ '(("t" "Todo" entry
+ (file+headline "~/notes/inbox.org" "Tasks")
+ "* TODO %?\n %U\n")
+ ("i" "Idea" entry
+ (file+headline "~/notes/inbox.org" "Ideas")
+ "* %?\n %U\n")))
+ :config
+ (require 'org-tempo)
+ (add-hook 'org-mode-hook #'visual-line-mode))
+
+(use-package evil-org
+ :after (evil org)
+ :hook (org-mode . evil-org-mode)
+ :config
+ (require 'evil-org-agenda)
+ (evil-org-agenda-set-keys))
+
+(use-package denote
+ :custom
+ (denote-directory "~/notes/")
+ (denote-file-type 'org))
+
+(use-package consult-denote
+ :after (consult denote)
+ :config
+ (consult-denote-mode 1))
+
+(use-package eglot
+ :ensure nil
+ :hook ((c-mode . eglot-ensure)
+ (c++-mode . eglot-ensure)
+ (rust-mode . eglot-ensure)
+ (go-mode . eglot-ensure)
+ (zig-mode . eglot-ensure))
+ :config
+ (add-to-list
+ 'eglot-server-programs
+ '((c-mode c++-mode) . ("clangd" "--background-index" "--header-insertion=never")))
+ (add-to-list 'eglot-server-programs '(zig-mode . ("zls"))))
+
+(use-package flymake
+ :ensure nil
+ :hook (prog-mode . flymake-mode))
+
+(use-package rust-mode
+ :mode "\\.rs\\'")
+
+(use-package go-mode
+ :mode "\\.go\\'")
+
+(use-package zig-mode
+ :mode "\\.zig\\'")
+
+(require 'warnings)
+(add-to-list 'warning-suppress-types '(bytecomp))
+
+(defvar coast-treesit-language-sources
+ '((bash . ("https://github.com/tree-sitter/tree-sitter-bash" "src"))
+ (c . ("https://github.com/tree-sitter/tree-sitter-c" "src"))
+ (cpp . ("https://github.com/tree-sitter/tree-sitter-cpp" "src"))
+ (css . ("https://github.com/tree-sitter/tree-sitter-css" "src"))
+ (go . ("https://github.com/tree-sitter/tree-sitter-go" "src"))
+ (haskell . ("https://github.com/tree-sitter/tree-sitter-haskell" "src"))
+ (html . ("https://github.com/tree-sitter/tree-sitter-html" "src"))
+ (javascript . ("https://github.com/tree-sitter/tree-sitter-javascript" "src"))
+ (json . ("https://github.com/tree-sitter/tree-sitter-json" "src"))
+ (lua . ("https://github.com/tree-sitter-grammars/tree-sitter-lua" "src"))
+ (make . ("https://github.com/tree-sitter-grammars/tree-sitter-make" "src"))
+ (markdown . ("https://github.com/tree-sitter/tree-sitter-markdown" "src"))
+ (python . ("https://github.com/tree-sitter/tree-sitter-python" "src"))
+ (rust . ("https://github.com/tree-sitter/tree-sitter-rust" "src"))
+ (toml . ("https://github.com/tree-sitter/tree-sitter-toml" "src"))
+ (tsx . ("https://github.com/tree-sitter/tree-sitter-typescript" "tsx/src"))
+ (typescript . ("https://github.com/tree-sitter/tree-sitter-typescript" "typescript/src"))
+ (yaml . ("https://github.com/tree-sitter-grammars/tree-sitter-yaml" "src"))))
+
+(defun coast-treesit-install (language)
+ (let* ((source (alist-get language coast-treesit-language-sources))
+ (url (car source))
+ (subdir (cadr source)))
+ (when source
+ (add-to-list
+ 'treesit-language-source-alist
+ (if subdir
+ (list language url nil subdir)
+ (list language url)))
+ (treesit-install-language-grammar language))))
+
+(defun my/make-terminal-transparent (&optional frame)
+ (unless (display-graphic-p frame)
+ (let ((target-frame (or frame (selected-frame)))
+ (faces '(default line-number line-number-current-line fringe
+ window-divider vertical-border hl-line)))
+ (dolist (face faces)
+ (when (facep face)
+ (set-face-background face "unspecified-bg" target-frame)))
+ (set-terminal-parameter (frame-terminal target-frame)
+ 'background-mode 'dark))))
+
+(add-hook 'window-setup-hook #'my/make-terminal-transparent)
+(add-hook 'after-make-frame-functions #'my/make-terminal-transparent)
+(advice-add 'load-theme :after
+ (lambda (&rest _) (my/make-terminal-transparent)))
+
+(use-package doom-themes)
+
+(defun coast/remove-scratch-buffer ()
+ (let ((buffer (get-buffer "*scratch*")))
+ (when (and buffer
+ (not (buffer-modified-p buffer))
+ (not (get-buffer-window buffer t)))
+ (kill-buffer buffer))))
+
+(add-hook 'after-init-hook #'coast/remove-scratch-buffer)
+
+(defun coast-treesit-install-missing ()
+ (dolist (language (mapcar #'car coast-treesit-language-sources))
+ (unless (treesit-language-available-p language)
+ (condition-case nil
+ (coast-treesit-install language)
+ (error nil)))))
+
+(add-hook 'after-init-hook #'coast-treesit-install-missing)
+
+(add-hook 'emacs-startup-hook
+ (lambda ()
+ (delete-other-windows)))
+
+(add-to-list 'load-path "/home/coast/.emacs.d/user-lisp")
+(require 'modeline)
+
+(use-package treemacs
+ :ensure t
+ :defer t
+ :config
+ (setq treemacs-collapse-dirs (if treemacs-python-executable 3 0)
+ treemacs-deferred-git-apply-delay 0.5
+ treemacs-directory-name-transformer #'identity
+ treemacs-display-in-side-window t
+ treemacs-eldoc-display 'simple
+ treemacs-file-event-delay 5000
+ treemacs-file-follow-delay 0.2
+ treemacs-follow-after-init t
+ treemacs-expand-after-init t
+ treemacs-indentation 2
+ treemacs-indentation-string " "
+ treemacs-is-other-window nil
+ treemacs-max-git-entries 5000
+ treemacs-no-png-images nil
+ treemacs-persist-file (expand-file-name ".cache/treemacs-persist" user-emacs-directory)
+ treemacs-recenter-distance 0.1
+ treemacs-file-watch-mode t
+ treemacs-fringe-indicator-mode 'always
+ treemacs-hide-gitignored-files-mode nil
+ treemacs-width 30)
+
+ (treemacs-resize-icons 11)
+ (treemacs-follow-mode t)
+ (treemacs-project-follow-mode t)
+ (treemacs-filewatch-mode t)
+ (treemacs-hide-gitignored-files-mode -1))
+
+(use-package treemacs-evil
+ :after (treemacs evil)
+ :ensure t)
+
+(use-package treemacs-icons-dired
+ :hook (dired-mode . treemacs-icons-dired-mode)
+ :ensure t)
+
+(use-package treemacs-magit
+ :after (treemacs magit)
+ :ensure t)
+
+(defun my/treemacs-open-current-dir ()
+ "Toggle Treemacs open to current directory/project."
+ (interactive)
+ (require 'treemacs)
+ (let ((state (treemacs-current-visibility)))
+ (if (eq state 'visible)
+ (treemacs)
+ (treemacs-add-and-display-current-project-exclusively))))
+
+(with-eval-after-load 'evil
+ (evil-define-key 'normal 'global (kbd "SPC e") #'my/treemacs-open-current-dir))
diff --git a/emacs/user-lisp/modeline.el b/emacs/user-lisp/modeline.el
new file mode 100644
index 0000000..627584c
--- /dev/null
+++ b/emacs/user-lisp/modeline.el
@@ -0,0 +1,140 @@
+;; -*- lexical-binding: t; -*-
+
+(require 'cl-lib)
+(require 'subr-x)
+
+(defvar my/ml-icons t
+ "Non-nil uses Nerd Font glyphs, nil falls back to ASCII.")
+
+;; Transparent background, white text
+(set-face-attribute 'mode-line nil
+ :background "unspecified-bg" :foreground "white"
+ :box nil :inverse-video nil :underline nil :overline nil)
+(set-face-attribute 'mode-line-inactive nil
+ :background "unspecified-bg" :foreground "brightblack"
+ :box nil :inverse-video nil :underline nil :overline nil)
+
+;;;; git diff counts (+added ~modified -removed), refreshed on open and save
+(defvar-local my/ml-diff-stat nil)
+
+(defun my/ml-diff-update ()
+ (setq my/ml-diff-stat nil)
+ (when (and buffer-file-name
+ (not (file-remote-p buffer-file-name))
+ (eq (vc-backend buffer-file-name) 'Git))
+ (let ((file buffer-file-name)
+ (default-directory (file-name-directory buffer-file-name))
+ (a 0) (m 0) (r 0))
+ (with-temp-buffer
+ (when (eq 0 (process-file "git" nil t nil "diff" "-U0" "--no-color"
+ "--no-ext-diff" "--" file))
+ (goto-char (point-min))
+ (while (re-search-forward
+ "^@@ -[0-9]+\\(?:,\\([0-9]+\\)\\)? \\+[0-9]+\\(?:,\\([0-9]+\\)\\)? @@"
+ nil t)
+ (let ((o (if (match-string 1) (string-to-number (match-string 1)) 1))
+ (n (if (match-string 2) (string-to-number (match-string 2)) 1)))
+ (cond ((= o 0) (cl-incf a n))
+ ((= n 0) (cl-incf r o))
+ (t (cl-incf m (min o n))
+ (cl-incf a (max 0 (- n o)))
+ (cl-incf r (max 0 (- o n)))))))))
+ (setq my/ml-diff-stat (list a m r)))))
+
+(add-hook 'find-file-hook #'my/ml-diff-update)
+(add-hook 'after-save-hook #'my/ml-diff-update)
+(add-hook 'after-revert-hook #'my/ml-diff-update)
+
+;;;; components
+(defun my/ml-state ()
+ (if (bound-and-true-p evil-local-mode) evil-state 'emacs))
+
+(defun my/ml-block (text)
+ (propertize text 'face '(:foreground "black" :background "white" :weight bold)))
+
+(defun my/ml-mode ()
+ (my/ml-block
+ (format " %s "
+ (pcase (my/ml-state)
+ ('normal "NORMAL")
+ ('insert "INSERT")
+ ('visual (pcase (bound-and-true-p evil-visual-selection)
+ ('line "V-LINE") ('block "V-BLOCK") (_ "VISUAL")))
+ ('replace "REPLACE")
+ ('operator "O-PENDING")
+ ('motion "MOTION")
+ (_ "EMACS")))))
+
+(defun my/ml-branch ()
+ (when (and vc-mode (stringp vc-mode))
+ (concat (if my/ml-icons "\ue0a0 " "")
+ (replace-regexp-in-string "\\`[ ]*[A-Za-z]+[-:@!?]" "" vc-mode))))
+
+(defun my/ml-diff ()
+ (when my/ml-diff-stat
+ (pcase-let ((`(,a ,m ,r) my/ml-diff-stat))
+ (let ((parts (delq nil (list (and (> a 0) (format "+%d" a))
+ (and (> m 0) (format "~%d" m))
+ (and (> r 0) (format "-%d" r))))))
+ (when parts (string-join parts " "))))))
+
+(defun my/ml-diag ()
+ (when (bound-and-true-p flymake-mode)
+ (let ((e 0) (w 0) (i 0))
+ (dolist (d (flymake-diagnostics))
+ (let ((ty (flymake-diagnostic-type d)))
+ (cond ((memq ty '(:error eglot-error)) (cl-incf e))
+ ((memq ty '(:warning eglot-warning)) (cl-incf w))
+ ((memq ty '(:note eglot-note)) (cl-incf i)))))
+ (let ((parts (delq nil
+ (list (and (> e 0) (format "%s%d" (if my/ml-icons "\U000F015A " "E:") e))
+ (and (> w 0) (format "%s%d" (if my/ml-icons "\U000F002A " "W:") w))
+ (and (> i 0) (format "%s%d" (if my/ml-icons "\U000F02FD " "I:") i))))))
+ (when parts (string-join parts " "))))))
+
+(defun my/ml-file ()
+ (concat (buffer-name)
+ (cond (buffer-read-only " [-]")
+ ((buffer-modified-p) " [+]")
+ (t ""))))
+
+(defun my/ml-encoding ()
+ (replace-regexp-in-string "-\\(unix\\|dos\\|mac\\)\\'" ""
+ (symbol-name (or buffer-file-coding-system 'utf-8))))
+
+(defun my/ml-eol ()
+ (pcase (coding-system-eol-type (or buffer-file-coding-system 'utf-8-unix))
+ (1 (if my/ml-icons "\ue70f" "dos"))
+ (2 (if my/ml-icons "\uf179" "mac"))
+ (_ (if my/ml-icons "\uf17c" "unix"))))
+
+(defun my/ml-filetype ()
+ (string-remove-suffix "-mode" (symbol-name major-mode)))
+
+(defun my/ml-loc (block)
+ (let ((s (format " %s:%-2d " (format-mode-line "%3l") (1+ (current-column)))))
+ (if block (my/ml-block s) s)))
+
+(defun my/ml-section (items)
+ (let ((items (cl-remove-if (lambda (s) (or (null s) (string-empty-p s))) items)))
+ (if items (concat " " (string-join items " | ") " ") "")))
+
+;;;; assembly
+(defun my/ml-format ()
+ (let* ((active (mode-line-window-selected-p))
+ (left (if active
+ (concat (my/ml-mode)
+ (my/ml-section (list (my/ml-branch) (my/ml-diff) (my/ml-diag)))
+ (my/ml-section (list (my/ml-file))))
+ (my/ml-section (list (my/ml-file)))))
+ (right (if active
+ (concat (my/ml-section (list (my/ml-encoding) (my/ml-eol) (my/ml-filetype)))
+ (my/ml-loc t))
+ (my/ml-loc nil)))
+ (fill (propertize " " 'display
+ `((space :align-to (- right ,(string-width right)))))))
+ (concat left fill right)))
+
+(setq-default mode-line-format '((:eval (my/ml-format))))
+
+(provide 'modeline)
diff --git a/niri/config.kdl b/niri/config.kdl
new file mode 100644
index 0000000..4c19f11
--- /dev/null
+++ b/niri/config.kdl
@@ -0,0 +1,266 @@
+spawn-at-startup "quickshell"
+spawn-at-startup "dbus-update-activation-environment" "--systemd" "WAYLAND_DISPLAY" "XDG_CURRENT_DESKTOP"
+
+input {
+ mod-key "Super"
+ keyboard {
+ xkb {
+ options "altwin:swap_alt_win"
+ }
+ numlock
+ repeat-delay 600
+ repeat-rate 25
+ }
+
+ touchpad {
+ tap
+ natural-scroll
+ }
+
+ trackpoint {
+ }
+}
+
+gestures {
+ hot-corners {
+ off
+ }
+}
+
+cursor {
+ xcursor-theme "Adwaita"
+ xcursor-size 24
+}
+
+output "HDMI-A-1" {
+ mode "1920x1080@100"
+ scale 1
+ transform "normal"
+}
+
+output "DP-1" {
+ mode "1920x1080@74.973"
+ scale 1
+ transform "normal"
+}
+
+output "eDP-1" {
+ off
+ scale 1
+ transform "normal"
+ position x=1280 y=0
+}
+
+layout {
+ gaps 10
+ center-focused-column "never"
+
+ preset-column-widths {
+ proportion 0.33333
+ proportion 0.5
+ proportion 0.66667
+ }
+
+ default-column-width {
+ proportion 0.5;
+ }
+
+ focus-ring {
+ width 2.5
+ active-color "#0E0F12"
+ inactive-color "#191724"
+ }
+
+ shadow {
+ on
+ softness 30
+ spread 5
+ offset x=0 y=5
+ color "#0007"
+ }
+
+ struts {
+ }
+}
+
+prefer-no-csd
+
+screenshot-path "~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png"
+
+window-rule {
+ match title="Waydroid"
+ open-fullscreen true
+}
+window-rule {
+ match app-id=r#"firefox$"# title="^Picture-in-Picture$"
+ open-floating true
+}
+
+window-rule {
+ match app-id=r#"waydroid$"#
+ open-fullscreen true
+}
+
+window-rule {
+ geometry-corner-radius 7
+ clip-to-geometry true
+}
+
+binds {
+ Mod+Shift+Slash { show-hotkey-overlay; }
+ Mod+Return hotkey-overlay-title="Open a Terminal: wezterm" { spawn "wezterm"; }
+ Mod+R hotkey-overlay-title="Run an Application: rofi" {
+ spawn-sh "rofi -show drun -config ~/.config/rofi/config.rasi"
+ }
+ Mod+Shift+Return hotkey-overlay-title="Run an application: rofi (run)" { spawn-sh "rofi -show run -config ~/.config/rofi/config.rasi"; }
+ Super+Alt+L hotkey-overlay-title="Lock the screen" { spawn "hyprlock"; }
+ Super+Alt+S allow-when-locked=true hotkey-overlay-title=null { spawn-sh "pkill orca || exec orca"; }
+
+ XF86AudioRaiseVolume allow-when-locked=true { spawn-sh "wpctl set-volume @DEFAULT_AUDIO_SINK@ 0.1+"; }
+ XF86AudioLowerVolume allow-when-locked=true { spawn-sh "wpctl set-volume @DEFAULT_AUDIO_SINK@ 0.1-"; }
+ XF86AudioMute allow-when-locked=true { spawn-sh "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle"; }
+ XF86AudioMicMute allow-when-locked=true { spawn-sh "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle"; }
+
+ XF86MonBrightnessUp allow-when-locked=true { spawn "brightnessctl" "--class=backlight" "set" "+10%"; }
+ XF86MonBrightnessDown allow-when-locked=true { spawn "brightnessctl" "--class=backlight" "set" "10%-"; }
+
+ Mod+O repeat=false { toggle-overview; }
+ Mod+S repeat=false { close-window; }
+
+ Mod+Left { focus-column-left; }
+ Mod+Down { focus-window-down; }
+ Mod+Up { focus-window-up; }
+ Mod+Right { focus-column-right; }
+ Mod+H { focus-column-left; }
+ Mod+J { focus-window-down; }
+ Mod+K { focus-window-up; }
+ Mod+L { focus-column-right; }
+ Mod+T {
+ spawn "sh" "-c" "pkill trayer 2>/dev/null; trayer --edge top --align right --widthtype pixel --width 350 --height 32 --padding 6 --transparent true --alpha 0 --tint 0x1e1e1e & pid=$!; sleep 15; kill $pid 2>/dev/null";
+ }
+
+ Mod+Ctrl+Left { move-column-left; }
+ Mod+Ctrl+Down { move-window-down; }
+ Mod+Ctrl+Up { move-window-up; }
+ Mod+Ctrl+Right { move-column-right; }
+ Mod+Shift+H { move-column-left; }
+ Mod+Shift+J { move-window-down; }
+ Mod+Shift+K { move-window-up; }
+ Mod+Shift+L { move-column-right; }
+
+ Mod+Home { focus-column-first; }
+ Mod+End { focus-column-last; }
+ Mod+Ctrl+Home { move-column-to-first; }
+ Mod+Ctrl+End { move-column-to-last; }
+
+ Mod+Shift+Left { focus-monitor-left; }
+ Mod+Shift+Down { focus-monitor-down; }
+ Mod+Shift+Up { focus-monitor-up; }
+ Mod+Shift+Right { focus-monitor-right; }
+ Mod+Ctrl+H { focus-monitor-left; }
+ Mod+Ctrl+J { focus-monitor-down; }
+ Mod+Ctrl+K { focus-monitor-up; }
+ Mod+Ctrl+L { focus-monitor-right; }
+
+ Mod+Shift+Ctrl+Left { move-column-to-monitor-left; }
+ Mod+Shift+Ctrl+Down { move-column-to-monitor-down; }
+ Mod+Shift+Ctrl+Up { move-column-to-monitor-up; }
+ Mod+Shift+Ctrl+Right { move-column-to-monitor-right; }
+ Mod+Shift+Ctrl+H { move-column-to-monitor-left; }
+ Mod+Shift+Ctrl+J { move-column-to-monitor-down; }
+ Mod+Shift+Ctrl+K { move-column-to-monitor-up; }
+ Mod+Shift+Ctrl+L { move-column-to-monitor-right; }
+
+ Mod+Page_Down { focus-workspace-down; }
+ Mod+Page_Up { focus-workspace-up; }
+ Mod+N { focus-workspace-down; }
+ Mod+M { focus-workspace-up; }
+ Mod+Ctrl+Page_Down { move-column-to-workspace-down; }
+ Mod+Ctrl+Page_Up { move-column-to-workspace-up; }
+ Mod+Ctrl+M { move-column-to-workspace-down; }
+ Mod+Ctrl+N { move-column-to-workspace-up; }
+
+ Mod+Shift+P hotkey-overlay-title="Restart waybar and eww" { spawn-sh "pkill waybar && waybar & disown && eww kill && eww daemon && eww open-many year month day daytype"; }
+
+ Mod+Shift+Page_Down { move-workspace-down; }
+ Mod+Shift+Page_Up { move-workspace-up; }
+ Mod+Shift+M { move-workspace-down; }
+ Mod+Shift+N { move-workspace-up; }
+
+ Mod+1 { focus-workspace 1; }
+ Mod+2 { focus-workspace 2; }
+ Mod+3 { focus-workspace 3; }
+ Mod+4 { focus-workspace 4; }
+ Mod+5 { focus-workspace 5; }
+ Mod+6 { focus-workspace 6; }
+ Mod+7 { focus-workspace 7; }
+ Mod+8 { focus-workspace 8; }
+ Mod+9 { focus-workspace 9; }
+ Mod+Shift+1 { move-column-to-workspace 1; }
+ Mod+Shift+2 { move-column-to-workspace 2; }
+ Mod+Shift+3 { move-column-to-workspace 3; }
+ Mod+Shift+4 { move-column-to-workspace 4; }
+ Mod+Shift+5 { move-column-to-workspace 5; }
+ Mod+Shift+6 { move-column-to-workspace 6; }
+ Mod+Shift+7 { move-column-to-workspace 7; }
+ Mod+Shift+8 { move-column-to-workspace 8; }
+ Mod+Shift+9 { move-column-to-workspace 9; }
+ Mod+Shift+F8 { spawn-sh "obs-cmd -w \"obsws://127.0.0.1:4455\" replay save"; }
+
+ Mod+BracketLeft { consume-or-expel-window-left; }
+ Mod+BracketRight { consume-or-expel-window-right; }
+
+ Mod+Comma { consume-window-into-column; }
+ Mod+Period { expel-window-from-column; }
+
+ Mod+D { switch-preset-column-width; }
+ Mod+Shift+R { switch-preset-window-height; }
+ Mod+Ctrl+R { reset-window-height; }
+ Mod+W { maximize-column; }
+ Mod+Shift+W { fullscreen-window; }
+
+ Mod+Ctrl+F { expand-column-to-available-width; }
+ Mod+C { center-column; }
+ Mod+Ctrl+C { center-visible-columns; }
+
+ Mod+Minus { set-column-width "-10%"; }
+ Mod+Equal { set-column-width "+10%"; }
+
+ Mod+Shift+Minus { set-window-height "-10%"; }
+ Mod+Shift+Equal { set-window-height "+10%"; }
+
+ Mod+Space { toggle-window-floating; }
+ Mod+Shift+V { switch-focus-between-floating-and-tiling; }
+
+ Mod+Ctrl+P { spawn-sh "qs ipc call shell restartBar; qs ipc call shell restartWall"; }
+
+ Mod+Shift+F { toggle-column-tabbed-display; }
+ Mod+F hotkey-overlay-title="Spawn file manager (nautilus)" { spawn-sh "nautilus"; }
+ Print { screenshot; }
+ Super+Shift+S { screenshot; }
+ Control+Alt+S { screenshot-window; }
+ Ctrl+Print { screenshot-screen; }
+ Alt+Print { screenshot-window; }
+
+ Mod+Escape allow-inhibiting=false { toggle-keyboard-shortcuts-inhibit; }
+
+ Mod+Shift+E { quit; }
+ Ctrl+Alt+Delete { quit; }
+
+ Mod+WheelScrollDown cooldown-ms=150 { focus-workspace-down; }
+ Mod+WheelScrollUp cooldown-ms=150 { focus-workspace-up; }
+ Mod+Ctrl+WheelScrollDown cooldown-ms=150 { move-column-to-workspace-down; }
+ Mod+Ctrl+WheelScrollUp cooldown-ms=150 { move-column-to-workspace-up; }
+
+ Mod+WheelScrollRight { focus-column-right; }
+ Mod+WheelScrollLeft { focus-column-left; }
+ Mod+Ctrl+WheelScrollRight { move-column-right; }
+ Mod+Ctrl+WheelScrollLeft { move-column-left; }
+
+ Mod+Shift+WheelScrollDown { focus-column-right; }
+ Mod+Shift+WheelScrollUp { focus-column-left; }
+ Mod+Ctrl+Shift+WheelScrollDown { move-column-right; }
+ Mod+Ctrl+Shift+WheelScrollUp { move-column-left; }
+}
+
+
diff --git a/nvim/init.lua b/nvim/init.lua
new file mode 100755
index 0000000..55d1321
--- /dev/null
+++ b/nvim/init.lua
@@ -0,0 +1,17 @@
+require("plugins/lazy")
+require("plugins/lsp")
+--require("plugins/nvim-tree")
+require("plugins/telescope")
+require("plugins/treesitter")
+require("config")
+require("plugins/render-markdown")
+--require("status_line").setup()
+require("plugins/lualine")
+require("plugins/catppuccin")
+require("plugins/conform")
+
+vim.filetype.add({
+ filename = {
+ ["cgitrc"] = "dosini",
+ },
+})
diff --git a/nvim/lazy-lock.json b/nvim/lazy-lock.json
new file mode 100755
index 0000000..9f4531f
--- /dev/null
+++ b/nvim/lazy-lock.json
@@ -0,0 +1,42 @@
+{
+ "cmp-buffer": { "branch": "main", "commit": "b74fab3656eea9de20a9b8116afa3cfc4ec09657" },
+ "cmp-cmdline": { "branch": "main", "commit": "d126061b624e0af6c3a556428712dd4d4194ec6d" },
+ "cmp-nvim-lsp": { "branch": "main", "commit": "cbc7b02bb99fae35cb42f514762b89b5126651ef" },
+ "cmp-nvim-lua": { "branch": "main", "commit": "e3a22cb071eb9d6508a156306b102c45cd2d573d" },
+ "cmp-path": { "branch": "main", "commit": "c642487086dbd9a93160e1679a1327be111cbc25" },
+ "cmp-spell": { "branch": "master", "commit": "694a4e50809d6d645c1ea29015dad0c293f019d6" },
+ "cmp-vsnip": { "branch": "main", "commit": "989a8a73c44e926199bfd05fa7a516d51f2d2752" },
+ "comfy-line-numbers.nvim": { "branch": "main", "commit": "9adedbaf1f410cdaacf4fb2ca8eb44f3d5f6d010" },
+ "conform.nvim": { "branch": "master", "commit": "016802de402556da54c36bd7359b441266b01cdd" },
+ "fterm.nvim": { "branch": "master", "commit": "d1320892cc2ebab472935242d9d992a2c9570180" },
+ "gentoo-syntax": { "branch": "master", "commit": "1b3a81ec62e5c843245dc431be0fe072add7eca8" },
+ "haskell-vim": { "branch": "master", "commit": "f35d02204b4813d1dbe8b0e98cc39701a4b8e15e" },
+ "lazy.nvim": { "branch": "main", "commit": "85c7ff3711b730b4030d03144f6db6375044ae82" },
+ "lsp_signature.nvim": { "branch": "master", "commit": "b7ace9ddb1640ce266012a45a672dfdaedfa5ec6" },
+ "lualine.nvim": { "branch": "master", "commit": "221ce6b2d999187044529f49da6554a92f740a96" },
+ "mason.nvim": { "branch": "main", "commit": "16ba83bfc8a25f52bb545134f5bee082b195c460" },
+ "mini.nvim": { "branch": "main", "commit": "d5e6f5b843f1d813d9c4bfb242b751dc5ab6f8ae" },
+ "moonfly": { "branch": "master", "commit": "261664f92ed51cf0c077555b2d4b2530307eac17" },
+ "none-ls-extras.nvim": { "branch": "main", "commit": "27681d797a26f1b4d6119296df42f5204c88a2dc" },
+ "none-ls.nvim": { "branch": "main", "commit": "01f8e62ea11603e59ad9ff7afcfa94fd183f76d6" },
+ "nvim": { "branch": "main", "commit": "0303a7208dba448c459767486a38a6ec05c4216b" },
+ "nvim-autopairs": { "branch": "master", "commit": "7b9923abad60b903ece7c52940e1321d39eccc79" },
+ "nvim-cmp": { "branch": "main", "commit": "a1d504892f2bc56c2e79b65c6faded2fd21f3eca" },
+ "nvim-lspconfig": { "branch": "master", "commit": "229b79051b380377664edc4cbd534930154921a1" },
+ "nvim-numbertoggle": { "branch": "main", "commit": "c5c0d305aee8532b5515914a08396b9154394c55" },
+ "nvim-toggler": { "branch": "main", "commit": "467808600882fd6c9e33b9dbc4889b1b80cfd917" },
+ "nvim-tree.lua": { "branch": "master", "commit": "07f541fcaa4a5ae019598240362449ab7e9812b3" },
+ "nvim-treesitter": { "branch": "main", "commit": "4916d6592ede8c07973490d9322f187e07dfefac" },
+ "nvim-web-devicons": { "branch": "master", "commit": "dfbfaa967a6f7ec50789bead7ef87e336c1fa63c" },
+ "plenary.nvim": { "branch": "master", "commit": "74b06c6c75e4eeb3108ec01852001636d85a932b" },
+ "presence.nvim": { "branch": "main", "commit": "87c857a56b7703f976d3a5ef15967d80508df6e6" },
+ "render-markdown.nvim": { "branch": "main", "commit": "5adf0895310c1904e5abfaad40a2baad7fe44a07" },
+ "telescope-undo.nvim": { "branch": "main", "commit": "928d0c2dc9606e01e2cc547196f48d2eaecf58e5" },
+ "telescope.nvim": { "branch": "master", "commit": "d90956833d7c27e73c621a61f20b29fdb7122709" },
+ "todo-comments.nvim": { "branch": "main", "commit": "31e3c38ce9b29781e4422fc0322eb0a21f4e8668" },
+ "twilight.nvim": { "branch": "main", "commit": "664e752f4a219801265cc3fc18782b457b58c1e1" },
+ "typst-preview.nvim": { "branch": "master", "commit": "325036ee145ca51d9efb145c09ac16bce3bc8b7d" },
+ "vim-closetag": { "branch": "master", "commit": "d0a562f8bdb107a50595aefe53b1a690460c3822" },
+ "vim-vsnip": { "branch": "master", "commit": "9bcfabea653abdcdac584283b5097c3f8760abaa" },
+ "which-key.nvim": { "branch": "main", "commit": "3aab2147e74890957785941f0c1ad87d0a44c15a" }
+}
diff --git a/nvim/lua/config.lua b/nvim/lua/config.lua
new file mode 100755
index 0000000..3fbccbd
--- /dev/null
+++ b/nvim/lua/config.lua
@@ -0,0 +1,89 @@
+local opt = vim.opt
+local map = vim.keymap.set
+local g = vim.g
+
+--filetypes
+vim.cmd("autocmd BufRead,BufNewFile *.m set filetype=objc")
+
+g.mapleader = " "
+
+opt.shiftwidth = 4
+opt.tabstop = 4
+opt.softtabstop = 4
+opt.smarttab = true
+opt.smartindent = true
+
+opt.undofile = true
+opt.undodir = vim.fn.stdpath("config") .. "/undo"
+
+opt.number = true
+opt.relativenumber = true
+
+opt.fillchars = "eob: "
+
+--g.loaded_netrw = 1
+--g.loaded_netrwPlugin = 1
+
+opt.termguicolors = true
+
+opt.spell = false
+opt.spelllang = { "en_us" }
+
+opt.shell = "/usr/bin/zsh"
+
+
+vim.lsp.handlers["textDocument/hover"] = function(err, result, ctx, config)
+ config = config or {}
+ config.border = "single"
+ return vim.lsp.handlers.hover(err, result, ctx, config)
+end
+vim.diagnostic.config({ float = { border = "single" } })
+
+map("n", "<leader>u", ":Telescope<cr>")
+map("n", "<leader>t", ":tabnew<cr>")
+map("n", "<A-Right>", ":tabn<cr>")
+map("n", "<A-Left>", ":tabp<cr>")
+map("n", "<A-q>", ":bw<cr>")
+
+map("n", "<leader>.", vim.diagnostic.open_float)
+
+map("n", "<leader>=", "=")
+
+map("n", "<leader>x", "<cmd>!chmod +x %<CR>")
+
+vim.api.nvim_create_autocmd("LspAttach", {
+ group = vim.api.nvim_create_augroup("UserLspConfig", {}),
+ callback = function(ev)
+ vim.bo[ev.buf].omnifunc = "v:lua.vim.lsp.omnifunc"
+
+ local opts = { buffer = ev.buf }
+ map("n", "K", function()
+ vim.lsp.buf.hover({ border = "single" })
+ end, opts)
+ map("n", "gi", vim.lsp.buf.implementation, opts)
+ map("n", "<C-k>", vim.lsp.buf.signature_help, opts)
+ map("n", "<space>wa", vim.lsp.buf.add_workspace_folder, opts)
+ map("n", "<space>wr", vim.lsp.buf.remove_workspace_folder, opts)
+ map("n", "<space>wl", function()
+ print(vim.inspect(vim.lsp.buf.list_workspace_folders()))
+ end, opts)
+ map("n", "<space>D", vim.lsp.buf.type_definition, opts)
+ map("n", "<space>rn", vim.lsp.buf.rename, opts)
+ map({ "n", "v" }, "<space>ca", vim.lsp.buf.code_action, opts)
+ map("n", "gr", vim.lsp.buf.references, opts)
+ map("n", "<space>f", function()
+ vim.lsp.buf.format({ async = true })
+ end, opts)
+ end,
+})
+
+local builtin = require("telescope.builtin")
+map("n", "<leader>ff", builtin.find_files, {})
+map("n", "<leader>fg", builtin.live_grep, {})
+map("n", "<leader>fb", builtin.buffers, {})
+map("n", "<leader>fh", builtin.help_tags, {})
+local treeapi = require("nvim-tree.api")
+map("n", "<leader>e", treeapi.tree.toggle, {})
+
+map("n", "<A-i>", '<CMD>lua require("FTerm").toggle()<CR>')
+map("t", "<A-i>", '<C-\\><C-n><CMD>lua require("FTerm").toggle()<CR>')
diff --git a/nvim/lua/plugins/catppuccin/init.lua b/nvim/lua/plugins/catppuccin/init.lua
new file mode 100755
index 0000000..c497b4d
--- /dev/null
+++ b/nvim/lua/plugins/catppuccin/init.lua
@@ -0,0 +1,90 @@
+local colors = {
+ black = "#121212",
+ white = "#ffffff",
+ gray = "#7e7e7e",
+ light_gray = "#d0d0d0",
+ dark_gray = "#505050",
+ red = "#ff5c57",
+ green = "#5af78e",
+ yellow = "#f3f99d",
+ blue = "#57c7ff",
+ purple = "#d183e8",
+ cyan = "#9aedfe",
+}
+
+local highlights = {
+ Normal = { fg = colors.light_gray, bg = "NONE" },
+ NormalNC = { fg = colors.light_gray, bg = "NONE" },
+ SignColumn = { bg = "NONE" },
+ FoldColumn = { bg = "NONE" },
+ Comment = { fg = colors.gray, italic = true },
+ Constant = { fg = colors.blue },
+ String = { fg = colors.green },
+ Identifier = { fg = colors.blue },
+ Function = { fg = colors.purple },
+ Statement = { fg = colors.yellow },
+ PreProc = { fg = colors.cyan },
+ Type = { fg = colors.blue },
+ Special = { fg = colors.red },
+ Underlined = { fg = colors.white, underline = true },
+ Todo = { fg = colors.black, bg = colors.yellow, bold = true },
+ StatusLine = { fg = colors.white, bg = "NONE" },
+ StatusLineNC = { fg = colors.light_gray, bg = "NONE" },
+ NvimTreeNormal = { fg = colors.light_gray, bg = "NONE" },
+ NvimTreeNormalNC = { fg = colors.light_gray, bg = "NONE" },
+ NvimTreeWinSeparator = { fg = colors.dark_gray, bg = "NONE" },
+ NvimTreeFolderName = { fg = colors.blue, bold = true },
+ NvimTreeOpenedFolderName = { fg = colors.green, bold = true },
+ NvimTreeRootFolder = { fg = colors.yellow, bold = true, underline = true },
+ NvimTreeFileIcon = { fg = colors.light_gray },
+ NvimTreeGitDirty = { fg = colors.red },
+ NvimTreeGitStaged = { fg = colors.green },
+ NvimTreeGitNew = { fg = colors.blue },
+ NvimTreeGitRenamed = { fg = colors.purple },
+ NvimTreeGitDeleted = { fg = colors.red },
+ CursorLine = { bg = colors.dark_gray },
+}
+
+for group, opts in pairs(highlights) do
+ vim.api.nvim_set_hl(0, group, opts)
+end
+
+local inactive_style = { fg = colors.dark_gray, bg = "NONE" }
+
+require("lualine").setup({
+ options = {
+ theme = {
+ normal = {
+ a = { fg = colors.black, bg = colors.white, gui = "bold" },
+ b = { fg = colors.white, bg = colors.dark_gray },
+ c = { fg = colors.white, bg = "NONE" },
+ },
+ inactive = {
+ a = { fg = colors.dark_gray, bg = "NONE", gui = "bold" },
+ b = inactive_style,
+ c = inactive_style,
+ },
+ },
+ },
+})
+
+require("nvim-tree").setup({
+ view = {
+ width = 30,
+ side = "left",
+ },
+ renderer = {
+ highlight_git = true,
+ root_folder_modifier = ":~",
+ icons = {
+ show = {
+ file = true,
+ folder = true,
+ git = true,
+ },
+ },
+ },
+ filters = {
+ dotfiles = false,
+ },
+})
diff --git a/nvim/lua/plugins/conform/init.lua b/nvim/lua/plugins/conform/init.lua
new file mode 100644
index 0000000..374ed55
--- /dev/null
+++ b/nvim/lua/plugins/conform/init.lua
@@ -0,0 +1,17 @@
+require("conform").setup({
+ formatters_by_ft = {
+ haskell = { "ormolu" },
+ },
+ formatters = {
+ ormolu = {
+ command = "ormolu",
+ args = { "--stdin-input-file", "$FILENAME" },
+ stdin = true,
+ },
+ },
+ format_on_save = { timeout_ms = 2000, lsp_format = "never" },
+})
+
+vim.keymap.set({ "n", "v" }, "<leader>f", function()
+ require("conform").format({ async = true, lsp_format = "never" })
+end)
diff --git a/nvim/lua/plugins/lazy/init.lua b/nvim/lua/plugins/lazy/init.lua
new file mode 100755
index 0000000..497cf69
--- /dev/null
+++ b/nvim/lua/plugins/lazy/init.lua
@@ -0,0 +1,17 @@
+local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
+if not vim.loop.fs_stat(lazypath) then
+ vim.fn.system({
+ "git",
+ "clone",
+ "--filter=blob:none",
+ "https://github.com/folke/lazy.nvim.git",
+ "--branch=stable",
+ lazypath,
+ })
+end
+vim.opt.rtp:prepend(lazypath)
+
+local lazy_plugins = require("plugins/lazy/plugins")
+
+require("lazy").setup(lazy_plugins)
+require("mason").setup()
diff --git a/nvim/lua/plugins/lazy/plugins.lua b/nvim/lua/plugins/lazy/plugins.lua
new file mode 100755
index 0000000..f668d40
--- /dev/null
+++ b/nvim/lua/plugins/lazy/plugins.lua
@@ -0,0 +1,144 @@
+return {
+ "andweeb/presence.nvim",
+ "catppuccin/nvim",
+ "neovim/nvim-lspconfig",
+ "gentoo/gentoo-syntax",
+ "williamboman/mason.nvim",
+ "stevearc/conform.nvim",
+ {
+ "neovimhaskell/haskell-vim",
+ ft = "haskell",
+ init = function()
+ vim.g.haskell_indent_disable = 0
+ vim.g.haskell_indent_let = 2
+ vim.g.haskell_indent_in = 1
+ vim.g.haskell_indent_where = 2
+ vim.g.haskell_indent_do = 2
+ vim.g.haskell_indent_case = 2
+ vim.g.haskell_indent_if = 2
+ end,
+ },
+ {
+ "nvim-telescope/telescope.nvim",
+ tag = "0.1.5",
+ dependencies = { "nvim-lua/plenary.nvim" },
+ },
+ {
+ "nvim-tree/nvim-tree.lua",
+ dependencies = { "nvim-tree/nvim-web-devicons" },
+ },
+ {
+ "folke/todo-comments.nvim",
+ dependencies = { "nvim-lua/plenary.nvim" },
+ opts = {},
+ },
+ {
+ "nvim-lualine/lualine.nvim",
+ dependencies = { "nvim-tree/nvim-web-devicons" },
+ },
+ {
+ "nvimtools/none-ls.nvim",
+ dependencies = {
+ "nvimtools/none-ls-extras.nvim",
+ },
+ },
+ {
+ "windwp/nvim-autopairs",
+ event = "InsertEnter",
+ },
+ {
+ "hrsh7th/nvim-cmp",
+ dependencies = {
+ "neovim/nvim-lspconfig",
+ "hrsh7th/cmp-nvim-lsp",
+ "hrsh7th/cmp-nvim-lua",
+ "hrsh7th/cmp-buffer",
+ "hrsh7th/cmp-path",
+ "hrsh7th/cmp-cmdline",
+ "hrsh7th/cmp-vsnip",
+ "hrsh7th/vim-vsnip",
+ },
+ },
+ {
+ "ray-x/lsp_signature.nvim",
+ event = "VeryLazy",
+ },
+ {
+ "nvim-treesitter/nvim-treesitter",
+ lazy = false,
+ priority = 1000,
+ build = ":TSUpdate",
+ },
+ "f3fora/cmp-spell",
+ {
+ "debugloop/telescope-undo.nvim",
+ dependencies = {
+ {
+ "nvim-telescope/telescope.nvim",
+ dependencies = { "nvim-lua/plenary.nvim" },
+ },
+ },
+ },
+ {
+ "chomosuke/typst-preview.nvim",
+ lazy = false,
+ version = "1.*",
+ opts = {},
+ },
+ {
+ "MeanderingProgrammer/render-markdown.nvim",
+ dependencies = {
+ "nvim-treesitter/nvim-treesitter",
+ "nvim-mini/mini.nvim",
+ },
+ opts = {},
+ },
+ "sitiom/nvim-numbertoggle",
+ "mluders/comfy-line-numbers.nvim",
+ {
+ "folke/which-key.nvim",
+ event = "VeryLazy",
+ init = function()
+ vim.o.timeout = true
+ vim.o.timeoutlen = 300
+ end,
+ },
+ {
+ "folke/twilight.nvim",
+ opts = {
+ dimming = {
+ alpha = 0.35,
+ color = { "Normal" },
+ term_bg = "#282828",
+ inactive = false,
+ },
+ context = 15,
+ treesitter = true,
+ expand = {
+ "function",
+ "method",
+ "table",
+ "if_statement",
+ },
+ exclude = {
+ "md",
+ },
+ },
+ },
+ "nguyenvukhang/nvim-toggler",
+ "alvan/vim-closetag",
+ {
+ "numtostr/fterm.nvim",
+ border = "double",
+ dimensions = {
+ height = 0.9,
+ width = 0.9,
+ },
+ },
+ {
+ "bluz71/vim-moonfly-colors",
+ name = "moonfly",
+ lazy = false,
+ priority = 1000,
+ },
+}
diff --git a/nvim/lua/plugins/lsp/init.lua b/nvim/lua/plugins/lsp/init.lua
new file mode 100755
index 0000000..3745198
--- /dev/null
+++ b/nvim/lua/plugins/lsp/init.lua
@@ -0,0 +1,305 @@
+local lsp = require("lspconfig")
+local cmp = require("cmp")
+
+vim.lsp.config('*', {
+ root_markers = { '.git' },
+})
+
+vim.diagnostic.config({
+ virtual_text = true,
+ severity_sort = true,
+ float = {
+ style = 'minimal',
+ border = 'rounded',
+ source = 'if_many',
+ header = '',
+ prefix = '',
+ },
+ signs = {
+ text = {
+ [vim.diagnostic.severity.ERROR] = '✘',
+ [vim.diagnostic.severity.WARN] = '▲',
+ [vim.diagnostic.severity.HINT] = '⚑',
+ [vim.diagnostic.severity.INFO] = '»',
+ },
+ },
+})
+
+local orig = vim.lsp.util.open_floating_preview
+---@diagnostic disable-next-line: duplicate-set-field
+function vim.lsp.util.open_floating_preview(contents, syntax, opts, ...)
+ opts = opts or {}
+ opts.border = opts.border or 'rounded'
+ opts.max_width = opts.max_width or 80
+ opts.max_height = opts.max_height or 24
+ opts.wrap = opts.wrap ~= false
+ return orig(contents, syntax, opts, ...)
+end
+
+vim.api.nvim_create_autocmd('LspAttach', {
+ group = vim.api.nvim_create_augroup('my.lsp', {}),
+ callback = function(args)
+ local client = assert(vim.lsp.get_client_by_id(args.data.client_id))
+ local buf = args.buf
+ local map = function(mode, lhs, rhs) vim.keymap.set(mode, lhs, rhs, { buffer = buf }) end
+
+ map('n', 'K', vim.lsp.buf.hover)
+ map('n', 'gd', vim.lsp.buf.definition)
+ map('n', 'gD', vim.lsp.buf.declaration)
+ map('n', 'gi', vim.lsp.buf.implementation)
+ map('n', 'go', vim.lsp.buf.type_definition)
+ map('n', 'gr', vim.lsp.buf.references)
+ map('n', 'gs', vim.lsp.buf.signature_help)
+ map('n', 'gl', vim.diagnostic.open_float)
+ map('n', '<F2>', vim.lsp.buf.rename)
+ map({ 'n', 'x' }, '<F3>', function() vim.lsp.buf.format({ async = true }) end)
+ map('n', '<F4>', vim.lsp.buf.code_action)
+
+ if client:supports_method('textDocument/documentHighlight') then
+ local highlight_augroup = vim.api.nvim_create_augroup('my.lsp.highlight', { clear = false })
+ vim.api.nvim_create_autocmd({ 'CursorHold', 'CursorHoldI' }, {
+ buffer = buf,
+ group = highlight_augroup,
+ callback = vim.lsp.buf.document_highlight,
+ })
+ vim.api.nvim_create_autocmd({ 'CursorMoved', 'CursorMovedI' }, {
+ buffer = buf,
+ group = highlight_augroup,
+ callback = vim.lsp.buf.clear_references,
+ })
+ end
+
+ local excluded_filetypes = { php = true, c = true, cpp = true, haskell = true, lhaskell = true }
+ if not client:supports_method('textDocument/willSaveWaitUntil')
+ and client:supports_method('textDocument/formatting')
+ and not excluded_filetypes[vim.bo[buf].filetype]
+ then
+ vim.api.nvim_create_autocmd('BufWritePre', {
+ group = vim.api.nvim_create_augroup('my.lsp.format', { clear = false }),
+ buffer = buf,
+ callback = function()
+ vim.lsp.buf.format({ bufnr = buf, id = client.id, timeout_ms = 1000 })
+ end,
+ })
+ end
+ end,
+})
+
+require("nvim-autopairs").setup({})
+require("lsp_signature").setup({ hint_enable = false })
+
+cmp.setup({
+ snippet = {
+ expand = function(args)
+ vim.fn["vsnip#anonymous"](args.body)
+ end,
+ },
+ mapping = cmp.mapping.preset.insert({
+ ["<C-b>"] = cmp.mapping.scroll_docs(-4),
+ ["<C-f>"] = cmp.mapping.scroll_docs(4),
+ ["<C-Space>"] = cmp.mapping.complete(),
+ ["<C-e>"] = cmp.mapping.abort(),
+ ["<CR>"] = cmp.mapping.confirm({ select = true }),
+ ["<Tab>"] = cmp.mapping(function(fallback)
+ if cmp.visible() then
+ cmp.select_next_item()
+ else
+ fallback()
+ end
+ end, { "i", "s" }),
+ ["<S-Tab>"] = cmp.mapping(function(fallback)
+ if cmp.visible() then
+ cmp.select_prev_item()
+ else
+ fallback()
+ end
+ end, { "i", "s" }),
+ }),
+ window = {
+ completion = cmp.config.window.bordered(),
+ documentation = cmp.config.window.bordered()
+ },
+ sources = cmp.config.sources({
+ { name = "nvim_lsp", keyword_length = 1 },
+ { name = "vsnip" },
+ }, {
+ { name = "buffer", keyword_length = 1 },
+ { name = "spell" },
+ }),
+})
+
+local caps = require("cmp_nvim_lsp").default_capabilities()
+
+vim.lsp.config['luals'] = {
+ cmd = { 'lua-language-server' },
+ filetypes = { 'lua' },
+ root_markers = { { '.luarc.json', '.luarc.jsonc' }, '.git' },
+ capabilities = caps,
+ settings = {
+ Lua = {
+ runtime = { version = 'LuaJIT' },
+ diagnostics = { globals = { 'vim' } },
+ workspace = {
+ checkThirdParty = false,
+ library = vim.list_extend(
+ vim.api.nvim_get_runtime_file('', true),
+ { '/home/tony/repos/oxwm/templates' }
+ ),
+ },
+ telemetry = { enable = false },
+ },
+ },
+}
+
+vim.lsp.config['cssls'] = {
+ cmd = { 'vscode-css-language-server', '--stdio' },
+ filetypes = { 'css', 'scss', 'less' },
+ root_markers = { 'package.json', '.git' },
+ capabilities = caps,
+ settings = {
+ css = { validate = true },
+ scss = { validate = true },
+ less = { validate = true },
+ },
+}
+
+vim.lsp.config['phpls'] = {
+ cmd = { 'intelephense', '--stdio' },
+ filetypes = { 'php' },
+ root_markers = { 'composer.json', '.git' },
+ capabilities = caps,
+ settings = {
+ intelephense = {
+ files = { maxSize = 5000000 },
+ },
+ },
+}
+
+vim.lsp.config['ts_ls'] = {
+ cmd = { 'typescript-language-server', '--stdio' },
+ filetypes = { 'javascript', 'javascriptreact', 'javascript.jsx', 'typescript', 'typescriptreact', 'typescript.tsx' },
+ root_markers = { 'package.json', 'tsconfig.json', 'jsconfig.json', '.git' },
+ capabilities = caps,
+ settings = {
+ completions = { completeFunctionCalls = true },
+ },
+}
+
+vim.lsp.config['zls'] = {
+ cmd = { 'zls' },
+ filetypes = { 'zig', 'zir' },
+ root_markers = { 'zls.json', 'build.zig', '.git' },
+ capabilities = caps,
+ settings = {
+ zls = {
+ enable_build_on_save = true,
+ build_on_save_step = "install",
+ warn_style = false,
+ enable_snippets = true,
+ }
+ }
+}
+
+vim.lsp.config['nil_ls'] = {
+ cmd = { 'nil' },
+ filetypes = { 'nix' },
+ root_markers = { 'flake.nix', 'default.nix', '.git' },
+ capabilities = caps,
+ settings = {
+ ['nil'] = {
+ formatting = {
+ command = { "nixpkgs-fmt" }
+ }
+ }
+ }
+}
+
+vim.lsp.config['rust_analyzer'] = {
+ cmd = { 'rust-analyzer' },
+ filetypes = { 'rust' },
+ root_markers = { 'Cargo.toml', 'rust-project.json', '.git' },
+ capabilities = caps,
+ settings = {
+ ['rust-analyzer'] = {
+ cargo = { allFeatures = true },
+ formatting = { command = { "rustfmt" } },
+ },
+ },
+}
+
+vim.lsp.config['clangd'] = {
+ cmd = { 'clangd', '--background-index', '--clang-tidy' },
+ filetypes = { 'c', 'cpp', 'objc', 'objcpp' },
+ root_markers = { 'compile_commands.json', '.clangd', 'configure.ac', 'Makefile', '.git' },
+ capabilities = caps,
+}
+
+vim.lsp.config['c3lsp'] = {
+ cmd = { 'c3-lsp' },
+ filetypes = { 'c3' },
+ root_markers = { 'project.json', '.git' },
+ capabilities = caps,
+}
+
+vim.lsp.config['serve_d'] = {
+ cmd = { 'serve-d' },
+ filetypes = { 'd' },
+ root_markers = { 'dub.sdl', 'dub.json', '.git' },
+ capabilities = caps,
+}
+
+vim.lsp.config['jsonls'] = {
+ cmd = { 'vscode-json-languageserver', '--stdio' },
+ filetypes = { 'json', 'jsonc' },
+ root_markers = { 'package.json', '.git', 'config.jsonc' },
+ capabilities = caps,
+}
+
+vim.lsp.config['hls'] = {
+ cmd = { 'haskell-language-server-wrapper', '--lsp' },
+ filetypes = { 'haskell', 'lhaskell' },
+ root_markers = { 'stack.yaml', 'cabal.project', 'package.yaml', 'hie.yaml', '.git' },
+ capabilities = caps,
+ settings = {
+ haskell = {
+ formattingProvider = 'ormolu',
+ plugin = { semanticTokens = { globalOn = false } },
+ },
+ },
+}
+
+vim.lsp.config['gopls'] = {
+ cmd = { 'gopls' },
+ filetypes = { 'go', 'gomod', 'gowork', 'gotmpl' },
+ root_markers = { 'go.mod', 'go.work', '.git' },
+ capabilities = caps,
+ settings = {
+ gopls = {
+ analyses = { unusedparams = false, ST1003 = false, ST1000 = false },
+ staticcheck = true,
+ },
+ },
+}
+
+vim.lsp.config['templ'] = {
+ cmd = { 'templ', 'lsp' },
+ filetypes = { 'templ' },
+ root_markers = { 'go.mod', '.git' },
+ capabilities = caps,
+}
+
+vim.filetype.add({
+ extension = {
+ h = 'c',
+ c3 = 'c3',
+ d = 'd',
+ templ = 'templ',
+ },
+})
+
+---@diagnostic disable-next-line: invisible
+for name, _ in pairs(vim.lsp.config._configs) do
+ if name ~= '*' then
+ vim.lsp.enable(name)
+ end
+end
diff --git a/nvim/lua/plugins/lualine/init.lua b/nvim/lua/plugins/lualine/init.lua
new file mode 100755
index 0000000..d23b323
--- /dev/null
+++ b/nvim/lua/plugins/lualine/init.lua
@@ -0,0 +1,50 @@
+local lualine = require("lualine")
+
+lualine.setup({
+ options = {
+ icons_enabled = true,
+ theme = "auto",
+ component_separators = { left = "|", right = "|" },
+ section_separators = { left = "", right = "" },
+ disabled_filetypes = {
+ statusline = {},
+ winbar = {},
+ },
+ ignore_focus = {},
+ always_divide_middle = true,
+ globalstatus = false,
+ refresh = {
+ statusline = 1000,
+ tabline = 1000,
+ winbar = 1000,
+ },
+ },
+ sections = {
+ lualine_a = { "mode" },
+ lualine_b = {
+ { "branch", color = { bg = "NONE" } },
+ { "diff", color = { bg = "NONE" } },
+ { "diagnostics", color = { bg = "NONE" } }
+ },
+ lualine_c = { { "filename", color = { bg = "NONE" } } },
+ lualine_x = {
+ { "encoding", color = { bg = "NONE" } },
+ { "fileformat", color = { bg = "NONE" } },
+ { "filetype", color = { bg = "NONE" } }
+ },
+ lualine_y = {},
+ lualine_z = { "location" },
+ },
+ inactive_sections = {
+ lualine_a = {},
+ lualine_b = {},
+ lualine_c = { { "filename", color = { bg = "NONE" } } },
+ lualine_x = { { "location", color = { bg = "NONE" } } },
+ lualine_y = {},
+ lualine_z = {},
+ },
+ tabline = {},
+ winbar = {},
+ inactive_winbar = {},
+ extensions = {},
+})
diff --git a/nvim/lua/plugins/nvim-tree/init.lua b/nvim/lua/plugins/nvim-tree/init.lua
new file mode 100755
index 0000000..c7174f7
--- /dev/null
+++ b/nvim/lua/plugins/nvim-tree/init.lua
@@ -0,0 +1,2 @@
+local tree = require("nvim-tree")
+tree.setup() \ No newline at end of file
diff --git a/nvim/lua/plugins/render-markdown/init.lua b/nvim/lua/plugins/render-markdown/init.lua
new file mode 100755
index 0000000..ab078ce
--- /dev/null
+++ b/nvim/lua/plugins/render-markdown/init.lua
@@ -0,0 +1,139 @@
+require("render-markdown").setup({
+ link = {
+ -- Turn on / off inline link icon rendering.
+ enabled = true,
+ -- Additional modes to render links.
+ render_modes = false,
+ -- How to handle footnote links, start with a '^'.
+ footnote = {
+ -- Turn on / off footnote rendering.
+ enabled = true,
+ -- Replace value with superscript equivalent.
+ superscript = true,
+ -- Added before link content.
+ prefix = "",
+ -- Added after link content.
+ suffix = "",
+ },
+ -- Inlined with 'image' elements.
+ image = "󰥶 ",
+ -- Inlined with 'email_autolink' elements.
+ email = "󰀓 ",
+ -- Fallback icon for 'inline_link' and 'uri_autolink' elements.
+ hyperlink = "󰌹 ",
+ -- Applies to the inlined icon as a fallback.
+ highlight = "RenderMarkdownLink",
+ -- Applies to WikiLink elements.
+ wiki = {
+ icon = "󱗖 ",
+ body = function()
+ return nil
+ end,
+ highlight = "RenderMarkdownWikiLink",
+ },
+ -- Define custom destination patterns so icons can quickly inform you of what a link
+ -- contains. Applies to 'inline_link', 'uri_autolink', and wikilink nodes. When multiple
+ -- patterns match a link the one with the longer pattern is used.
+ -- The key is for healthcheck and to allow users to change its values, value type below.
+ -- | pattern | matched against the destination text |
+ -- | icon | gets inlined before the link text |
+ -- | kind | optional determines how pattern is checked |
+ -- | | pattern | @see :h lua-patterns, is the default if not set |
+ -- | | suffix | @see :h vim.endswith() |
+ -- | priority | optional used when multiple match, uses pattern length if empty |
+ -- | highlight | optional highlight for 'icon', uses fallback highlight if empty |
+ custom = {
+ web = { pattern = "^http", icon = "󰖟 " },
+ github = { pattern = "github%.com", icon = "󰊤 " },
+ gitlab = { pattern = "gitlab%.com", icon = "󰮠 " },
+ stackoverflow = { pattern = "stackoverflow%.com", icon = "󰓌 " },
+ wikipedia = { pattern = "wikipedia%.org", icon = "󰖬 " },
+ youtube = { pattern = "youtube%.com", icon = "󰗃 " },
+ },
+ },
+ callout = {
+ -- Callouts are a special instance of a 'block_quote' that start with a 'shortcut_link'.
+ -- The key is for healthcheck and to allow users to change its values, value type below.
+ -- | raw | matched against the raw text of a 'shortcut_link', case insensitive |
+ -- | rendered | replaces the 'raw' value when rendering |
+ -- | highlight | highlight for the 'rendered' text and quote markers |
+ -- | quote_icon | optional override for quote.icon value for individual callout |
+ -- | category | optional metadata useful for filtering |
+
+ note = { raw = "[!NOTE]", rendered = "󰋽 Note", highlight = "RenderMarkdownInfo" },
+ tip = { raw = "[!TIP]", rendered = "󰌶 Tip", highlight = "RenderMarkdownSuccess" },
+ important = { raw = "[!IMPORTANT]", rendered = "󰅾 Important", highlight = "RenderMarkdownHint" },
+ warning = { raw = "[!WARNING]", rendered = "󰀪 Warning", highlight = "RenderMarkdownWarn" },
+ caution = { raw = "[!CAUTION]", rendered = "󰳦 Caution", highlight = "RenderMarkdownError" },
+ abstract = { raw = "[!ABSTRACT]", rendered = "󰨸 Abstract", highlight = "RenderMarkdownInfo" },
+ summary = { raw = "[!SUMMARY]", rendered = "󰨸 Summary", highlight = "RenderMarkdownInfo" },
+ tldr = { raw = "[!TLDR]", rendered = "󰨸 Tldr", highlight = "RenderMarkdownInfo" },
+ info = { raw = "[!INFO]", rendered = "󰋽 Info", highlight = "RenderMarkdownInfo" },
+ todo = { raw = "[!TODO]", rendered = "󰗡 Todo", highlight = "RenderMarkdownInfo" },
+ hint = { raw = "[!HINT]", rendered = "󰌶 Hint", highlight = "RenderMarkdownSuccess" },
+ success = { raw = "[!SUCCESS]", rendered = "󰄬 Success", highlight = "RenderMarkdownSuccess" },
+ check = { raw = "[!CHECK]", rendered = "󰄬 Check", highlight = "RenderMarkdownSuccess" },
+ done = { raw = "[!DONE]", rendered = "󰄬 Done", highlight = "RenderMarkdownSuccess" },
+ question = { raw = "[!QUESTION]", rendered = "󰘥 Question", highlight = "RenderMarkdownWarn" },
+ help = { raw = "[!HELP]", rendered = "󰘥 Help", highlight = "RenderMarkdownWarn" },
+ faq = { raw = "[!FAQ]", rendered = "󰘥 Faq", highlight = "RenderMarkdownWarn" },
+ attention = { raw = "[!ATTENTION]", rendered = "󰀪 Attention", highlight = "RenderMarkdownWarn" },
+ failure = { raw = "[!FAILURE]", rendered = "󰅖 Failure", highlight = "RenderMarkdownError" },
+ fail = { raw = "[!FAIL]", rendered = "󰅖 Fail", highlight = "RenderMarkdownError" },
+ missing = { raw = "[!MISSING]", rendered = "󰅖 Missing", highlight = "RenderMarkdownError" },
+ danger = { raw = "[!DANGER]", rendered = "󱐌 Danger", highlight = "RenderMarkdownError" },
+ error = { raw = "[!ERROR]", rendered = "󱐌 Error", highlight = "RenderMarkdownError" },
+ bug = { raw = "[!BUG]", rendered = "󰨰 Bug", highlight = "RenderMarkdownError" },
+ example = { raw = "[!EXAMPLE]", rendered = "󰉹 Example", highlight = "RenderMarkdownHint" },
+ quote = { raw = "[!QUOTE]", rendered = "󱆨 Quote", highlight = "RenderMarkdownQuote" },
+ cite = { raw = "[!CITE]", rendered = "󱆨 Cite", highlight = "RenderMarkdownQuote" },
+ },
+ checkbox = {
+ enabled = true,
+ render_modes = false,
+ bullet = false,
+ right_pad = 1,
+ unchecked = {
+ icon = "󰄱 ",
+ highlight = "RenderMarkdownUnchecked",
+ scope_highlight = nil,
+ },
+ checked = {
+ icon = "󰱒 ",
+ highlight = "RenderMarkdownChecked",
+ scope_highlight = nil,
+ },
+ custom = {
+ todo = { raw = "[-]", rendered = "󰥔 ", highlight = "RenderMarkdownTodo", scope_highlight = nil },
+ },
+ },
+ bullet = {
+ enabled = true,
+ render_modes = false,
+ icons = { "●", "○", "◆", "◇" },
+ ordered_icons = function(ctx)
+ local value = vim.trim(ctx.value)
+ local index = tonumber(value:sub(1, #value - 1))
+ return ("%d."):format(index > 1 and index or ctx.index)
+ end,
+ left_pad = 0,
+ right_pad = 0,
+ highlight = "RenderMarkdownBullet",
+ scope_highlight = {},
+ },
+ quote = { icon = "▋" },
+ anti_conceal = {
+ enabled = true,
+ -- Which elements to always show, ignoring anti conceal behavior. Values can either be
+ -- booleans to fix the behavior or string lists representing modes where anti conceal
+ -- behavior will be ignored. Valid values are:
+ -- head_icon, head_background, head_border, code_language, code_background, code_border,
+ -- dash, bullet, check_icon, check_scope, quote, table_border, callout, link, sign
+ ignore = {
+ code_background = true,
+ sign = true,
+ },
+ above = 0,
+ below = 0,
+ },
+})
diff --git a/nvim/lua/plugins/telescope/init.lua b/nvim/lua/plugins/telescope/init.lua
new file mode 100755
index 0000000..2cb0f77
--- /dev/null
+++ b/nvim/lua/plugins/telescope/init.lua
@@ -0,0 +1,3 @@
+local tel = require("telescope")
+tel.setup({})
+tel.load_extension("undo")
diff --git a/nvim/lua/plugins/treesitter/init.lua b/nvim/lua/plugins/treesitter/init.lua
new file mode 100755
index 0000000..c50393b
--- /dev/null
+++ b/nvim/lua/plugins/treesitter/init.lua
@@ -0,0 +1,26 @@
+require("nvim-treesitter").setup()
+
+vim.api.nvim_create_autocmd("FileType", {
+ pattern = "*",
+ callback = function(args)
+ local lang = vim.treesitter.language.get_lang(args.match)
+
+ if not lang or lang == "nix" then
+ return
+ end
+
+ if vim.treesitter.language.add(lang) then
+ vim.treesitter.start(args.buf, lang)
+ return
+ end
+
+ local available = require("nvim-treesitter").get_available()
+
+ if vim.tbl_contains(available, lang) then
+ require("nvim-treesitter").install({ lang }):wait(30000)
+ if vim.treesitter.language.add(lang) then
+ vim.treesitter.start(args.buf, lang)
+ end
+ end
+ end,
+})
diff --git a/nvim/lua/plugins/whichkey/init.lua b/nvim/lua/plugins/whichkey/init.lua
new file mode 100755
index 0000000..e6b9906
--- /dev/null
+++ b/nvim/lua/plugins/whichkey/init.lua
@@ -0,0 +1,2 @@
+local wk = require("which-key")
+wk.setup({})
diff --git a/nvim/lua/status_line/init.lua b/nvim/lua/status_line/init.lua
new file mode 100755
index 0000000..904684b
--- /dev/null
+++ b/nvim/lua/status_line/init.lua
@@ -0,0 +1,102 @@
+local colors = {
+ foreground = "#ebdbb2",
+ pink = "#b16286",
+ red = "#cc241d",
+ green = "#98971a",
+ yellow = "#d79921",
+ orange = "#d65d0e",
+ magenta = "#b16286",
+ cyan = "#458588",
+ bg_darken = "#1d2021",
+ dark_text = "#1d2021",
+}
+
+local M = {}
+
+function M.setup()
+ vim.cmd("hi StatusBackground guifg=" .. colors.foreground .. " guibg=" .. colors.bg_darken)
+ vim.cmd("hi Moden guifg=" .. colors.dark_text .. " guibg=" .. colors.pink .. " gui=bold")
+ vim.cmd("hi Modei guifg=" .. colors.dark_text .. " guibg=" .. colors.green .. " gui=bold")
+ vim.cmd("hi Modev guifg=" .. colors.dark_text .. " guibg=" .. colors.yellow .. " gui=bold")
+ vim.cmd("hi Modet guifg=" .. colors.dark_text .. " guibg=" .. colors.orange .. " gui=bold")
+ vim.cmd("hi Modec guifg=" .. colors.dark_text .. " guibg=" .. colors.magenta .. " gui=bold")
+ vim.cmd("hi Moder guifg=" .. colors.dark_text .. " guibg=" .. colors.red .. " gui=bold")
+ vim.cmd("hi Filetype guifg=" .. colors.cyan .. " guibg=" .. colors.bg_darken .. " gui=bold")
+ vim.cmd("hi Position guifg=" .. colors.yellow .. " guibg=" .. colors.bg_darken .. " gui=bold")
+ vim.cmd("hi GitBranch guifg=" .. colors.orange .. " guibg=" .. colors.bg_darken .. " gui=bold")
+ vim.cmd("hi GitDiff guifg=" .. colors.green .. " guibg=" .. colors.bg_darken)
+ vim.cmd("hi GitDiffDel guifg=" .. colors.red .. " guibg=" .. colors.bg_darken)
+ vim.cmd("hi Separator guifg=" .. colors.foreground .. " guibg=" .. colors.bg_darken)
+ vim.cmd("hi LSPError guifg=" .. colors.red .. " guibg=" .. colors.bg_darken)
+ vim.cmd("hi LSPWarn guifg=" .. colors.yellow .. " guibg=" .. colors.bg_darken)
+ vim.cmd("hi LSPInfo guifg=" .. colors.cyan .. " guibg=" .. colors.bg_darken)
+ vim.cmd("hi LSPOk guifg=" .. colors.green .. " guibg=" .. colors.bg_darken)
+
+ vim.o.statusline = table.concat({
+ "%{%v:lua.themeStatuslineMode()%} ",
+ "%f ",
+ "%{%v:lua.themeGitBranch()%} ",
+ "%{%v:lua.themeGitDiff()%} ",
+ "%{%v:lua.themeLSP()%} ",
+ "%=",
+ "%{%v:lua.themeStatuslineFiletype()%} ",
+ "%{%v:lua.themeStatuslinePosition()%}",
+ })
+end
+
+function themeStatuslineMode()
+ local mode = vim.fn.mode()
+ local mode_highlight = "Mode" .. mode:sub(1, 1)
+ return string.format("%%#%s# %s %%#StatusBackground#", mode_highlight, mode:upper())
+end
+
+function themeStatuslineFiletype()
+ return string.format("%%#Filetype# %s %%#Separator#│%%#StatusBackground#", vim.bo.filetype)
+end
+
+function themeStatuslinePosition()
+ local row, col = unpack(vim.api.nvim_win_get_cursor(0))
+ return string.format("%%#Position# %03d:%02d ", row, col)
+end
+
+function themeGitBranch()
+ local branch = vim.fn.systemlist("git rev-parse --abbrev-ref HEAD 2>/dev/null")[1] or ""
+ if branch ~= "" then
+ return string.format("%%#GitBranch# %s %%#Separator#│%%#StatusBackground#", branch)
+ end
+ return ""
+end
+
+function themeGitDiff()
+ local diff = vim.fn.systemlist("git diff --shortstat 2>/dev/null")[1] or ""
+ local added, removed = diff:match("(%d+) insertions?"), diff:match("(%d+) deletions?")
+ added = added or "0"
+ removed = removed or "0"
+ if diff ~= "" then
+ return string.format("%%#GitDiff#+%s %%#GitDiffDel#-%s %%#StatusBackground#", added, removed)
+ end
+ return ""
+end
+
+function themeLSP()
+ local errors = #vim.diagnostic.get(0, { severity = vim.diagnostic.severity.ERROR })
+ local warns = #vim.diagnostic.get(0, { severity = vim.diagnostic.severity.WARN })
+ local info = #vim.diagnostic.get(0, { severity = vim.diagnostic.severity.INFO })
+ local hints = #vim.diagnostic.get(0, { severity = vim.diagnostic.severity.HINT })
+ local str = ""
+ if errors > 0 then
+ str = str .. string.format("%%#LSPError#E:%d ", errors)
+ end
+ if warns > 0 then
+ str = str .. string.format("%%#LSPWarn#W:%d ", warns)
+ end
+ if info > 0 then
+ str = str .. string.format("%%#LSPInfo#I:%d ", info)
+ end
+ if hints > 0 then
+ str = str .. string.format("%%#LSPOk#H:%d ", hints)
+ end
+ return str
+end
+
+return M
diff --git a/quickshell/LICENSE b/quickshell/LICENSE
new file mode 100644
index 0000000..f288702
--- /dev/null
+++ b/quickshell/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
diff --git a/quickshell/config.js b/quickshell/config.js
new file mode 100644
index 0000000..36531c5
--- /dev/null
+++ b/quickshell/config.js
@@ -0,0 +1,66 @@
+const barHeight = 42
+const barOutput = "HDMI-A-1"
+const barBackground = "#000000"
+const barMargin = 8
+const barPosition = "top"
+
+const accentGreen = "#a6e3a1"
+const accentYellow = "#f9e2af"
+const accentOrange = "#fab387"
+const accentRed = "#f38ba8"
+
+const textPrimary = "#cdd6f4"
+const textSecondary = "#a6adc8"
+const textInactive = "#89b4fa"
+const textWindow = "#d3869b"
+const textDark = "#09090F"
+
+const workspaceBackground = "#16161a"
+
+const fontFamily = "Iosevka Nerd Font"
+const fontSize = 14
+
+const blockHeight = 30
+const blockRadius = 7
+const blockPadding = 24
+
+const workspaceWidth = 39
+const workspaceSpacing = 6
+
+const gradientAnimationDuration = 3000
+
+var wallpapers = [
+ {
+ path: "/home/coast/Wallpapers/palm.png",
+ outputs: ["HDMI-A-1"],
+ fillMode: "PreserveAspectCrop",
+ opacity: 1.0,
+ blur: 0.0,
+ asynchronous: true,
+ cache: true
+ },
+ {
+ path: "/home/coast/Wallpapers/palm.png",
+ outputs: ["DP-1"],
+ fillMode: "PreserveAspectCrop",
+ opacity: 1.0,
+ blur: 0.0,
+ asynchronous: true,
+ cache: true
+ }
+]
+var wallpaperWipeDirection = "random"
+
+var notificationWidth = 360
+var notificationMarginTop = 50
+var notificationMarginRight = 12
+var notificationSpacing = 8
+var notificationBorder = "#313244"
+var notificationTimeout = 5000
+var notificationAnimationDuration = 200
+var notificationSlideDistance = 30
+
+const notificationVesktopAppId = "vesktop"
+const notificationVesktopNames = ["vesktop", "discord"]
+
+const musicMaxWidth = 400
diff --git a/quickshell/modules/bar/Bar.qml b/quickshell/modules/bar/Bar.qml
new file mode 100644
index 0000000..e983faf
--- /dev/null
+++ b/quickshell/modules/bar/Bar.qml
@@ -0,0 +1,171 @@
+import QtQuick
+import QtQuick.Layouts
+import Quickshell
+import "../../config.js" as Config
+
+PanelWindow {
+ id: bar
+
+ required property var modelData
+ screen: modelData
+
+ readonly property bool atBottom: Config.barPosition === "bottom"
+ readonly property real hiddenY: atBottom ? Config.barHeight : -Config.barHeight
+
+ anchors {
+ top: !bar.atBottom
+ bottom: bar.atBottom
+ left: true
+ right: true
+ }
+
+ implicitHeight: Config.barHeight
+ color: "transparent"
+
+ Rectangle {
+ id: content
+
+ anchors.fill: parent
+ color: Config.barBackground
+ opacity: 0
+ y: bar.hiddenY
+
+ RowLayout {
+ anchors {
+ left: parent.left
+ leftMargin: Config.barMargin
+ verticalCenter: parent.verticalCenter
+ }
+
+ spacing: Config.workspaceSpacing
+
+ Loader {
+ active: true
+ sourceComponent: Workspaces {}
+ }
+ }
+
+ Rectangle {
+ anchors {
+ horizontalCenter: parent.horizontalCenter
+ verticalCenter: parent.verticalCenter
+ }
+
+ height: Config.blockHeight
+ width: windowTitle.contentWidth + Config.blockPadding
+ radius: Config.blockRadius
+ color: "transparent"
+
+ Text {
+ id: windowTitle
+
+ anchors.centerIn: parent
+
+ text: (niri.focusedWindow?.appId === "firefox" ||
+ niri.focusedWindow?.appId === "firefox-bin")
+ ? "Mozilla Firefox"
+ : (niri.focusedWindow?.appId === "vesktop" ||
+ niri.focusedWindow?.appId === "vesktop-bin")
+ ? "Vesktop"
+ : (niri.focusedWindow?.appId === "equibop" ||
+ niri.focusedWindow?.appId === "equibop-bin")
+ ? "Equibop"
+ : (niri.focusedWindow?.title ?? "")
+
+
+ color: Config.textWindow
+
+ font.family: Config.fontFamily
+ font.pixelSize: Config.fontSize
+ font.bold: true
+
+ elide: Text.ElideRight
+ }
+ }
+
+ RowLayout {
+ id: rightLayout
+
+ anchors {
+ right: parent.right
+ rightMargin: Config.barMargin
+ verticalCenter: parent.verticalCenter
+ }
+
+ spacing: Config.workspaceSpacing
+
+ Loader {
+ active: true
+ sourceComponent: Music {}
+ }
+
+ Loader {
+ active: true
+ sourceComponent: Time {}
+ }
+
+ Loader {
+ active: true
+ sourceComponent: QuickControl {}
+ }
+ }
+ }
+
+ ParallelAnimation {
+ id: showAnimation
+
+ NumberAnimation {
+ target: content
+ property: "y"
+ from: bar.hiddenY
+ to: 0
+ duration: Config.barAnimationDuration
+ easing.type: Easing.OutCubic
+ }
+
+ NumberAnimation {
+ target: content
+ property: "opacity"
+ from: 0
+ to: 1
+ duration: Config.barAnimationDuration
+ easing.type: Easing.OutCubic
+ }
+ }
+
+ ParallelAnimation {
+ id: hideAnimation
+
+ NumberAnimation {
+ target: content
+ property: "y"
+ from: 0
+ to: bar.hiddenY
+ duration: Config.barAnimationDuration
+ easing.type: Easing.InCubic
+ }
+
+ NumberAnimation {
+ target: content
+ property: "opacity"
+ from: 1
+ to: 0
+ duration: Config.barAnimationDuration
+ easing.type: Easing.InCubic
+ }
+
+ onFinished: {
+ showAnimation.start()
+ }
+ }
+
+ Component.onCompleted: {
+ showAnimation.start()
+ }
+
+ function restart() {
+ showAnimation.stop()
+ hideAnimation.stop()
+ hideAnimation.start()
+ }
+}
diff --git a/quickshell/modules/bar/Music.qml b/quickshell/modules/bar/Music.qml
new file mode 100644
index 0000000..50e1cf1
--- /dev/null
+++ b/quickshell/modules/bar/Music.qml
@@ -0,0 +1,237 @@
+import QtQuick
+import QtQuick.Layouts
+import Quickshell
+import Quickshell.Services.Mpris
+import "../../config.js" as Config
+
+Rectangle {
+ id: musicBlock
+
+ property var player: null
+ property var supportedPlayers: ["feishin", "fooyin"]
+
+ implicitWidth: visible
+ ? Math.min(textMetrics.width + Config.blockPadding + 8, 400)
+ : 0
+
+ implicitHeight: Config.blockHeight
+
+ radius: Config.blockRadius
+
+ visible: player !== null && player.isPlaying
+
+ function isSupported(p) {
+ var identity = (p.identity || "").toLowerCase()
+ var desktopEntry = (p.desktopEntry || "").toLowerCase()
+
+ for (var i = 0; i < supportedPlayers.length; i++) {
+ var name = supportedPlayers[i]
+
+ if (
+ identity === name ||
+ desktopEntry === name ||
+ desktopEntry.endsWith("." + name)
+ )
+ return true
+ }
+
+ return false
+ }
+
+ function findPlayer() {
+ var fallback = null
+
+ for (var i = 0; i < Mpris.players.values.length; i++) {
+ var p = Mpris.players.values[i]
+
+ console.log(
+ "MPRIS:",
+ "identity =", p.identity,
+ "desktopEntry =", p.desktopEntry
+ )
+
+ if (!isSupported(p))
+ continue
+
+ if (p.isPlaying) {
+ player = p
+ return
+ }
+
+ if (!fallback)
+ fallback = p
+ }
+
+ player = fallback
+ }
+
+ function cleanTitle(title) {
+ if (!title)
+ return ""
+
+ return title
+ .replace(/\s*[\(\[][^)\]]*\blive\b[^)\]]*[\)\]]/gi, "")
+ .replace(/\s*[-–—]\s*\blive\b.*$/i, "")
+ .trim()
+ }
+
+ Component.onCompleted: {
+ findPlayer()
+ }
+
+ Connections {
+ target: Mpris.players
+
+ function onValuesChanged() {
+ findPlayer()
+ }
+ }
+
+ gradient: Gradient {
+ GradientStop {
+ id: stop1
+ position: 0.0
+ color: Config.accentGreen
+ }
+
+ GradientStop {
+ id: stop2
+ position: 0.33
+ color: Config.accentYellow
+ }
+
+ GradientStop {
+ id: stop3
+ position: 0.66
+ color: Config.accentOrange
+ }
+
+ GradientStop {
+ id: stop4
+ position: 1.0
+ color: Config.accentRed
+ }
+ }
+
+ TextMetrics {
+ id: textMetrics
+
+ font.family: Config.fontFamily
+ font.pixelSize: Config.fontSize
+ font.bold: true
+
+ text: player
+ ? "󰎇 " + player.trackArtist + " — " + cleanTitle(player.trackTitle)
+ : ""
+ }
+
+ Text {
+ id: musicText
+
+ anchors {
+ left: parent.left
+ right: parent.right
+ verticalCenter: parent.verticalCenter
+ }
+
+ leftPadding: Config.blockPadding / 2
+ rightPadding: Config.blockPadding / 2
+
+ text: player
+ ? "󰎇 " + player.trackArtist + " — " + cleanTitle(player.trackTitle)
+ : ""
+
+ color: Config.textDark
+
+ font.family: Config.fontFamily
+ font.pixelSize: Config.fontSize
+ font.bold: true
+
+ elide: Text.ElideRight
+ maximumLineCount: 1
+ }
+
+ MouseArea {
+ anchors.fill: parent
+
+ cursorShape: Qt.PointingHandCursor
+
+ onClicked: {
+ if (player && player.canRaise)
+ player.raise()
+ }
+ }
+
+ SequentialAnimation {
+ running: true
+ loops: Animation.Infinite
+
+ ParallelAnimation {
+ ColorAnimation {
+ target: stop1
+ property: "color"
+ from: Config.accentGreen
+ to: Config.accentRed
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop2
+ property: "color"
+ from: Config.accentYellow
+ to: Config.accentGreen
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop3
+ property: "color"
+ from: Config.accentOrange
+ to: Config.accentYellow
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop4
+ property: "color"
+ from: Config.accentRed
+ to: Config.accentOrange
+ duration: Config.gradientAnimationDuration
+ }
+ }
+
+ ParallelAnimation {
+ ColorAnimation {
+ target: stop1
+ property: "color"
+ from: Config.accentRed
+ to: Config.accentGreen
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop2
+ property: "color"
+ from: Config.accentGreen
+ to: Config.accentYellow
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop3
+ property: "color"
+ from: Config.accentYellow
+ to: Config.accentOrange
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop4
+ property: "color"
+ from: Config.accentOrange
+ to: Config.accentRed
+ duration: Config.gradientAnimationDuration
+ }
+ }
+ }
+}
diff --git a/quickshell/modules/bar/QuickControl.qml b/quickshell/modules/bar/QuickControl.qml
new file mode 100644
index 0000000..4b54d22
--- /dev/null
+++ b/quickshell/modules/bar/QuickControl.qml
@@ -0,0 +1,246 @@
+import QtQuick
+import QtQuick.Layouts
+import Quickshell
+import Quickshell.Services.SystemTray
+import "../../config.js" as Config
+
+Rectangle {
+ id: root
+
+ property bool expanded: false
+
+ implicitWidth: expanded
+ ? layout.implicitWidth + 24
+ : dotsContainer.implicitWidth + 24
+
+ implicitHeight: Config.blockHeight
+
+ radius: Config.blockRadius
+
+ gradient: Gradient {
+ GradientStop {
+ id: stop1
+ position: 0.0
+ color: Config.accentGreen
+ }
+
+ GradientStop {
+ id: stop2
+ position: 0.33
+ color: Config.accentYellow
+ }
+
+ GradientStop {
+ id: stop3
+ position: 0.66
+ color: Config.accentOrange
+ }
+
+ GradientStop {
+ id: stop4
+ position: 1.0
+ color: Config.accentRed
+ }
+ }
+
+ Behavior on implicitWidth {
+ NumberAnimation {
+ duration: 250
+ easing.type: Easing.OutCubic
+ }
+ }
+
+ RowLayout {
+ id: layout
+
+ anchors {
+ right: parent.right
+ verticalCenter: parent.verticalCenter
+ rightMargin: 12
+ }
+
+ spacing: 8
+
+ Repeater {
+ model: SystemTray.items
+
+ delegate: Item {
+ id: trayItem
+
+ required property var modelData
+
+ implicitWidth: 18
+ implicitHeight: 18
+
+ Layout.preferredWidth: 18
+ Layout.preferredHeight: 18
+
+ opacity: root.expanded ? 1 : 0
+
+ Behavior on opacity {
+ NumberAnimation {
+ duration: 200
+ easing.type: Easing.OutCubic
+ }
+ }
+
+ visible: opacity > 0
+
+ QsMenuAnchor {
+ id: menuAnchor
+
+ anchor.item: trayItem
+ menu: modelData.menu
+ }
+
+ Image {
+ anchors.fill: parent
+
+ source: modelData.icon
+ sourceSize.width: 18
+ sourceSize.height: 18
+ fillMode: Image.PreserveAspectFit
+ }
+
+ MouseArea {
+ anchors.fill: parent
+
+ enabled: root.expanded
+
+ acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton
+
+ cursorShape: Qt.PointingHandCursor
+
+ onClicked: function(mouse) {
+ if (mouse.button === Qt.RightButton) {
+ if (modelData.hasMenu)
+ menuAnchor.open()
+ return
+ }
+
+ if (mouse.button === Qt.MiddleButton) {
+ modelData.secondaryActivate()
+ return
+ }
+
+ if (modelData.onlyMenu && modelData.hasMenu)
+ menuAnchor.open()
+ else
+ modelData.activate()
+ }
+ }
+ }
+ }
+
+ Item {
+ id: dotsContainer
+
+ implicitWidth: 24
+ implicitHeight: Config.blockHeight
+
+ Layout.preferredWidth: 24
+ Layout.preferredHeight: Config.blockHeight
+ Layout.alignment: Qt.AlignVCenter
+
+ Text {
+ id: dots
+
+ anchors.centerIn: parent
+
+ text: "•••"
+
+ color: Config.textDark
+
+ font.family: Config.fontFamily
+ font.pixelSize: Config.fontSize
+ font.bold: true
+
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+
+ MouseArea {
+ anchors.fill: parent
+
+ cursorShape: Qt.PointingHandCursor
+
+ onClicked: {
+ root.expanded = !root.expanded
+ }
+ }
+ }
+ }
+ }
+
+ SequentialAnimation {
+ running: true
+ loops: Animation.Infinite
+
+ ParallelAnimation {
+ ColorAnimation {
+ target: stop1
+ property: "color"
+ from: Config.accentGreen
+ to: Config.accentRed
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop2
+ property: "color"
+ from: Config.accentYellow
+ to: Config.accentGreen
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop3
+ property: "color"
+ from: Config.accentOrange
+ to: Config.accentYellow
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop4
+ property: "color"
+ from: Config.accentRed
+ to: Config.accentOrange
+ duration: Config.gradientAnimationDuration
+ }
+ }
+
+ ParallelAnimation {
+ ColorAnimation {
+ target: stop1
+ property: "color"
+ from: Config.accentRed
+ to: Config.accentGreen
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop2
+ property: "color"
+ from: Config.accentGreen
+ to: Config.accentYellow
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop3
+ property: "color"
+ from: Config.accentYellow
+ to: Config.accentOrange
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop4
+ property: "color"
+ from: Config.accentOrange
+ to: Config.accentRed
+ duration: Config.gradientAnimationDuration
+ }
+ }
+ }
+}
diff --git a/quickshell/modules/bar/Time.qml b/quickshell/modules/bar/Time.qml
new file mode 100644
index 0000000..dfa3f0d
--- /dev/null
+++ b/quickshell/modules/bar/Time.qml
@@ -0,0 +1,144 @@
+import QtQuick
+import QtQuick.Layouts
+import Quickshell
+import "../../config.js" as Config
+
+Rectangle {
+ id: clockBlock
+
+ implicitWidth: timeText.contentWidth + Config.blockPadding
+ implicitHeight: Config.blockHeight
+
+ radius: Config.blockRadius
+
+ property bool alternateFormat: false
+
+ gradient: Gradient {
+ GradientStop {
+ id: stop1
+ position: 0.0
+ color: Config.accentGreen
+ }
+
+ GradientStop {
+ id: stop2
+ position: 0.33
+ color: Config.accentYellow
+ }
+
+ GradientStop {
+ id: stop3
+ position: 0.66
+ color: Config.accentOrange
+ }
+
+ GradientStop {
+ id: stop4
+ position: 1.0
+ color: Config.accentRed
+ }
+ }
+
+ SystemClock {
+ id: clock
+ precision: SystemClock.Seconds
+ }
+
+ Text {
+ id: timeText
+
+ anchors.centerIn: parent
+
+ text: alternateFormat
+ ? Qt.formatDateTime(clock.date, "󱑃 dddd, dd MMMM yyyy")
+ : Qt.formatDateTime(clock.date, " hh:mm AP • ddd, dd/MM")
+
+ color: Config.textDark
+ font.family: Config.fontFamily
+ font.pixelSize: Config.fontSize
+ font.bold: true
+ }
+
+ MouseArea {
+ anchors.fill: parent
+
+ cursorShape: Qt.PointingHandCursor
+
+ onClicked: {
+ alternateFormat = !alternateFormat
+ }
+ }
+
+ SequentialAnimation {
+ running: true
+ loops: Animation.Infinite
+
+ ParallelAnimation {
+ ColorAnimation {
+ target: stop1
+ property: "color"
+ from: Config.accentGreen
+ to: Config.accentRed
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop2
+ property: "color"
+ from: Config.accentYellow
+ to: Config.accentGreen
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop3
+ property: "color"
+ from: Config.accentOrange
+ to: Config.accentYellow
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop4
+ property: "color"
+ from: Config.accentRed
+ to: Config.accentOrange
+ duration: Config.gradientAnimationDuration
+ }
+ }
+
+ ParallelAnimation {
+ ColorAnimation {
+ target: stop1
+ property: "color"
+ from: Config.accentRed
+ to: Config.accentGreen
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop2
+ property: "color"
+ from: Config.accentGreen
+ to: Config.accentYellow
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop3
+ property: "color"
+ from: Config.accentYellow
+ to: Config.accentOrange
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop4
+ property: "color"
+ from: Config.accentOrange
+ to: Config.accentRed
+ duration: Config.gradientAnimationDuration
+ }
+ }
+ }
+}
diff --git a/quickshell/modules/bar/Workspaces.qml b/quickshell/modules/bar/Workspaces.qml
new file mode 100644
index 0000000..c9c8104
--- /dev/null
+++ b/quickshell/modules/bar/Workspaces.qml
@@ -0,0 +1,177 @@
+import QtQuick
+import QtQuick.Layouts
+import Quickshell
+import "../../config.js" as Config
+
+Rectangle {
+ id: workspaceBlock
+
+ implicitWidth: workspaceRow.width + 16
+ implicitHeight: Config.blockHeight
+
+ color: "transparent"
+
+ RowLayout {
+ id: workspaceRow
+
+ anchors.verticalCenter: parent.verticalCenter
+ spacing: Config.workspaceSpacing
+
+ Repeater {
+ model: niri.workspaces
+
+ delegate: Rectangle {
+ id: workspace
+
+ visible: model.output === Config.barOutput && index < 11
+
+ implicitWidth: Config.workspaceWidth
+ implicitHeight: Config.blockHeight
+
+ radius: Config.blockRadius
+
+ color: model.isActive
+ ? "transparent"
+ : Config.workspaceBackground
+
+ layer.enabled: true
+
+ Rectangle {
+ anchors.fill: parent
+ radius: parent.radius
+ visible: model.isActive
+
+ gradient: Gradient {
+ GradientStop {
+ id: stop1
+ position: 0.0
+ color: Config.accentGreen
+ }
+
+ GradientStop {
+ id: stop2
+ position: 0.33
+ color: Config.accentYellow
+ }
+
+ GradientStop {
+ id: stop3
+ position: 0.66
+ color: Config.accentOrange
+ }
+
+ GradientStop {
+ id: stop4
+ position: 1.0
+ color: Config.accentRed
+ }
+ }
+
+ SequentialAnimation {
+ running: true
+ loops: Animation.Infinite
+
+ ParallelAnimation {
+ ColorAnimation {
+ target: stop1
+ property: "color"
+ from: Config.accentGreen
+ to: Config.accentRed
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop2
+ property: "color"
+ from: Config.accentYellow
+ to: Config.accentGreen
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop3
+ property: "color"
+ from: Config.accentOrange
+ to: Config.accentYellow
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop4
+ property: "color"
+ from: Config.accentRed
+ to: Config.accentOrange
+ duration: Config.gradientAnimationDuration
+ }
+ }
+
+ ParallelAnimation {
+ ColorAnimation {
+ target: stop1
+ property: "color"
+ from: Config.accentRed
+ to: Config.accentGreen
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop2
+ property: "color"
+ from: Config.accentGreen
+ to: Config.accentYellow
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop3
+ property: "color"
+ from: Config.accentYellow
+ to: Config.accentOrange
+ duration: Config.gradientAnimationDuration
+ }
+
+ ColorAnimation {
+ target: stop4
+ property: "color"
+ from: Config.accentOrange
+ to: Config.accentRed
+ duration: Config.gradientAnimationDuration
+ }
+ }
+ }
+ }
+
+ Item {
+ anchors.fill: parent
+
+ Text {
+ anchors.fill: parent
+
+ text: index
+
+ color: model.isActive
+ ? Config.textDark
+ : Config.textInactive
+
+ font.family: Config.fontFamily
+ font.pixelSize: Config.fontSize
+ font.bold: true
+
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+ }
+
+ MouseArea {
+ anchors.fill: parent
+
+ cursorShape: Qt.PointingHandCursor
+
+ onClicked: {
+ niri.focusWorkspaceById(model.id)
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/quickshell/modules/desktop/Wallpaper.qml b/quickshell/modules/desktop/Wallpaper.qml
new file mode 100644
index 0000000..c956862
--- /dev/null
+++ b/quickshell/modules/desktop/Wallpaper.qml
@@ -0,0 +1,180 @@
+import QtQuick
+import QtQuick.Effects
+import Quickshell
+import Quickshell.Io
+import Quickshell.Wayland
+import "../../config.js" as Config
+
+Variants {
+ model: Quickshell.screens
+
+ PanelWindow {
+ id: win
+
+ required property var modelData
+
+ property var wallpaper: Config.wallpapers.find(function(w) {
+ return w.outputs.indexOf(modelData.name) !== -1
+ })
+
+ property string currentPath: wallpaper ? wallpaper.path : ""
+ property string previousPath: ""
+ property real reveal: 1
+ property bool started: false
+ property bool initialized: false
+ property int direction: 0
+
+ readonly property bool horizontal: direction < 2
+ readonly property bool fromStart: direction % 2 === 0
+
+ readonly property int fillValue: wallpaper ? Image[wallpaper.fillMode] : Image.PreserveAspectCrop
+
+ readonly property bool ready: initialized && currentPath !== "" && newImage.status === Image.Ready && (previousPath === "" || oldImage.status === Image.Ready)
+
+ screen: modelData
+ visible: wallpaper !== undefined
+ color: "#000000"
+
+ anchors {
+ top: true
+ bottom: true
+ left: true
+ right: true
+ }
+
+ exclusionMode: ExclusionMode.Ignore
+
+ WlrLayershell.layer: WlrLayer.Background
+ WlrLayershell.namespace: "wallpaper"
+
+ FileView {
+ id: stateFile
+
+ path: Quickshell.env("HOME") + "/.cache/qs-wallpaper-" + win.modelData.name
+ blockLoading: true
+ printErrors: false
+ }
+
+ function pickDirection() {
+ const names = ["left", "right", "top", "bottom"]
+ const wanted = Config.wallpaperWipeDirection ?? "random"
+ const index = names.indexOf(wanted)
+
+ if (index !== -1)
+ return index
+
+ return Math.floor(Math.random() * 4)
+ }
+
+ function begin() {
+ if (started || currentPath === "")
+ return
+
+ started = true
+
+ if (reveal < 1)
+ wipe.start()
+ else
+ finish()
+ }
+
+ function finish() {
+ previousPath = currentPath
+ stateFile.setText(currentPath)
+ }
+
+ onReadyChanged: {
+ if (ready)
+ begin()
+ }
+
+ Component.onCompleted: {
+ let saved = ""
+
+ try {
+ saved = stateFile.text().trim()
+ } catch (e) {
+ saved = ""
+ }
+
+ previousPath = saved
+ direction = pickDirection()
+ reveal = (saved !== currentPath) ? 0 : 1
+ initialized = true
+
+ if (ready)
+ begin()
+ }
+
+ NumberAnimation {
+ id: wipe
+
+ target: win
+ property: "reveal"
+ from: 0
+ to: 1
+ duration: Config.wallpaperWipeDuration ?? 700
+ easing.type: Easing.InOutCubic
+
+ onFinished: win.finish()
+ }
+
+ Item {
+ id: stack
+
+ anchors.fill: parent
+ opacity: win.wallpaper ? win.wallpaper.opacity : 1.0
+
+ readonly property real clipWidth: win.horizontal ? (width * win.reveal) : width
+ readonly property real clipHeight: win.horizontal ? height : (height * win.reveal)
+ readonly property real clipX: (win.horizontal && !win.fromStart) ? (width - clipWidth) : 0
+ readonly property real clipY: (!win.horizontal && !win.fromStart) ? (height - clipHeight) : 0
+
+ Image {
+ id: oldImage
+
+ anchors.fill: parent
+ clip: true
+
+ source: win.initialized ? win.previousPath : ""
+ fillMode: win.fillValue
+ asynchronous: win.wallpaper ? win.wallpaper.asynchronous : true
+ cache: win.wallpaper ? win.wallpaper.cache : true
+ }
+
+ Item {
+ id: revealClip
+
+ x: stack.clipX
+ y: stack.clipY
+ width: stack.clipWidth
+ height: stack.clipHeight
+ clip: true
+
+ Image {
+ id: newImage
+
+ x: 0 - stack.clipX
+ y: 0 - stack.clipY
+ width: stack.width
+ height: stack.height
+ clip: true
+
+ source: win.initialized ? win.currentPath : ""
+ fillMode: win.fillValue
+ asynchronous: win.wallpaper ? win.wallpaper.asynchronous : true
+ cache: win.wallpaper ? win.wallpaper.cache : true
+ }
+ }
+ }
+
+ MultiEffect {
+ anchors.fill: stack
+
+ source: stack
+
+ blurEnabled: win.wallpaper ? win.wallpaper.blur > 0 : false
+ blur: win.wallpaper ? win.wallpaper.blur : 0
+ }
+ }
+}
diff --git a/quickshell/modules/notifications/NotificationCard.qml b/quickshell/modules/notifications/NotificationCard.qml
new file mode 100644
index 0000000..983cbd7
--- /dev/null
+++ b/quickshell/modules/notifications/NotificationCard.qml
@@ -0,0 +1,257 @@
+import QtQuick
+import QtQuick.Layouts
+import Quickshell.Services.Notifications
+import "../../config.js" as Config
+
+Rectangle {
+ id: root
+
+ required property var notification
+
+ signal activated()
+
+ property bool dismissing: false
+
+ Layout.fillWidth: true
+
+ implicitHeight: body.implicitHeight + 20
+
+ radius: Config.blockRadius
+
+ color: Config.barBackground
+
+ border.width: 1
+ border.color: Config.notificationBorder
+
+ x: dismissing ? Config.notificationSlideDistance : 0
+ opacity: dismissing ? 0 : 1
+
+ Component.onCompleted: {
+ x = Config.notificationSlideDistance
+ opacity = 0
+
+ enterAnimation.start()
+
+ if (notification.urgency !== NotificationUrgency.Critical)
+ dismissTimer.start()
+ }
+
+ Timer {
+ id: dismissTimer
+
+ interval: Config.notificationTimeout
+ repeat: false
+
+ onTriggered: {
+ dismiss()
+ }
+ }
+
+ function dismiss() {
+ if (dismissing)
+ return
+
+ dismissTimer.stop()
+ dismissing = true
+ exitAnimation.start()
+ }
+
+ SequentialAnimation {
+ id: enterAnimation
+
+ ParallelAnimation {
+ NumberAnimation {
+ target: root
+ property: "x"
+ from: Config.notificationSlideDistance
+ to: 0
+ duration: Config.notificationAnimationDuration
+ easing.type: Easing.OutCubic
+ }
+
+ NumberAnimation {
+ target: root
+ property: "opacity"
+ from: 0
+ to: 1
+ duration: Config.notificationAnimationDuration
+ easing.type: Easing.OutCubic
+ }
+ }
+ }
+
+ SequentialAnimation {
+ id: exitAnimation
+
+ ParallelAnimation {
+ NumberAnimation {
+ target: root
+ property: "x"
+ to: Config.notificationSlideDistance
+ duration: Config.notificationAnimationDuration
+ easing.type: Easing.InCubic
+ }
+
+ NumberAnimation {
+ target: root
+ property: "opacity"
+ to: 0
+ duration: Config.notificationAnimationDuration
+ easing.type: Easing.InCubic
+ }
+ }
+
+ ScriptAction {
+ script: root.notification.expire()
+ }
+ }
+
+ ColumnLayout {
+ id: body
+
+ anchors.fill: parent
+ anchors.margins: 10
+
+ spacing: 6
+
+ RowLayout {
+ Layout.fillWidth: true
+
+ Image {
+ visible: root.notification.appIcon !== ""
+
+ source: root.notification.appIcon
+
+ sourceSize.width: 24
+ sourceSize.height: 24
+
+ Layout.preferredWidth: 24
+ Layout.preferredHeight: 24
+
+ fillMode: Image.PreserveAspectFit
+ asynchronous: true
+ }
+
+ Text {
+ Layout.fillWidth: true
+
+ text: root.notification.appName || "Notification"
+
+ color: Config.textPrimary
+
+ font.family: Config.fontFamily
+ font.pixelSize: 12
+ font.bold: true
+
+ elide: Text.ElideRight
+ }
+
+ Text {
+ text: "×"
+
+ color: "#6c7086"
+
+ font.family: Config.fontFamily
+ font.pixelSize: 18
+ font.bold: true
+
+ MouseArea {
+ anchors.fill: parent
+
+ cursorShape: Qt.PointingHandCursor
+
+ onClicked: {
+ root.dismiss()
+ }
+ }
+ }
+ }
+
+ Text {
+ Layout.fillWidth: true
+
+ text: root.notification.summary
+
+ color: Config.textPrimary
+
+ font.family: Config.fontFamily
+ font.pixelSize: Config.fontSize
+ font.bold: true
+
+ wrapMode: Text.Wrap
+ }
+
+ Text {
+ visible: root.notification.body !== ""
+
+ Layout.fillWidth: true
+
+ text: root.notification.body
+
+ color: Config.textSecondary
+
+ font.family: Config.fontFamily
+ font.pixelSize: 12
+
+ wrapMode: Text.Wrap
+
+ maximumLineCount: 4
+ elide: Text.ElideRight
+ }
+
+ Rectangle {
+ Layout.fillWidth: true
+ Layout.preferredHeight: 3
+
+ radius: 2
+
+ gradient: Gradient {
+ GradientStop {
+ position: 0.0
+ color: Config.accentGreen
+ }
+
+ GradientStop {
+ position: 0.33
+ color: Config.accentYellow
+ }
+
+ GradientStop {
+ position: 0.66
+ color: Config.accentOrange
+ }
+
+ GradientStop {
+ position: 1.0
+ color: Config.accentRed
+ }
+ }
+ }
+ }
+
+ MouseArea {
+ anchors.fill: parent
+
+ cursorShape: Qt.PointingHandCursor
+
+ onClicked: {
+ root.activated()
+ }
+ }
+
+ MouseArea {
+ anchors.right: parent.right
+ anchors.top: parent.top
+
+ width: 36
+ height: 36
+
+ z: 10
+
+ cursorShape: Qt.PointingHandCursor
+
+ onClicked: {
+ root.dismiss()
+ }
+ }
+}
diff --git a/quickshell/modules/notifications/NotificationFocus.qml b/quickshell/modules/notifications/NotificationFocus.qml
new file mode 100644
index 0000000..ce59b2a
--- /dev/null
+++ b/quickshell/modules/notifications/NotificationFocus.qml
@@ -0,0 +1,239 @@
+import QtQuick
+import Quickshell
+import Quickshell.Io
+
+Item {
+ id: root
+
+ property string pendingApp: ""
+
+ function normalize(value) {
+ if (!value)
+ return ""
+
+ var result = String(value).toLowerCase().trim()
+
+ result = result.replace(/^org\./, "")
+ result = result.replace(/^com\./, "")
+ result = result.replace(/^net\./, "")
+ result = result.replace(/^app\./, "")
+
+ result = result.replace(/\.desktop$/, "")
+ result = result.replace(/\.desktop$/i, "")
+
+ result = result.replace(/[^a-z0-9]+/g, "")
+
+ return result
+ }
+
+ function candidates(value) {
+ var result = []
+ var raw = String(value || "").toLowerCase().trim()
+
+ function add(candidate) {
+ if (!candidate)
+ return
+
+ candidate = normalize(candidate)
+
+ if (candidate && result.indexOf(candidate) === -1)
+ result.push(candidate)
+ }
+
+ add(raw)
+
+ if (raw.indexOf(".") !== -1) {
+ var parts = raw.split(".")
+
+ add(parts[parts.length - 1])
+
+ for (var i = 0; i < parts.length; i++)
+ add(parts[i])
+ }
+
+ if (raw.indexOf("/") !== -1)
+ add(raw.split("/").pop())
+
+ if (raw.indexOf("-") !== -1) {
+ var dashParts = raw.split("-")
+
+ for (var j = 0; j < dashParts.length; j++)
+ add(dashParts[j])
+ }
+
+ if (raw.indexOf("_") !== -1) {
+ var underscoreParts = raw.split("_")
+
+ for (var k = 0; k < underscoreParts.length; k++)
+ add(underscoreParts[k])
+ }
+
+ return result
+ }
+
+ function matchScore(notificationApp, windowAppId, windowTitle) {
+ var notificationCandidates = candidates(notificationApp)
+ var windowCandidates = candidates(windowAppId)
+
+ var best = 0
+
+ for (var i = 0; i < notificationCandidates.length; i++) {
+ var notificationCandidate = notificationCandidates[i]
+
+ for (var j = 0; j < windowCandidates.length; j++) {
+ var windowCandidate = windowCandidates[j]
+
+ if (notificationCandidate === windowCandidate)
+ best = Math.max(best, 100)
+
+ else if (
+ notificationCandidate.length >= 4 &&
+ windowCandidate.length >= 4 &&
+ (
+ notificationCandidate.indexOf(windowCandidate) !== -1 ||
+ windowCandidate.indexOf(notificationCandidate) !== -1
+ )
+ ) {
+ best = Math.max(best, 60)
+ }
+ }
+ }
+
+ var titleCandidates = candidates(windowTitle)
+
+ for (var n = 0; n < notificationCandidates.length; n++) {
+ for (var t = 0; t < titleCandidates.length; t++) {
+ if (
+ notificationCandidates[n].length >= 4 &&
+ notificationCandidates[n] === titleCandidates[t]
+ ) {
+ best = Math.max(best, 30)
+ }
+ }
+ }
+
+ return best
+ }
+
+ Process {
+ id: windowsProcess
+
+ command: ["niri", "msg", "-j", "windows"]
+
+ stdout: StdioCollector {
+ id: windowsOutput
+
+ onStreamFinished: {
+ var windows
+
+ try {
+ windows = JSON.parse(this.text)
+ } catch (error) {
+ console.error(
+ "Failed to parse Niri windows:",
+ error
+ )
+
+ root.pendingApp = ""
+ return
+ }
+
+ var notificationApp = root.pendingApp
+ root.pendingApp = ""
+
+ if (!notificationApp) {
+ console.warn("Notification focus: empty app name")
+ return
+ }
+
+ if (!Array.isArray(windows)) {
+ console.error(
+ "Niri windows response is not an array"
+ )
+ return
+ }
+
+ var bestWindow = null
+ var bestScore = 0
+
+ for (var i = 0; i < windows.length; i++) {
+ var window = windows[i]
+
+ if (!window || window.id === undefined)
+ continue
+
+ var appId = window.app_id || ""
+ var title = window.title || ""
+
+ var score = matchScore(
+ notificationApp,
+ appId,
+ title
+ )
+
+ if (score > bestScore) {
+ bestScore = score
+ bestWindow = window
+ }
+ }
+
+ if (!bestWindow) {
+ console.log(
+ "No matching Niri window found:",
+ notificationApp
+ )
+ return
+ }
+
+ console.log(
+ "FOCUSING WINDOW:",
+ bestWindow.id,
+ "notification app:",
+ notificationApp,
+ "app_id:",
+ bestWindow.app_id,
+ "title:",
+ bestWindow.title,
+ "score:",
+ bestScore
+ )
+
+ focusProcess.exec([
+ "niri",
+ "msg",
+ "action",
+ "focus-window",
+ "--id",
+ String(bestWindow.id)
+ ])
+ }
+ }
+ }
+
+ Process {
+ id: focusProcess
+ }
+
+ function focusSource(notification) {
+ if (!notification)
+ return
+
+ var app = notification.appName || ""
+
+ if (!app) {
+ console.warn(
+ "Notification has no application name"
+ )
+ return
+ }
+
+ root.pendingApp = app
+
+ windowsProcess.exec([
+ "niri",
+ "msg",
+ "-j",
+ "windows"
+ ])
+ }
+}
diff --git a/quickshell/modules/notifications/Notifications.qml b/quickshell/modules/notifications/Notifications.qml
new file mode 100644
index 0000000..2965b90
--- /dev/null
+++ b/quickshell/modules/notifications/Notifications.qml
@@ -0,0 +1,125 @@
+import QtQuick
+import QtQuick.Layouts
+import Quickshell
+import Quickshell.Wayland
+import Quickshell.Services.Notifications
+import "../../config.js" as Config
+import "./"
+
+Item {
+ id: root
+
+ NotificationServer {
+ id: server
+
+ bodySupported: true
+ imageSupported: true
+ actionsSupported: true
+ actionIconsSupported: true
+ bodyMarkupSupported: true
+
+ onNotification: function(notification) {
+ console.log("NOTIFICATION")
+ console.log(" app:", notification.appName)
+ console.log(" summary:", notification.summary)
+ console.log(" body:", notification.body)
+ console.log(" actions:", notification.actions.length)
+
+ for (var i = 0; i < notification.actions.length; i++) {
+ console.log(
+ " action:",
+ notification.actions[i].identifier,
+ notification.actions[i].text
+ )
+ }
+
+ notification.tracked = true
+ }
+ }
+
+ NotificationFocus {
+ id: focus
+ }
+
+ function activateNotification(notification) {
+ if (!notification)
+ return
+
+ console.log(
+ "ACTIVATE:",
+ notification.appName,
+ notification.summary
+ )
+
+ focus.focusSource(notification)
+
+ for (var i = 0; i < notification.actions.length; i++) {
+ var action = notification.actions[i]
+
+ if (action.identifier === "default") {
+ console.log(
+ "Invoking default action:",
+ action.text
+ )
+
+ action.invoke()
+ return
+ }
+ }
+
+ console.log("No default action available")
+ notification.dismiss()
+ }
+
+ PanelWindow {
+ id: window
+
+ screen: Quickshell.screens.find(function(screen) {
+ return screen.name === Config.barOutput
+ })
+
+ anchors {
+ top: true
+ right: true
+ }
+
+ margins {
+ top: Config.notificationMarginTop
+ right: Config.notificationMarginRight
+ }
+
+ exclusionMode: ExclusionMode.Ignore
+
+ WlrLayershell.layer: WlrLayer.Overlay
+ WlrLayershell.namespace: "notifications"
+
+ color: "transparent"
+
+ implicitWidth: Config.notificationWidth
+ implicitHeight: list.implicitHeight
+
+ ColumnLayout {
+ id: list
+
+ anchors.top: parent.top
+ anchors.right: parent.right
+
+ width: Config.notificationWidth
+ spacing: Config.notificationSpacing
+
+ Repeater {
+ model: server.trackedNotifications
+
+ delegate: NotificationCard {
+ required property var modelData
+
+ notification: modelData
+
+ onActivated: {
+ root.activateNotification(modelData)
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/quickshell/shell.qml b/quickshell/shell.qml
new file mode 100644
index 0000000..6e9ca84
--- /dev/null
+++ b/quickshell/shell.qml
@@ -0,0 +1,63 @@
+//@ pragma UseQApplication
+
+import QtQuick
+import QtQuick.Layouts
+import Quickshell
+import Quickshell.Io
+import Quickshell.Wayland
+import Niri
+import "./modules/bar/"
+import "./modules/desktop/"
+import "./modules/notifications/"
+
+ShellRoot {
+ id: root
+
+ Niri {
+ id: niri
+ Component.onCompleted: connect()
+
+ onConnected: console.info("Connected to niri")
+ onErrorOccurred: function(error) {
+ console.error("Niri error:", error)
+ }
+ }
+
+ LazyLoader {
+ id: wallLoader
+ active: true
+ component: Wallpaper {}
+ }
+
+ Timer {
+ id: wallTimer
+ interval: 100
+ onTriggered: wallLoader.active = true
+ }
+
+ Variants {
+ id: barVariants
+
+ model: Quickshell.screens.filter(s => s.name === "HDMI-A-1")
+
+ Bar {}
+ }
+
+ IpcHandler {
+ target: "shell"
+
+ function restartBar(): void {
+ for (const b of barVariants.instances)
+ b.restart()
+ }
+
+ function restartWall(): void {
+ Quickshell.reload(false)
+ }
+ }
+
+ LazyLoader {
+ active: true
+ component: Notifications {}
+ }
+}
diff --git a/zathura/zathurarc b/zathura/zathurarc
new file mode 100644
index 0000000..7b73965
--- /dev/null
+++ b/zathura/zathurarc
@@ -0,0 +1,4 @@
+set recolor true
+set recolor-keephue false
+set recolor-lightcolor "rgba(0,0,0,0)"
+set recolor-darkcolor "#cccccc"