Compare commits

..
18 Commits
25 changed files with 1327 additions and 1750 deletions
+12 -2
View File
@@ -1,11 +1,21 @@
# emacs
config/emacs/.ignore
config/emacs/var/
config/emacs/elfeed/
config/emacs/eshell/
config/emacs/auto-save-list/
# fish
config/fish/conf.d
config/fish/functions
config/fish/fish_variables
config/fish/generated_completions/
# vim/nvim
config/vim/autoload/
config/vim/pack/
config/vim/.netrwhist
config/nvim/nvim-pack-lock.json
config/awesome/bling
scripts/bin/Obsidian
# other
scripts/bin/ols
scripts/bin/deno
scripts/bin/yt-dlp
scripts/bin/tiled
+36
View File
@@ -0,0 +1,36 @@
;;; early-init.el --- Pre-init: GC ceiling and frame chrome -*- lexical-binding: t; -*-
;;; Commentary:
;; Runs before package.el initialises and first frame is drawn, the only place these take effect:
;; 1. Raising the GC ceiling, so the whole startup (package load + init.el)
;; runs without garbage-collection pauses.
;; 2. Removing the menu/tool/scroll bars via `default-frame-alist', so they
;; are never drawn — toggling the minor modes in init.el instead draws
;; them and then removes them, a visible flicker on launch.
;;
;; Nothing here may depend on a package: none are loaded yet.
;;; Code:
(startup-redirect-eln-cache (expand-file-name "var/eln-cache/" user-emacs-directory))
(setq package-user-dir (expand-file-name "var/elpa/" user-emacs-directory))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Garbage Collection
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Hold GC off for the whole startup; init.el lowers it back on
;; `emacs-startup-hook' (see the Commentary above).
(setq gc-cons-threshold most-positive-fixnum)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Frame Chrome (no flicker)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Disable the bars before the first frame exists.
(push '(menu-bar-lines . 0) default-frame-alist)
(push '(tool-bar-lines . 0) default-frame-alist)
(push '(vertical-scroll-bars) default-frame-alist)
;; Don't resize the frame when the UI/fonts change during startup.
(setq frame-inhibit-implied-resize t)
;;; early-init.el ends here
+235
View File
@@ -0,0 +1,235 @@
;;; init.el --- Minimal Emacs: built-ins + evil + consult -*- lexical-binding: t; -*-
;; Learning: F1 k KEY, F1 f FUNCTION, F1 v VARIABLE, F1 m (modes), F1 b (bindings).
;; C-h works too, except in normal/motion state where it moves to the window on the left.
;;; Code:
;;;; 1. Emac setup, gc, files, packages
(add-hook 'emacs-startup-hook
(lambda () (setq gc-cons-threshold (* 16 1024 1024))))
(dolist (bin (list (expand-file-name "~/.local/bin")
(expand-file-name "~/.go/bin")))
(add-to-list 'exec-path bin)
(setenv "PATH" (concat bin path-separator (getenv "PATH"))))
;; Add the themes directory to the load path
(add-to-list 'custom-theme-load-path (expand-file-name "themes" user-emacs-directory))
;; all the emacs runtime files live in var/.
;; early-init.el redirects eln-cache and elpa.
(defvar my/var (locate-user-emacs-file "var/"))
(make-directory (concat my/var "transient/") t)
(setq savehist-file (concat my/var "history")
save-place-file (concat my/var "places.eld")
recentf-save-file (concat my/var "recentf.eld")
project-list-file (concat my/var "projects.eld")
transient-history-file (concat my/var "transient/history.el")
transient-levels-file (concat my/var "transient/levels.el")
transient-values-file (concat my/var "transient/values.el"))
;; Custom output goes to its own file, not here.
(setq custom-file (expand-file-name "var/custom.el" user-emacs-directory))
(load custom-file 'noerror 'nomessage)
;; No session manager save: stops the empty session.* files.
(add-hook 'emacs-save-session-functions #'always)
;; Package GNU, NonGNU are built in melpa additional lowest priority.
(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(setq package-archive-priorities '(("gnu" . 3) ("nongnu" . 2) ("melpa" . 1)))
(package-initialize)
(require 'use-package)
(setq use-package-always-ensure t)
;;;; 2. Ui settings, theme setup and fonts
(add-to-list 'default-frame-alist '(fullscreen . maximized))
(menu-bar-mode -1)
(tool-bar-mode -1)
(scroll-bar-mode -1)
(blink-cursor-mode -1)
(setq inhibit-startup-screen t
ring-bell-function #'ignore
use-short-answers t
scroll-conservatively 101
scroll-margin 20)
(setq-default indent-tabs-mode nil tab-width 4)
(global-hl-line-mode 1)
(global-display-line-numbers-mode 1)
(pixel-scroll-precision-mode 1)
(column-number-mode 1)
(electric-pair-mode 1)
(which-key-mode 1)
;; Load the compline theme
(load-theme 'minimal t)
;; set mode line at the top of each window
(setq-default header-line-format mode-line-format mode-line-format nil)
(set-face-attribute 'default nil :family "JetBrainsMono Nerd Font" :height 120)
(setq-default line-spacing '(4 . 4))
;; Command launcher.
(keymap-global-set "C-S-p" #'execute-extended-command)
;; Escape behaves like C-g
(keymap-global-set "<escape>" #'keyboard-quit)
(keymap-set minibuffer-local-map "<escape>" #'abort-minibuffers)
;;;; 3. Files
(setq make-backup-files nil
auto-save-default nil
auto-save-list-file-prefix nil
create-lockfiles nil
confirm-kill-processes nil
vc-follow-symlinks t)
(save-place-mode 1)
(savehist-mode 1)
(recentf-mode 1)
(global-auto-revert-mode 1)
;;;; 4. Completion
;; C-n/C-p navigates up and down, section 5 stops evil from overrriding
(use-package vertico
:init
(vertico-mode 1)
:custom
(vertico-count 15))
(setq completion-styles '(basic partial-completion flex)
completion-ignore-case t
read-buffer-completion-ignore-case t
read-file-name-completion-ignore-case t
completions-format 'one-column
isearch-lazy-count t
xref-search-program 'ripgrep)
(use-package vertico-posframe
:after vertico
:custom
(vertico-posframe-poshandler #'posframe-poshandler-frame-center)
:config
(vertico-posframe-mode 1))
;;;; 5. Evil
(defvar evil-mode-buffers nil)
(defun my/insert-tab ()
"Insert a literal tab character."
(interactive)
(insert "\t"))
(use-package evil
:init
(setq evil-want-C-u-scroll t
evil-undo-system 'undo-redo
evil-want-keybinding nil)
:config
(evil-mode 1)
(evil-define-key 'insert 'global
(kbd "TAB") #'my/insert-tab
(kbd "C-SPC") #'completion-at-point)
;; C-p and C-f are assigned to Consult below, C-m to ibuffer.
(evil-define-key '(normal motion) 'global
(kbd "C-e") #'dired-jump))
(use-package evil-collection
:after evil
:custom
;; Don't add its global [b ]b [e ]e ... bindings.
(evil-collection-want-unimpaired-p nil)
:config
(evil-collection-init '(info ibuffer)))
;; Minibuffer. Vertico
(with-eval-after-load 'vertico
(evil-define-key '(insert normal) vertico-map
(kbd "C-n") #'vertico-next
(kbd "C-p") #'vertico-previous
(kbd "TAB") #'vertico-insert
(kbd "<tab>") #'vertico-insert))
(evil-define-key '(insert normal) minibuffer-local-map
(kbd "<escape>") #'abort-minibuffers)
;; Result buffers: hjkl motions and RET to jump to a result.
(dolist (m '(occur-mode grep-mode xref--xref-buffer-mode))
(evil-set-initial-state m 'motion))
(with-eval-after-load 'compile
(evil-define-key 'motion compilation-mode-map
(kbd "RET") #'compile-goto-error))
(with-eval-after-load 'grep
(evil-define-key 'motion grep-mode-map
(kbd "RET") #'compile-goto-error))
(with-eval-after-load 'replace
(evil-define-key 'motion occur-mode-map
(kbd "RET") #'occur-mode-goto-occurrence))
(with-eval-after-load 'xref
(evil-define-key 'motion xref--xref-buffer-mode-map
(kbd "RET") #'xref-goto-xref))
;;;; 6. Consult
;; Requires the external `fd` and `rg` executables. Both honour .ignore files,
;; so keep excludes (node_modules, elpa, ...) in ~/.ignore or per-project .ignore.
;; Searches start at the project root when there is one, else the current directory.
(use-package consult
:config
;; Don't re-run the preview on every keystroke while typing a search.
(consult-customize consult-ripgrep
:preview-key '(:debounce 0.4 any)))
;; Bind immediately, without waiting for consult to load.
;; C-m opens the full ibuffer list (evil-collection provides its navigation).
(evil-define-key '(normal motion) 'global
(kbd "C-p") #'consult-fd
(kbd "C-f") #'consult-ripgrep
(kbd "C-m") #'ibuffer)
;;;; 7. Dired
(setq dired-dwim-target t
dired-kill-when-opening-new-dired-buffer t
dired-listing-switches "-agho --group-directories-first"
dired-recursive-copies 'always
delete-by-moving-to-trash t)
(with-eval-after-load 'dired
(require 'dired-x)
(setq dired-omit-files "\\`\\.")
(add-hook 'dired-mode-hook #'dired-omit-mode)
(evil-set-initial-state 'dired-mode 'motion)
(evil-define-key 'motion dired-mode-map
"h" #'dired-up-directory
"l" #'dired-find-file
"." #'dired-omit-mode
(kbd "RET") #'dired-find-file))
;;;; Core config ends here.
;;;; -----------------------------------------------------------------
;;;; 8. Modules - additional functionality outside of the core init.el
;; Setup for orgmode
(load (locate-user-emacs-file "modules/org.el") nil 'nomessage)
;; Magit and the :git command live in modules/git.el.
(load (locate-user-emacs-file "modules/git.el") nil 'nomessage)
;; Setup for personal build commands and system.
(load (locate-user-emacs-file "modules/build.el") nil 'nomessage)
;; Setup for dev languages, modes, lsp and lsp keybinds.
(load (locate-user-emacs-file "modules/dev.el") nil 'nomessage)
;;; init.el ends here
+47
View File
@@ -0,0 +1,47 @@
;;; build.el --- Project build, set up for Evil -*- lexical-binding: t; -*-
;;; Commentary:
;; Loaded from init.el after Evil.
;;
;; :build Run ./build.sh from the nearest ancestor dir containing it.
;; :build ARGS Run ./build.sh ARGS, e.g. :build release -v
;;
;; If no build.sh is found (or it isn't executable), report it and do nothing.
;;
;; The compilation buffer opens at the bottom (45% height, full width) and
;; takes focus when the build starts. ANSI colours are rendered.
;; Kill a running build with M-x kill-compilation.
;;; Code:
(setq compilation-read-command nil ; run without prompting
compilation-ask-about-save nil
compilation-scroll-output t)
(add-hook 'compilation-filter-hook #'ansi-color-compilation-filter)
;; Compilation buffer at the bottom, full frame width.
(add-to-list 'display-buffer-alist
'((derived-mode . compilation-mode)
(display-buffer-reuse-window display-buffer-at-bottom)
(window-height . 0.45)))
;; Focus the compilation window when a build starts.
(add-hook 'compilation-start-hook
(lambda (proc)
(when-let* ((win (get-buffer-window (process-buffer proc))))
(select-window win))))
;; :build runs ./build.sh; :build ARGS appends ARGS.
(evil-define-command my/ex-build (args)
"Run `./build.sh ARGS' from the nearest directory containing build.sh."
(interactive "<a>")
(let ((root (locate-dominating-file default-directory "build.sh")))
(unless root
(user-error "No build.sh found"))
(let ((default-directory root))
(unless (file-executable-p "build.sh")
(user-error "build.sh is not executable"))
(compile (string-trim (concat "./build.sh " args)))))) ; nil args -> ""
(evil-ex-define-cmd "build" #'my/ex-build)
;;; build.el ends here
+99
View File
@@ -0,0 +1,99 @@
;;; development.el --- Languages, Eglot and code navigation -*- lexical-binding: t; -*-
;;; Commentary:
;; Loaded from init.el. Requires Evil.
;;
;; Languages:
;; Markdown markdown-mode.
;; PHP php-mode, PSR-2 style, 4 spaces.
;; Odin odin-mode from git (needs Emacs 30+ for :vc), Eglot + OLS.
;; Go go-mode, Eglot + gopls; organize imports and format on save.
;;
;; Normal/motion state:
;; gd definitions gD references K eldoc
;; C-t jump back ESC closes any open eldoc window.
;;; Code:
;;;; Markdown
(use-package markdown-mode)
;;;; PHP
(use-package php-mode
:mode "\\.php\\'"
:hook
(php-mode . (lambda ()
(setq-local indent-tabs-mode nil)
(setq-local tab-width 4)
(setq-local c-basic-offset 4)))
:custom
(php-mode-coding-style 'psr2))
;;;; Odin
(use-package odin-mode
:vc (:url "https://github.com/mattt-b/odin-mode"
:rev :newest)
:mode "\\.odin\\'"
:hook (odin-mode . eglot-ensure))
;;;; Go
;; Emacs started from the desktop doesn't inherit fish env.
(dolist (d '("/usr/local/go/bin" "/home/jason/.go/bin"))
(add-to-list 'exec-path d)
(setenv "PATH" (concat d ":" (getenv "PATH"))))
(setenv "GOPATH" "/home/jason/.go")
(setenv "GOCACHE" "/home/jason/.go/cache")
(defun my/go-before-save ()
"Organize imports and format the buffer through Eglot."
(when (eglot-managed-p)
(eglot-code-actions nil nil "source.organizeImports" t)
(eglot-format-buffer)))
(use-package go-mode
:mode "\\.go\\'"
:hook ((go-mode . eglot-ensure)
(go-mode . (lambda ()
(setq-local tab-width 4)
(add-hook 'before-save-hook #'my/go-before-save nil t)))))
;;;; Eglot
(use-package eglot
:ensure nil
:config
(setq eglot-autoshutdown t)
(add-to-list 'eglot-ignored-server-capabilities :signatureHelpProvider)
;; Explicit OLS configuration.
(add-to-list
'eglot-server-programs
'(odin-mode
. ("/home/jason/.local/bin/ols"
:initializationOptions
(:odin_command "/home/jason/.odin/odin"
:enable_document_symbols t
:enable_format t
:collections
[(:name "core"
:path "/home/jason/.odin/core")
(:name "vendor"
:path "/home/jason/.odin/vendor")])))))
;; OLS built-in definitions.
(setenv "OLS_BUILTIN_FOLDER" "/home/jason/ThirdParty/ols/builtin")
;;;; Code navigation
;; Vim-style code navigation.
(evil-define-key '(normal motion) 'global
"gd" #'xref-find-definitions
"gD" #'xref-find-references
"K" #'eldoc
(kbd "C-t") #'xref-go-back)
;; ESC closes any open eldoc window.
(defun my/eldoc-close (&rest _)
"Quit every window showing an *eldoc* buffer."
(dolist (w (window-list))
(when (string-prefix-p "*eldoc" (buffer-name (window-buffer w)))
(quit-window nil w))))
(advice-add 'evil-force-normal-state :after #'my/eldoc-close)
;;; development.el ends here
+58
View File
@@ -0,0 +1,58 @@
;;; git.el --- Magit, set up for Evil -*- lexical-binding: t; -*-
;;; Commentary:
;; Loaded from init.el, which must set `evil-want-keybinding' to nil before
;; Evil loads (Evil Collection needs it).
;;
;; :git Magit status for the current repo, full frame.
;; q puts your previous window layout back.
;; :git ARGS Run `git ARGS' from the repo root, e.g. :git log --oneline
;;
;; In Magit (Evil Collection layout):
;; j/k move, TAB folds, s stage, u unstage, x discard, c c commit,
;; p push, F pull, l log, d diff, b branch, z stash, gr refresh, q quit,
;; ? lists every command.
;; Commit message: opens in insert state; ZZ or C-c C-c commits,
;; ZQ or C-c C-k cancels.
;; Ctrl+hjkl, C-f and C-b keep their global meaning in Magit buffers.
;;; Code:
(use-package magit
:defer t
:custom
;; Status takes the whole frame; q restores the previous window layout.
(magit-display-buffer-function #'magit-display-buffer-fullframe-status-v1)
(magit-bury-buffer-function #'magit-restore-window-configuration))
;; Vim-style keys for Magit, its section folding, and the commit message buffer.
;; (magit magit-submodule) is the form Evil Collection's own default list uses.
(with-eval-after-load 'evil-collection
;; Evil Collection puts section jumps on C-j/C-k and page scrolling on C-f/C-b.
;; Unbind them so your window and Consult keys show through. Sibling sections
;; are still on ] and [, and scrolling on C-d/C-u.
(add-hook 'evil-collection-setup-hook
(lambda (mode &rest _)
(when (eq mode 'magit)
(evil-define-key 'normal magit-mode-map
(kbd "C-j") nil
(kbd "C-k") nil
(kbd "C-f") nil
(kbd "C-b") nil))))
(evil-collection-init '((magit magit-submodule) magit-section with-editor)))
;; Commit messages start in insert state. Added after git-commit loads so its
;; default hook functions are kept.
(with-eval-after-load 'git-commit
(add-hook 'git-commit-setup-hook #'evil-insert-state))
;; :git opens Magit; :git ARGS runs a git command.
(evil-define-command my/git (args)
"Open Magit status, or run `git ARGS' from the repository root."
(interactive "<a>")
(if (and args (string-match-p "[^[:space:]]" args))
(magit-git-command-topdir (concat "git " args))
(magit-status)))
(evil-ex-define-cmd "git" #'my/git)
;;; git.el ends here
+58
View File
@@ -0,0 +1,58 @@
;;; org.el --- Org appearance -*- lexical-binding: t; -*-
;;; Code:
;; Styled stars, bullets, tags, tables and blocks.
(use-package org-modern
:hook (org-mode . org-modern-mode))
(setq org-directory "~/Projects/notebook")
(setq org-startup-indented t ; visual indent per level, no real spaces
org-hide-emphasis-markers t ; *bold* shows bold, markers hidden
org-pretty-entities t ; \rarr, \alpha etc. as symbols
org-ellipsis " ▾" ; folded-heading marker
org-startup-folded 'show2levels ; open with headings only
org-fontify-whole-heading-line t
org-fontify-done-headline t
org-fontify-quote-and-verse-blocks t
org-src-fontify-natively t ; highlight src blocks with their own mode
org-src-tab-acts-natively t
org-edit-src-content-indentation 0
org-return-follows-link t)
;; org-modern's recommended settings.
(setq org-auto-align-tags nil
org-tags-column 0
org-catch-invisible-edits 'show-and-error
org-special-ctrl-a/e t
org-insert-heading-respect-content t)
(add-hook 'org-mode-hook #'visual-line-mode) ; wrap at window edge
(add-hook 'org-mode-hook (lambda () (display-line-numbers-mode -1))) ; prose reads better without
;; doom-themes' own Org fontification fixes (blocks, headings).
(doom-themes-org-config)
;; Heading sizes; 'user survives theme reloads, same as in init.el.
(doom-themes-set-faces 'user
'(org-document-title :height 1.3 :weight 'bold)
'(org-level-1 :height 1.2 :weight 'bold)
'(org-level-2 :height 1.15 :weight 'bold)
'(org-level-3 :height 1.1 :weight 'bold)
'(org-level-4 :height 1.0 :weight 'bold))
(defun my/notes-index ()
"Open the notes index."
(interactive)
(find-file (expand-file-name "index.org" org-directory)))
(keymap-global-set "C-c n" #'my/notes-index)
;; Evil's TAB (= C-i, jump-forward) shadows Org's. Rebind for Org buffers only.
(with-eval-after-load 'org
(evil-define-key 'normal org-mode-map
(kbd "TAB") #'org-cycle ; fold/unfold the heading at point
(kbd "<tab>") #'org-cycle ; GUI emits <tab>, so bind both
(kbd "RET") #'org-open-at-point
(kbd "<backtab>") #'org-shifttab)) ; S-TAB: cycle all headings
;;; org.el ends here
+360
View File
@@ -0,0 +1,360 @@
;;; minimal-theme.el --- Minimal high-functionality Ujelly port -*- lexical-binding: t; -*-
;;
;; Built on the compline-theme face structure, updated with the color palette
;; from Ujelly (color-theme-ujelly by marktran).
;;
;;; Code:
(deftheme minimal
"Created 2026-10-26.")
(custom-theme-set-faces
'minimal
;;;; Core
'(default ((t (:inherit nil :extend nil :stipple nil :background "#151515" :foreground "#e8e8d3" :inverse-video nil :box nil :strike-through nil :overline nil :underline nil :slant normal :weight regular :height 143 :width normal))))
'(cursor ((t (:background "#8fbfdc"))))
'(fixed-pitch ((t (:height 140))))
'(variable-pitch ((t (:height 140))))
'(escape-glyph ((t (:foreground "#8fbfdc"))))
'(homoglyph ((t (:foreground "#8fbfdc"))))
'(minibuffer-prompt ((t (:foreground "#8fbfdc"))))
'(highlight ((t (:foreground "#e8e8d3" :background "#303030" :weight bold))))
'(hl-line ((t (:extend t :background "#1b1b1b"))))
'(region ((t (:extend t :background "#404040"))))
'(shadow ((t (:foreground "#7f7f7f"))))
'(secondary-selection ((t (:extend t :background "#404040"))))
'(trailing-whitespace ((t (:background "#cf6a4c"))))
'(vertical-border ((t (:foreground "#262626"))))
'(error ((t (:foreground "#cf6a4c"))))
'(warning ((t (:foreground "#fad07a"))))
'(success ((t (:foreground "#99ad6a"))))
;;;; Line numbers
'(line-number ((t (:foreground "#404040"))))
'(line-number-current-line ((t (:foreground "#e8e8d3"))))
;;;; Font lock
'(font-lock-bracket-face ((t (:inherit (font-lock-punctuation-face)))))
'(font-lock-builtin-face ((t (:foreground "#8fbfdc"))))
'(font-lock-comment-delimiter-face ((default (:inherit (font-lock-comment-face)))))
'(font-lock-comment-face ((t (:foreground "#767676"))))
'(font-lock-constant-face ((t (:foreground "#c6b5ed"))))
'(font-lock-delimiter-face ((t (:inherit (font-lock-punctuation-face)))))
'(font-lock-doc-face ((t (:foreground "#767676" :inherit (font-lock-comment-face)))))
'(font-lock-doc-markup-face ((t (:inherit (font-lock-constant-face)))))
'(font-lock-escape-face ((t (:inherit (font-lock-regexp-grouping-backslash)))))
'(font-lock-function-call-face ((t (:slant italic :foreground "#8fbfdc" :inherit (font-lock-function-name-face)))))
'(font-lock-function-name-face ((t (:foreground "#8fbfdc"))))
'(font-lock-keyword-face ((t (:foreground "#fad07a"))))
'(font-lock-negation-char-face ((t (:foreground "#cf6a4c" :inherit (bold)))))
'(font-lock-number-face ((t (:foreground "#cf6a4c"))))
'(font-lock-misc-punctuation-face ((t (:inherit (font-lock-punctuation-face)))))
'(font-lock-operator-face ((t (:foreground "#a0a8b0"))))
'(font-lock-preprocessor-face ((t (:foreground "#fad07a" :inherit (bold)))))
'(font-lock-property-name-face ((t (:weight bold :foreground "#8fbfdc"))))
'(font-lock-property-use-face ((t (:weight regular :inherit (font-lock-property-name-face)))))
'(font-lock-punctuation-face ((t (:foreground "#a0a8b0"))))
'(font-lock-regexp-grouping-backslash ((t (:foreground "#fad07a" :inherit (bold)))))
'(font-lock-regexp-grouping-construct ((t (:foreground "#fad07a" :inherit (bold)))))
'(font-lock-string-face ((t (:foreground "#99ad6a"))))
'(font-lock-type-face ((t (:foreground "#ffb17a"))))
'(font-lock-variable-name-face ((t (:foreground "#e8e8d3"))))
'(font-lock-variable-use-face ((t (:foreground "#e8e8d3" :inherit (font-lock-variable-name-face)))))
'(font-lock-warning-face ((t (:inherit (warning)))))
;;;; Links, buttons, tooltips
'(button ((t (:inherit (link)))))
'(link ((t (:weight bold :underline (:color foreground-color :style line :position nil) :foreground "#8fbfdc"))))
'(link-visited ((t (:inherit link :foreground "#c6b5ed"))))
'(fringe ((t (:foreground "#404040" :inherit (default)))))
'(tooltip ((t (:foreground "#e8e8d3" :background "#1b1b1b"))))
;;;; Mode line
'(header-line ((t (:inherit (mode-line)))))
'(mode-line ((t (:background "#1c1c1c" :foreground unspecified))))
'(mode-line-buffer-id ((t (:weight bold))))
'(mode-line-emphasis ((t (:foreground "#fad07a"))))
'(mode-line-highlight ((t (:inherit (highlight)))))
'(mode-line-inactive ((t (:foreground "#767676" :background "#121212"))))
;;;; Tab bar
'(tab-bar ((t (:inherit default :background "#151515" :foreground "#767676"))))
'(tab-bar-tab ((t (:inherit tab-bar :background "#262626" :foreground "#e8e8d3" :weight bold :box nil))))
'(tab-bar-tab-inactive ((t (:inherit tab-bar :background "#151515" :foreground "#999999" :weight normal :box nil))))
;;;; Search & Completions
'(isearch ((t (:weight bold :inherit (lazy-highlight)))))
'(isearch-fail ((t (:weight bold :foreground "#151515" :background "#cf6a4c"))))
'(lazy-highlight ((t (:weight bold :foreground "#e8e8d3" :background "#303030"))))
'(match ((t (:weight bold :foreground "#99ad6a" :background "#151515"))))
'(next-error ((t (:inherit (region)))))
'(query-replace ((t (:inherit (isearch)))))
;;;; VC gutter: added = green, deleted = red, modified = sand/yellow
'(diff-hl-insert ((t (:foreground "#99ad6a" :background "#99ad6a"))))
'(diff-hl-delete ((t (:foreground "#cf6a4c" :background "#cf6a4c"))))
'(diff-hl-change ((t (:foreground "#fad07a" :background "#fad07a"))))
'(git-gutter:added ((t (:foreground "#99ad6a" :background "#99ad6a"))))
'(git-gutter:deleted ((t (:foreground "#cf6a4c" :background "#cf6a4c"))))
'(git-gutter:modified ((t (:foreground "#fad07a" :background "#fad07a"))))
'(git-gutter-fr:added ((t (:foreground "#99ad6a"))))
'(git-gutter-fr:deleted ((t (:foreground "#cf6a4c"))))
'(git-gutter-fr:modified ((t (:foreground "#fad07a"))))
;;;; gnus: group buffer
'(gnus-group-news-1 ((t (:foreground "#e8e8d3" :weight bold))))
'(gnus-group-news-1-empty ((t (:foreground "#e8e8d3" :weight normal))))
'(gnus-group-news-2 ((t (:foreground "#8fbfdc" :weight bold))))
'(gnus-group-news-2-empty ((t (:foreground "#8fbfdc" :weight normal))))
'(gnus-group-news-3 ((t (:foreground "#fad07a" :weight bold))))
'(gnus-group-news-3-empty ((t (:foreground "#fad07a" :weight normal))))
'(gnus-group-news-low ((t (:foreground "#767676" :weight bold))))
'(gnus-group-news-low-empty ((t (:foreground "#404040" :weight normal))))
'(gnus-group-mail-1 ((t (:foreground "#99ad6a" :weight bold))))
'(gnus-group-mail-1-empty ((t (:foreground "#99ad6a" :weight normal))))
'(gnus-group-mail-2 ((t (:foreground "#c6b5ed" :weight bold))))
'(gnus-group-mail-2-empty ((t (:foreground "#c6b5ed" :weight normal))))
'(gnus-group-mail-3 ((t (:foreground "#ffb17a" :weight bold))))
'(gnus-group-mail-3-empty ((t (:foreground "#ffb17a" :weight normal))))
'(gnus-group-mail-low ((t (:foreground "#767676" :weight bold))))
'(gnus-group-mail-low-empty ((t (:foreground "#404040" :weight normal))))
;;;; gnus: summary buffer
'(gnus-summary-normal-unread ((t (:foreground "#e8e8d3" :weight bold))))
'(gnus-summary-normal-read ((t (:foreground "#767676" :weight normal))))
'(gnus-summary-normal-ticked ((t (:foreground "#cf6a4c" :weight bold))))
'(gnus-summary-normal-ancient ((t (:foreground "#404040" :weight normal))))
'(gnus-summary-normal-undownloaded ((t (:foreground "#8fbfdc" :slant italic))))
'(gnus-summary-high-unread ((t (:foreground "#fad07a" :weight bold))))
'(gnus-summary-high-read ((t (:foreground "#99ad6a" :weight normal))))
'(gnus-summary-high-ticked ((t (:foreground "#cf6a4c" :weight bold))))
'(gnus-summary-high-ancient ((t (:foreground "#767676" :weight normal))))
'(gnus-summary-low-unread ((t (:foreground "#8fbfdc" :slant italic :weight bold))))
'(gnus-summary-low-read ((t (:foreground "#404040" :slant italic :weight normal))))
'(gnus-summary-low-ticked ((t (:foreground "#cf6a4c" :slant italic))))
'(gnus-summary-low-ancient ((t (:foreground "#404040" :slant italic))))
'(gnus-summary-selected ((t (:background "#404040" :foreground "#e8e8d3" :weight bold))))
'(gnus-summary-cancelled ((t (:foreground "#cf6a4c" :strike-through t))))
;;;; gnus: article headers, citations, misc
'(gnus-header-name ((t (:foreground "#8fbfdc" :weight bold))))
'(gnus-header-content ((t (:foreground "#fad07a" :slant italic))))
'(gnus-header-from ((t (:foreground "#e8e8d3" :weight bold))))
'(gnus-header-subject ((t (:foreground "#ffb17a"))))
'(gnus-header-newsgroups ((t (:foreground "#fad07a" :slant italic))))
'(gnus-cite-1 ((t (:foreground "#8fbfdc"))))
'(gnus-cite-2 ((t (:foreground "#99ad6a"))))
'(gnus-cite-3 ((t (:foreground "#fad07a"))))
'(gnus-cite-4 ((t (:foreground "#cf6a4c"))))
'(gnus-cite-5 ((t (:foreground "#c6b5ed"))))
'(gnus-cite-6 ((t (:foreground "#ffb17a"))))
'(gnus-cite-7 ((t (:foreground "#8fbfdc"))))
'(gnus-cite-8 ((t (:foreground "#99ad6a"))))
'(gnus-cite-9 ((t (:foreground "#fad07a"))))
'(gnus-cite-10 ((t (:foreground "#767676"))))
'(gnus-cite-11 ((t (:foreground "#404040"))))
'(gnus-signature ((t (:foreground "#767676" :slant italic))))
'(gnus-button ((t (:inherit link))))
'(gnus-splash ((t (:foreground "#767676"))))
'(gnus-emphasis-bold ((t (:weight bold))))
'(gnus-emphasis-italic ((t (:slant italic))))
'(gnus-emphasis-underline ((t (:underline t))))
'(gnus-emphasis-bold-italic ((t (:weight bold :slant italic))))
'(gnus-server-opened ((t (:foreground "#99ad6a" :weight bold))))
'(gnus-server-closed ((t (:foreground "#767676" :slant italic))))
'(gnus-server-denied ((t (:foreground "#cf6a4c" :weight bold))))
'(gnus-server-offline ((t (:foreground "#fad07a" :weight bold))))
'(gnus-server-agent ((t (:foreground "#8fbfdc" :weight bold))))
;;;; message (gnus composition buffers)
'(message-header-name ((t (:foreground "#8fbfdc" :weight bold))))
'(message-header-to ((t (:foreground "#e8e8d3" :weight bold))))
'(message-header-cc ((t (:foreground "#ffb17a"))))
'(message-header-subject ((t (:foreground "#e8e8d3" :weight bold))))
'(message-header-newsgroups ((t (:foreground "#fad07a" :slant italic))))
'(message-header-other ((t (:foreground "#fad07a"))))
'(message-header-xheader ((t (:foreground "#767676"))))
'(message-separator ((t (:foreground "#404040"))))
'(message-mml ((t (:foreground "#99ad6a"))))
'(message-cited-text-1 ((t (:foreground "#8fbfdc"))))
'(message-cited-text-2 ((t (:foreground "#99ad6a"))))
'(message-cited-text-3 ((t (:foreground "#fad07a"))))
'(message-cited-text-4 ((t (:foreground "#cf6a4c"))))
;;;; magit: sections
'(magit-section-heading ((t (:foreground "#fad07a" :weight bold :extend t))))
'(magit-section-secondary-heading ((t (:foreground "#c6b5ed" :weight bold :extend t))))
'(magit-section-heading-selection ((t (:foreground "#99ad6a" :weight bold :extend t))))
'(magit-section-highlight ((t (:background "#1b1b1b" :extend t))))
'(magit-section-child-count ((t (:foreground "#767676"))))
;;;; magit: refs, hashes, log
'(magit-branch-local ((t (:foreground "#8fbfdc"))))
'(magit-branch-remote ((t (:foreground "#99ad6a"))))
'(magit-branch-current ((t (:foreground "#8fbfdc" :weight bold :box (:line-width -1 :color "#767676")))))
'(magit-branch-remote-head ((t (:foreground "#99ad6a" :weight bold :box (:line-width -1 :color "#767676")))))
'(magit-branch-upstream ((t (:slant italic))))
'(magit-tag ((t (:foreground "#fad07a"))))
'(magit-hash ((t (:foreground "#767676"))))
'(magit-dimmed ((t (:foreground "#404040"))))
'(magit-filename ((t (:foreground "#e8e8d3"))))
'(magit-refname ((t (:foreground "#767676"))))
'(magit-refname-stash ((t (:foreground "#767676"))))
'(magit-refname-wip ((t (:foreground "#767676"))))
'(magit-log-author ((t (:foreground "#fad07a"))))
'(magit-log-date ((t (:foreground "#767676"))))
'(magit-log-graph ((t (:foreground "#404040"))))
'(magit-keyword ((t (:foreground "#fad07a"))))
'(magit-keyword-squash ((t (:foreground "#fad07a"))))
'(magit-cherry-equivalent ((t (:foreground "#e8e8d3"))))
'(magit-cherry-unmatched ((t (:foreground "#8fbfdc"))))
;;;; magit: diffs
'(magit-diff-file-heading ((t (:foreground "#e8e8d3" :weight bold :extend t))))
'(magit-diff-file-heading-highlight ((t (:inherit magit-diff-file-heading :background "#1b1b1b"))))
'(magit-diff-file-heading-selection ((t (:inherit magit-diff-file-heading :foreground "#fad07a" :background "#404040"))))
'(magit-diff-hunk-heading ((t (:foreground "#c6b5ed" :background "#222222" :extend t))))
'(magit-diff-hunk-heading-highlight ((t (:foreground "#e8e8d3" :background "#404040" :extend t))))
'(magit-diff-hunk-heading-selection ((t (:foreground "#fad07a" :background "#404040" :extend t))))
'(magit-diff-lines-heading ((t (:foreground "#151515" :background "#fad07a" :extend t))))
'(magit-diff-context ((t (:foreground "#767676" :extend t))))
'(magit-diff-context-highlight ((t (:foreground "#c6b5ed" :background "#1b1b1b" :extend t))))
'(magit-diff-added ((t (:foreground "#99ad6a" :background "#1a261a" :extend t))))
'(magit-diff-added-highlight ((t (:foreground "#99ad6a" :background "#223322" :extend t))))
'(magit-diff-removed ((t (:foreground "#cf6a4c" :background "#261a1a" :extend t))))
'(magit-diff-removed-highlight ((t (:foreground "#cf6a4c" :background "#332222" :extend t))))
'(magit-diffstat-added ((t (:foreground "#99ad6a"))))
'(magit-diffstat-removed ((t (:foreground "#cf6a4c"))))
'(magit-diff-whitespace-warning ((t (:background "#cf6a4c"))))
;;;; magit: process, signatures, sequences, blame, header line
'(magit-process-ok ((t (:foreground "#99ad6a" :weight bold))))
'(magit-process-ng ((t (:foreground "#cf6a4c" :weight bold))))
'(magit-mode-line-process ((t (:foreground "#fad07a"))))
'(magit-mode-line-process-error ((t (:foreground "#cf6a4c"))))
'(magit-signature-good ((t (:foreground "#99ad6a"))))
'(magit-signature-bad ((t (:foreground "#cf6a4c" :weight bold))))
'(magit-signature-untrusted ((t (:foreground "#8fbfdc"))))
'(magit-signature-expired ((t (:foreground "#fad07a"))))
'(magit-signature-expired-key ((t (:foreground "#fad07a"))))
'(magit-signature-revoked ((t (:foreground "#cf6a4c"))))
'(magit-signature-error ((t (:foreground "#cf6a4c"))))
'(magit-sequence-head ((t (:foreground "#8fbfdc"))))
'(magit-sequence-stop ((t (:foreground "#99ad6a"))))
'(magit-sequence-part ((t (:foreground "#fad07a"))))
'(magit-sequence-done ((t (:foreground "#767676"))))
'(magit-sequence-onto ((t (:foreground "#767676"))))
'(magit-bisect-good ((t (:foreground "#99ad6a"))))
'(magit-bisect-skip ((t (:foreground "#fad07a"))))
'(magit-bisect-bad ((t (:foreground "#cf6a4c"))))
'(magit-blame-heading ((t (:foreground "#c6b5ed" :background "#222222" :extend t))))
'(magit-header-line ((t (:foreground "#fad07a" :weight bold))))
'(magit-header-line-key ((t (:foreground "#fad07a"))))
'(magit-header-line-log-select ((t (:foreground "#e8e8d3" :weight bold))))
;;;; transient (magit popups)
'(transient-heading ((t (:foreground "#fad07a" :weight bold))))
'(transient-key ((t (:foreground "#fad07a" :weight bold))))
'(transient-key-exit ((t (:foreground "#cf6a4c" :weight bold))))
'(transient-key-return ((t (:foreground "#fad07a" :weight bold))))
'(transient-key-stay ((t (:foreground "#99ad6a" :weight bold))))
'(transient-argument ((t (:foreground "#99ad6a" :weight bold))))
'(transient-value ((t (:foreground "#99ad6a"))))
'(transient-inactive-argument ((t (:foreground "#767676"))))
'(transient-inactive-value ((t (:foreground "#767676"))))
'(transient-unreachable ((t (:foreground "#404040"))))
'(transient-unreachable-key ((t (:foreground "#404040"))))
'(transient-disabled-suffix ((t (:foreground "#151515" :background "#cf6a4c" :weight bold))))
'(transient-enabled-suffix ((t (:foreground "#151515" :background "#99ad6a" :weight bold))))
'(transient-delimiter ((t (:foreground "#767676"))))
'(transient-separator ((t (:background "#1c1c1c"))))
'(transient-active-infix ((t (:foreground "#e8e8d3" :background "#404040"))))
'(transient-mismatched-key ((t (:underline t))))
'(transient-nonstandard-key ((t (:underline t))))
;;;; git-commit (magit commit message buffer)
'(git-commit-summary ((t (:foreground "#e8e8d3" :weight bold))))
'(git-commit-overlong-summary ((t (:foreground "#cf6a4c" :weight bold))))
'(git-commit-nonempty-second-line ((t (:foreground "#cf6a4c"))))
'(git-commit-keyword ((t (:foreground "#8fbfdc"))))
'(git-commit-comment-heading ((t (:foreground "#fad07a" :weight bold))))
'(git-commit-comment-branch-local ((t (:foreground "#8fbfdc"))))
'(git-commit-comment-branch-remote ((t (:foreground "#99ad6a"))))
'(git-commit-comment-detached ((t (:foreground "#fad07a"))))
'(git-commit-comment-file ((t (:foreground "#fad07a"))))
'(git-commit-comment-action ((t (:foreground "#99ad6a"))))
;;;; erc: buffer text
'(erc-default-face ((t (:foreground "#e8e8d3"))))
'(erc-input-face ((t (:foreground "#99ad6a"))))
'(erc-my-nick-face ((t (:foreground "#99ad6a" :weight bold))))
'(erc-my-nick-prefix-face ((t (:foreground "#99ad6a" :weight bold))))
'(erc-nick-default-face ((t (:foreground "#e8e8d3" :weight bold))))
'(erc-nick-prefix-face ((t (:foreground "#fad07a"))))
'(erc-notice-face ((t (:foreground "#767676" :weight normal))))
'(erc-information ((t (:foreground "#c6b5ed"))))
'(erc-timestamp-face ((t (:foreground "#404040" :weight normal))))
'(erc-action-face ((t (:foreground "#ffb17a" :weight bold))))
'(erc-prompt-face ((t (:foreground "#fad07a" :weight bold))))
'(erc-command-indicator-face ((t (:foreground "#c6b5ed" :weight bold))))
'(erc-error-face ((t (:foreground "#cf6a4c"))))
'(erc-button ((t (:inherit link :foreground "#e8e8d3"))))
'(erc-button-nick-default-face ((t (:foreground "#e8e8d3" :weight bold))))
'(erc-bold-face ((t (:weight bold))))
'(erc-italic-face ((t (:slant italic))))
'(erc-underline-face ((t (:underline t))))
'(erc-inverse-face ((t (:foreground "#151515" :background "#e8e8d3"))))
'(erc-keep-place-indicator-line ((t (:underline (:color "#404040" :style line)))))
'(erc-keep-place-indicator-arrow ((t (:foreground "#cf6a4c"))))
'(erc-header-line ((t (:foreground "#c6b5ed" :background "#1c1c1c"))))
'(erc-header-line-disconnected ((t (:foreground "#cf6a4c" :background "#1c1c1c"))))
;;;; erc: highlighting rules
'(erc-current-nick-face ((t (:foreground "#fad07a" :weight bold))))
'(erc-keyword-face ((t (:foreground "#fad07a" :weight bold))))
'(erc-nick-msg-face ((t (:foreground "#fad07a" :weight bold))))
'(erc-direct-msg-face ((t (:foreground "#cf6a4c"))))
'(erc-pal-face ((t (:foreground "#99ad6a" :weight bold))))
'(erc-fool-face ((t (:foreground "#404040"))))
'(erc-dangerous-host-face ((t (:foreground "#cf6a4c"))))
;;;; css-mode / scss-mode
'(css-proprietary-property ((t (:foreground "#fad07a"))))
'(css-property ((t (:foreground "#e8e8d3"))))
'(css-selector ((t (:foreground "#cf6a4c"))))
;;;; markdown-mode
'(markdown-markup-face ((t (:foreground "#404040"))))
'(markdown-header-face ((t (:inherit bold :foreground "#cf6a4c"))))
'(markdown-code-face ((t (:background "#1c1c1c"))))
;;;; outline
'(outline-1 ((t (:foreground "#e8e8d3" :weight ultra-bold))))
'(outline-2 ((t (:foreground "#8fbfdc"))))
'(outline-3 ((t (:foreground "#99ad6a"))))
'(outline-4 ((t (:foreground "#c6b5ed"))))
'(outline-5 ((t (:foreground "#ffb17a"))))
'(outline-6 ((t (:foreground "#fad07a"))))
'(outline-7 ((t (:foreground "#cf6a4c"))))
'(outline-8 ((t (:foreground "#e8e8d3"))))
;;;; org
'(org-block ((t (:background "#1b1b1b"))))
'(org-block-begin-line ((t (:foreground "#404040" :slant italic :background "#121212"))))
'(org-ellipsis ((t (:underline nil :background "#151515" :foreground "#cf6a4c"))))
'(org-quote ((t (:background "#1c1c1c"))))
'(org-hide ((t (:foreground "#151515"))))
;;;; solaire-mode
'(solaire-mode-line-face ((t (:inherit mode-line :background "#1c1c1c"))))
'(solaire-mode-line-inactive-face ((t (:inherit mode-line-inactive :background "#1c1c1c")))))
;;;###autoload
(when load-file-name
(add-to-list 'custom-theme-load-path
(file-name-as-directory (file-name-directory load-file-name))))
(provide-theme 'minimal)
;;; minimal-theme.el ends here
+4 -2
View File
@@ -21,7 +21,6 @@ set -gx COLORTERM "truecolor"
set -gx DEBUG 1
set -gx GOPATH "$HOME/.go"
set -gx GOCACHE "$HOME/.go/cache"
set -gx GOROOT /usr/local/go
set -gx OLS_BUILTIN_FOLDER "/home/jason/ThirdParty/ols/builtin"
# Direnv
@@ -82,7 +81,10 @@ alias bat 'bat --theme="ansi" --paging=never'
alias :q 'exit'
alias files 'thunar .'
alias fetch 'pfetch'
alias vi 'vis'
# Map 'php' directly to FrankenPHP's bundled PHP CLI runtime
alias php="frankenphp php-cli"
# Optional: A shorthand to spin up your local dev server instantly using your Caddyfile
alias phpserve="frankenphp php-server --config Caddyfile"
# Custom shortcuts
alias reload 'source ~/.config/fish/config.fish'
-256
View File
@@ -1,256 +0,0 @@
// ============================================================================
// GENERAL & SYSTEM SETTINGS
// Wiki: https://github.com/YaLTeR/niri/wiki/Configuration:-Introduction
// ============================================================================
config-notification {
disable-failed
}
hotkey-overlay {
skip-at-startup
}
prefer-no-csd
screenshot-path "~/Pictures/Screenshots/Screenshot from %Y-%m-%d %H-%M-%S.png"
debug {
honor-xdg-activation-with-invalid-serial
}
// ============================================================================
// ENVIRONMENT VARIABLES
// ============================================================================
environment {
XDG_CURRENT_DESKTOP "niri"
QT_QPA_PLATFORM "wayland;xcb"
ELECTRON_OZONE_PLATFORM_HINT "auto"
QT_QPA_PLATFORMTHEME "gtk3"
QT_QPA_PLATFORMTHEME_QT6 "gtk3"
TERMINAL "alacritty"
}
// ============================================================================
// AUTOSTART / SPAWN AT STARTUP
// ============================================================================
spawn-at-startup "dms" "run"
// Export the session env to D-Bus so xdg-desktop-portal works
spawn-at-startup "dbus-update-activation-environment" "--all"
// Audio stack
spawn-at-startup "pipewire"
// PolicyKit authentication agent (verify path: xbps-query -f mate-polkit | grep -i agent)
spawn-at-startup "sh" "-c" "/usr/libexec/polkit-mate-authentication-agent-1 &"
// Clipboard history backing store for Mod+V
spawn-at-startup "sh" "-c" "wl-paste --watch cliphist store"
// Automount removable media
spawn-at-startup "udiskie" "--no-tray"
// ============================================================================
// INPUT & GESTURES
// Wiki: https://github.com/YaLTeR/niri/wiki/Configuration:-Input
// ============================================================================
input {
keyboard {
xkb {
// If this section is empty, niri will fetch xkb settings
// from org.freedesktop.locale1. You can control these using
// localectl set-x11-keymap.
}
// Enable numlock on startup, omitting this setting disables it.
numlock
}
// Focus windows and outputs automatically when moving the mouse into them.
focus-follows-mouse max-scroll-amount="95%"
}
gestures {
hot-corners {
off
}
}
// ============================================================================
// LAYOUT & VISUAL APPEARANCE
// Wiki: https://github.com/YaLTeR/niri/wiki/Configuration:-Layout
// ============================================================================
layout {
background-color "transparent"
// When to center a column when changing focus: "never", "always", or "on-overflow"
center-focused-column "never"
// Custom widths toggled by "switch-preset-column-width" (Mod+R)
preset-column-widths {
proportion 0.5
proportion 1.0
}
default-column-width { proportion 0.5; }
border {
off
width 4
active-color "#707070" // Neutral gray
inactive-color "#d0d0d0" // Light gray
urgent-color "#cc4444" // Softer red
}
shadow {
softness 30
spread 5
offset x=0 y=5
color "#0007"
}
struts {
}
}
overview {
workspace-shadow {
off
}
}
// ============================================================================
// ANIMATIONS
// ============================================================================
animations {
workspace-switch {
spring damping-ratio=0.80 stiffness=523 epsilon=0.0001
}
window-open {
duration-ms 150
curve "ease-out-expo"
}
window-close {
duration-ms 150
curve "ease-out-quad"
}
horizontal-view-movement {
spring damping-ratio=0.85 stiffness=423 epsilon=0.0001
}
window-movement {
spring damping-ratio=0.75 stiffness=323 epsilon=0.0001
}
window-resize {
spring damping-ratio=0.85 stiffness=423 epsilon=0.0001
}
config-notification-open-close {
spring damping-ratio=0.65 stiffness=923 epsilon=0.001
}
screenshot-ui-open {
duration-ms 200
curve "ease-out-quad"
}
overview-open-close {
spring damping-ratio=0.85 stiffness=800 epsilon=0.0001
}
}
// ============================================================================
// WINDOW & LAYER RULES
// Wiki: https://github.com/YaLTeR/niri/wiki/Configuration:-Window-Rules
// ============================================================================
layer-rule {
match namespace="^quickshell$"
place-within-backdrop true
}
window-rule {
match app-id=r#"^org\.gnome\."#
draw-border-with-background false
geometry-corner-radius 12
clip-to-geometry true
}
window-rule {
match app-id=r#"^gnome-control-center$"#
match app-id=r#"^pavucontrol$"#
match app-id=r#"^nm-connection-editor$"#
default-column-width { proportion 0.5; }
open-floating false
}
window-rule {
match app-id=r#"^org\.gnome\.Calculator$"#
match app-id=r#"^gnome-calculator$"#
match app-id=r#"^galculator$"#
match app-id=r#"^blueman-manager$"#
match app-id=r#"^org\.gnome\.Nautilus$"#
match app-id=r#"^xdg-desktop-portal$"#
open-floating true
}
window-rule {
match app-id="Alacritty"
match app-id="kitty"
draw-border-with-background false
}
window-rule {
match app-id=r#"firefox$"# title="^Picture-in-Picture$"
match app-id="zoom"
open-floating true
}
// Force the Steam client, Friends List, and settings windows to float
window-rule {
match app-id="steam"
open-floating true
}
window-rule {
match app-id="steam"
match title="Friends List"
open-floating true
default-floating-position x=10 y=10 relative-to="bottom-right"
}
window-rule {
match app-id=r#"^steam$"# title=r#"^notificationtoasts_\d+_desktop$"#
default-floating-position x=10 y=10 relative-to="top-right"
open-focused false
}
// Automatically fullscreen games launched via Proton
window-rule {
// Proton games typically use "steam_app_<id>" as their app-id.
// The r#"..."# syntax tells niri to evaluate this as a regular expression.
match app-id=r#"^steam_app_\d+$"#
open-fullscreen true
}
// ============================================================================
// NAVIGATION & KEYBINDINGS
// ============================================================================
// Override to disable super+tab
recent-windows {
binds {
Alt+Tab { next-window scope="output"; }
Alt+Shift+Tab { previous-window scope="output"; }
Alt+grave { next-window filter="app-id"; }
Alt+Shift+grave { previous-window filter="app-id"; }
}
}
// ============================================================================
// INCLUDES (DMS MANAGEMENT)
// ============================================================================
// @NOTE: only dms file we could edit
include optional=true "dms/binds.kdl"
// @NOTE: Non-editable files below all managed by dms
include optional=true "dms/colors.kdl"
include optional=true "dms/layout.kdl"
include optional=true "dms/alttab.kdl"
include optional=true "dms/outputs.kdl"
include optional=true "dms/cursor.kdl"
include optional=true "dms/input.kdl"
-152
View File
@@ -1,152 +0,0 @@
binds {
// ========================================================================
// SYSTEM & DMS APPLICATION LAUNCHERS
// ========================================================================
// Screenshots
Alt+Print hotkey-overlay-title="Screenshot: Window" { spawn "dms" "screenshot" "window"; }
Alt+XF86Launch1 hotkey-overlay-title="Screenshot: Window" { spawn "dms" "screenshot" "window"; }
Ctrl+Print hotkey-overlay-title="Screenshot: Full Screen" { spawn "dms" "screenshot" "full"; }
Ctrl+XF86Launch1 hotkey-overlay-title="Screenshot: Full Screen" { spawn "dms" "screenshot" "full"; }
Mod+P hotkey-overlay-title="Screenshot: Region" { spawn "dms" "screenshot"; }
// Launchers & Overlays
Mod+D hotkey-overlay-title="Application Launcher" { spawn "dms" "ipc" "call" "spotlight" "toggle"; }
Mod+space hotkey-overlay-title="Spotlight Bar: Toggle" { spawn "dms" "ipc" "call" "spotlight-bar" "toggle"; }
Mod+Y hotkey-overlay-title="Browse Wallpapers" { spawn "dms" "ipc" "call" "dash" "toggle" "wallpaper"; }
Super+Backspace hotkey-overlay-title="Power Menu: Toggle" { spawn "dms" "ipc" "call" "powermenu" "toggle"; }
Mod+Alt+L hotkey-overlay-title="Lock Screen" { spawn "dms" "ipc" "call" "lock" "lock"; }
// Utilities & Tools
Ctrl+Alt+Delete hotkey-overlay-title="Task Manager" { spawn "dms" "ipc" "call" "processlist" "focusOrToggle"; }
Mod+M hotkey-overlay-title="Task Manager" { spawn "dms" "ipc" "call" "processlist" "focusOrToggle"; }
Mod+N hotkey-overlay-title="Notification Center" { spawn "dms" "ipc" "call" "notifications" "toggle"; }
Mod+S hotkey-overlay-title="Notepad" { spawn "dms" "ipc" "call" "notepad" "toggle"; }
Mod+V hotkey-overlay-title="Clipboard Manager" { spawn "dms" "ipc" "call" "clipboard" "toggle"; }
Mod+Comma hotkey-overlay-title="Settings" { spawn "dms" "ipc" "call" "settings" "focusOrToggle"; }
// Configuration Helpers
Ctrl+Shift+R hotkey-overlay-title="Rename Workspace" { spawn "dms" "ipc" "call" "workspace-rename" "open"; }
Mod+Shift+W hotkey-overlay-title="Create window rule" { spawn "dms" "ipc" "call" "window-rules" "toggle"; }
// Audio Control (MPRIS)
Ctrl+XF86AudioLowerVolume allow-when-locked=true { spawn "dms" "ipc" "call" "mpris" "decrement" "3"; }
Ctrl+XF86AudioRaiseVolume allow-when-locked=true { spawn "dms" "ipc" "call" "mpris" "increment" "3"; }
// ========================================================================
// WINDOW & COLUMN FOCUS
// ========================================================================
// Focus Columns (Horizontal)
Mod+H { focus-column-left; }
Mod+Left { focus-column-left; }
Mod+L { focus-column-right; }
Mod+Right { focus-column-right; }
Mod+Home { focus-column-first; }
Mod+End { focus-column-last; }
// Focus Windows within Column (Vertical)
Mod+J { focus-window-down; }
Mod+Down { focus-window-down; }
Mod+K { focus-window-up; }
Mod+Up { focus-window-up; }
// Mouse Wheel Focus Navigation
Mod+WheelScrollLeft { focus-column-left; }
Mod+WheelScrollRight { focus-column-right; }
Mod+Shift+WheelScrollUp { focus-column-left; }
Mod+Shift+WheelScrollDown { focus-column-right; }
// ========================================================================
// WINDOW & COLUMN MANIPULATION
// ========================================================================
// Close Window
Mod+Q repeat=false { close-window; }
// Move Columns (Horizontal)
Mod+Shift+H { move-column-left; }
Mod+Shift+Left { move-column-left; }
Mod+Shift+L { move-column-right; }
Mod+Shift+Right { move-column-right; }
Mod+Ctrl+Home { move-column-to-first; }
Mod+Ctrl+End { move-column-to-last; }
Mod+Ctrl+WheelScrollLeft { move-column-left; }
Mod+Ctrl+WheelScrollRight { move-column-right; }
Mod+Ctrl+Shift+WheelScrollUp { move-column-left; }
Mod+Ctrl+Shift+WheelScrollDown { move-column-right; }
// Move Windows within Column (Vertical)
Mod+Shift+J { move-window-down; }
Mod+Shift+Down { move-window-down; }
Mod+Shift+K { move-window-up; }
Mod+Shift+Up { move-window-up; }
// Sizing & Layout Constraints
Mod+Equal { set-column-width "+10%"; }
Mod+Minus { set-column-width "-10%"; }
Mod+Shift+Equal { set-window-height "+10%"; }
Mod+Shift+Minus { set-window-height "-10%"; }
Mod+R { switch-preset-column-width; }
Mod+Shift+R { switch-preset-window-height; }
Mod+Ctrl+R { reset-window-height; }
Mod+Ctrl+F { expand-column-to-available-width; }
Mod+F { maximize-column; }
Mod+Shift+F { fullscreen-window; }
// Positioning, Floating & Tiling States
Mod+C { center-column; }
Mod+Ctrl+C { center-visible-columns; }
Mod+W { toggle-column-tabbed-display; }
Mod+Shift+T { toggle-window-floating; }
Mod+Shift+V { switch-focus-between-floating-and-tiling; }
Mod+BracketLeft { consume-or-expel-window-left; }
Mod+BracketRight { consume-or-expel-window-right; }
Mod+Period { expel-window-from-column; }
// ========================================================================
// WORKSPACE MANAGEMENT
// ========================================================================
// Switch Workspace Focus
Mod+Page_Up { focus-workspace-up; }
Mod+I { focus-workspace-up; }
Mod+Page_Down { focus-workspace-down; }
Mod+U { focus-workspace-down; }
Mod+WheelScrollUp cooldown-ms=150 { focus-workspace-up; }
Mod+WheelScrollDown cooldown-ms=150 { focus-workspace-down; }
// Switch Workspace by Index
Mod+1 { focus-workspace 1; }
Mod+2 { focus-workspace 2; }
Mod+3 { focus-workspace 3; }
Mod+4 { focus-workspace 4; }
// Move Focused Column to Workspace
Mod+Ctrl+Up { move-column-to-workspace-up; }
Mod+Ctrl+I { move-column-to-workspace-up; }
Mod+Ctrl+Down { move-column-to-workspace-down; }
Mod+Ctrl+U { move-column-to-workspace-down; }
Mod+Ctrl+WheelScrollUp cooldown-ms=150 { move-column-to-workspace-up; }
Mod+Ctrl+WheelScrollDown cooldown-ms=150 { move-column-to-workspace-down; }
// Move Focused Column to Workspace Index
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; }
// Move Entire Workspace Position
Mod+Shift+Page_Up { move-workspace-up; }
Mod+Shift+I { move-workspace-up; }
Mod+Shift+Page_Down { move-workspace-down; }
Mod+Shift+U { move-workspace-down; }
// ========================================================================
// COMPOSITOR & SESSION CONTROLS
// ========================================================================
Mod+T hotkey-overlay-title="Open Terminal" { spawn "alacritty"; }
Mod+O repeat=false { toggle-overview; }
Mod+Tab repeat=false { toggle-overview; }
Mod+Slash { show-hotkey-overlay; }
Mod+Escape allow-inhibiting=false { toggle-keyboard-shortcuts-inhibit; }
Mod+Shift+P { power-off-monitors; }
Mod+Shift+E { quit; }
}
-68
View File
@@ -1,68 +0,0 @@
-- Runs ./build.sh from vis's cwd (project root), logs the output for the
-- error picker (pickers.errors), and returns to vis on Enter/Esc.
local M = {}
-- last build's output; pickers.errors() reads it
M.log = '/tmp/vis-build.log'
-- single-quote a string for the shell
local function q(s) return "'" .. s:gsub("'", "'\\''") .. "'" end
-- write a raw escape sequence straight to the terminal
local function tty(s)
local t = io.open('/dev/tty', 'w')
if t then t:write(s) t:close() end
end
local function exists(path)
local f = io.open(path, 'r')
if f then f:close() end
return f ~= nil
end
-- args: flags passed through to build.sh, e.g. { 'run' }
function M.run(args)
if not exists('build.sh') then
vis:info('No build.sh in current directory')
return
end
-- save so the build sees your latest changes
local file = vis.win and vis.win.file
if file and file.modified and file.path then vis:command('w') end
local parts = {}
for _, a in ipairs(args or {}) do parts[#parts + 1] = q(a) end
local argstr = table.concat(parts, ' ')
local script = table.concat({
'saved=$(stty -g); stty sane',
-- script runs the build on a pty (colors stay on) and logs it to M.log
'script -qefc ' .. q('bash ./build.sh ' .. argstr) .. ' ' .. q(M.log),
'st=$?',
[[printf '\n[exit %s] enter/esc: back to vis ' "$st"]],
[[while IFS= read -rsn1 k; do [ -z "$k" ] || [ "$k" = $'\e' ] && break; done]],
'stty "$saved"',
}, '\n')
-- leave the alt screen and clear it, so each run starts fresh and the
-- output lands in normal scrollback; run; come back to vis
tty('\27[?1049l\27[H\27[2J')
os.execute('bash -c ' .. q(script) .. ' </dev/tty >/dev/tty 2>&1')
tty('\27[?1049h')
vis:redraw()
end
-- ---------------------------------------------------------------------------
-- events
-- ---------------------------------------------------------------------------
-- re-index tags in the background after saving an .odin file
-- (build.sh -t re-scans src and merges it with the cached stdlib tags)
vis.events.subscribe(vis.events.FILE_SAVE_POST, function(file, path) -- luacheck: no unused args
if path and path:match('%.odin$') and exists('build.sh') then
os.execute('bash ./build.sh -t </dev/null >/dev/null 2>&1 &')
end
end)
return M
-130
View File
@@ -1,130 +0,0 @@
-- Copyright 2026 Artur Ugnivenko. See LICENSE.
-- Odin LPeg lexer.
-- References:
-- https://odin-lang.org/spec/grammar/
-- https://pkg.odin-lang.org/base/builtin/
local lexer = lexer
local P, S, R = lpeg.P, lpeg.S, lpeg.R
local lex = lexer.new(...)
local ws = S(' \t')^0
local word_end = -(lexer.alnum + '_')
-- Keywords.
lex:add_rule('keyword', lex:tag(lexer.KEYWORD, lex:word_match(lexer.KEYWORD)))
-- Definitions: `name :: proc` and `Name :: struct / enum / union / distinct ...`.
local proc_def = lex:tag(lexer.FUNCTION, lexer.word) *
#(ws * '::' * ws * ('#' * lexer.word * ws)^0 * 'proc' * word_end)
local type_def = lex:tag(lexer.TYPE, lexer.word) *
#(ws * '::' * ws *
(P('struct') + 'enum' + 'union' + 'bit_set' + 'bit_field' + 'distinct') * word_end)
lex:add_rule('definition', proc_def + type_def)
-- Types.
lex:add_rule('type', lex:tag(lexer.TYPE, lex:word_match(lexer.TYPE)))
-- Polymorphic parameters: $T, $N.
lex:add_rule('poly', lex:tag(lexer.TYPE, '$' * lexer.word))
-- Functions.
local builtin_func = -lpeg.B('.') *
lex:tag(lexer.FUNCTION_BUILTIN, lex:word_match(lexer.FUNCTION_BUILTIN))
local func = lex:tag(lexer.FUNCTION, lexer.word)
lex:add_rule('function', (builtin_func + func) * #P('('))
-- Constants.
lex:add_rule('constant', lex:tag(lexer.CONSTANT_BUILTIN, lex:word_match(lexer.CONSTANT_BUILTIN)))
-- Variables.
lex:add_rule('variable',
lex:tag(lexer.VARIABLE_BUILTIN, lex:word_match(lexer.VARIABLE_BUILTIN)))
-- Identifiers.
lex:add_rule('identifier', lex:tag(lexer.IDENTIFIER, lexer.word))
-- Strings. Quoted strings end at the line; raw strings span lines, no escapes.
local sq_str = lexer.range("'", true)
local dq_str = lexer.range('"', true)
local raw_str = lexer.range('`', false, false)
lex:add_rule('string', lex:tag(lexer.STRING, sq_str + dq_str + raw_str))
-- Comments.
local comment = lexer.to_eol(P('//') + '#!')
local block_comment = lexer.range('/*', '*/', false, false, true)
local file_tag = lexer.to_eol('#+')
lex:add_rule('comment', lex:tag(lexer.COMMENT, comment + block_comment + file_tag))
-- Directives.
lex:add_rule('directive', lex:tag(lexer.PREPROCESSOR, '#' * S(' \t')^0 * lexer.word))
-- Attributes.
lex:add_rule('attribute', lex:tag(lexer.PREPROCESSOR, '@' * lexer.word^-1))
-- Numbers: 1_000_000, 1.5e-3, 0xff, 0b1010, 0o17, 0z1a (dozenal), 0h3f80_0000 (hex float), 2i.
local function digits(d) return d * (P('_')^-1 * d)^0 end
local dec = digits(lexer.digit)
local float = (dec * '.' * dec^-1 * -P('.') + '.' * dec) * (S('eE') * S('+-')^-1 * dec)^-1 +
dec * S('eE') * S('+-')^-1 * dec
local int = '0' * (
S('xh') * P('_')^-1 * digits(lexer.xdigit) +
'b' * P('_')^-1 * digits(S('01')) +
'o' * P('_')^-1 * digits(R('07')) +
'z' * P('_')^-1 * digits(R('09') + S('abAB'))) + dec
lex:add_rule('number', lex:tag(lexer.NUMBER, (float + int) * S('ijk')^-1))
-- Operators.
lex:add_rule('operator', lex:tag(lexer.OPERATOR, '..' + S('+-/*%<>!=^&|?~:;,.()[]{}')))
-- Fold points.
lex:add_fold_point(lexer.OPERATOR, '(', ')')
lex:add_fold_point(lexer.OPERATOR, '[', ']')
lex:add_fold_point(lexer.OPERATOR, '{', '}')
lex:add_fold_point(lexer.COMMENT, '/*', '*/')
-- Word lists.
lex:set_word_list(lexer.KEYWORD, {
'asm', 'auto_cast', 'bit_field', 'bit_set', 'break', 'case', 'cast', 'continue', 'defer',
'distinct', 'do', 'dynamic', 'else', 'enum', 'fallthrough', 'for', 'foreign', 'if', 'import',
'in', 'map', 'matrix', 'not_in', 'or_break', 'or_continue', 'or_else', 'or_return', 'package',
'proc', 'return', 'struct', 'switch', 'transmute', 'union', 'using', 'when', 'where'
})
lex:set_word_list(lexer.TYPE, {
'any', 'b8', 'b16', 'b32', 'b64', 'bool', 'byte', 'complex32', 'complex64', 'complex128',
'cstring', 'cstring16', 'f16', 'f16le', 'f16be', 'f32', 'f32le', 'f32be', 'f64', 'f64le', 'f64be',
'i8', 'i16', 'i16le', 'i16be', 'i32', 'i32le', 'i32be', 'i64', 'i64le', 'i64be', 'i128',
'i128le', 'i128be', 'int', 'quaternion64', 'quaternion128', 'quaternion256', 'rawptr', 'rune',
'string', 'string16', 'typeid', 'u8', 'u16', 'u16le', 'u16be', 'u32', 'u32le', 'u32be', 'u64',
'u64le', 'u64be', 'u128', 'u128le', 'u128be', 'uint', 'uintptr'
})
lex:set_word_list(lexer.FUNCTION_BUILTIN, {
-- compiler built-ins
'abs', 'align_of', 'cap', 'clamp', 'complex', 'compress_values', 'conj', 'expand_values', 'imag',
'jmag', 'kmag', 'len', 'max', 'min', 'offset_of', 'offset_of_by_string', 'offset_of_member',
'offset_of_selector', 'quaternion', 'raw_data', 'real', 'size_of', 'soa_unzip', 'soa_zip',
'swizzle', 'type_info_of', 'type_of', 'typeid_of',
-- base:runtime procs exported as @(builtin)
'append', 'assert', 'card', 'clear', 'copy', 'delete', 'delete_key', 'ensure', 'free',
'free_all', 'inject_at', 'make', 'new', 'new_clone', 'ordered_remove', 'panic', 'pop',
'pop_front', 'reserve', 'resize', 'shrink', 'unimplemented', 'unordered_remove', 'unreachable'
})
lex:set_word_list(lexer.CONSTANT_BUILTIN, {
'false', 'true', 'nil', 'ODIN_ARCH', 'ODIN_BUILD_MODE', 'ODIN_COMPILE_TIMESTAMP', 'ODIN_DEBUG',
'ODIN_DEFAULT_TO_NIL_ALLOCATOR', 'ODIN_DEFAULT_TO_PANIC_ALLOCATOR', 'ODIN_DISABLE_ASSERT',
'ODIN_ENDIAN', 'ODIN_ERROR_POS_STYLE', 'ODIN_NO_BOUNDS_CHECK', 'ODIN_NO_CRT',
'ODIN_NO_ENTRY_POINT', 'ODIN_NO_RTTI', 'ODIN_NO_TYPE_ASSERT', 'ODIN_OPTIMIZATION_MODE',
'ODIN_OS', 'ODIN_PLATFORM_SUBTARGET', 'ODIN_ROOT', 'ODIN_TEST', 'ODIN_VENDOR', 'ODIN_VERSION',
'ODIN_WINDOWS_SUBSYSTEM'
})
lex:set_word_list(lexer.VARIABLE_BUILTIN, {
'context', 'runtime'
})
return lex
-285
View File
@@ -1,285 +0,0 @@
-- pickers: fuzzy file finder, live ripgrep, build errors, yazi file manager, gitu.
local M = {}
-- height of the drop-down fzf panel at the top ('100%' = full screen)
M.height = '100%'
-- ---------------------------------------------------------------------------
-- shared constants
-- ---------------------------------------------------------------------------
local BAT = 'bat --theme=custom_zen --color=always --style=numbers'
local PREVIEW_FILE = BAT .. ' --line-range=:500 {}' -- {} = path
local PREVIEW_AT = BAT .. ' --highlight-line {2} {1}' -- {1}=path {2}=line
local KEYS_NAV = 'ctrl-n:down,ctrl-p:up'
local KEYS_PREVIEW = 'ctrl-/:toggle-preview,shift-up:preview-up,shift-down:preview-down'
-- an Odin error line (-error-pos-style:unix): path.odin:line:col: message
local ERROR_PATTERN = '^(.-%.odin):(%d+):(%d+):'
-- ---------------------------------------------------------------------------
-- helpers
-- ---------------------------------------------------------------------------
-- single-quote a string for the shell
local function q(s)
return "'" .. s:gsub("'", "'\\''") .. "'"
end
-- write a raw escape sequence straight to the terminal
local function tty_write(seq)
local tty = io.open('/dev/tty', 'w')
if tty then
tty:write(seq)
tty:close()
end
end
-- hand the terminal back to vis: re-enter the alt screen and repaint
local function restore_screen()
tty_write('\27[?1049h')
vis:redraw()
end
-- fzf command line: common flags + a list of extra options
local function fzf(prompt, opts)
return 'fzf --height=' .. M.height .. ' --layout=reverse --border'
.. ' --prompt ' .. q(prompt) .. ' ' .. table.concat(opts, ' ')
end
-- "--flag 'value'"
local function opt(flag, value)
return flag .. ' ' .. q(value)
end
-- run an fzf pipeline; returns its output lines and exit code
local function run_fzf(cmd)
-- --height mode draws from the cursor down, so start at the top-left
tty_write('\27[H')
local handle = io.popen(cmd)
if not handle then
vis:info('fzf: failed to start')
return nil
end
local lines = {}
for line in handle:lines() do table.insert(lines, line) end
local _, _, code = handle:close()
restore_screen()
return lines, code
end
-- open a path in the current window, unless it's already the current file.
-- returns false if the buffer has unsaved changes and nothing was opened
local function open_path(path)
local win = vis.win
local current = win and win.file and win.file.path
if current and (current == path or current:sub(-#path - 1) == '/' .. path) then
return true
end
if win and win.file and win.file.modified then
vis:info('Buffer has unsaved changes — :w first (or use :e! manually)')
return false
end
vis:command(string.format("e '%s'", path))
return true
end
-- ---------------------------------------------------------------------------
-- files: fuzzy-find a file, or create one from the query
-- enter open in the current window
-- ctrl-v open in a vertical split
-- alt-enter create a file named after the query
-- ctrl-n/p move down/up
-- ---------------------------------------------------------------------------
function M.files()
local cmd = 'fd --type f --hidden --exclude .git | ' .. fzf('files> ', {
'--print-query --expect=alt-enter,ctrl-v', -- output: query, key, choice
opt('--header', 'enter: open · alt-enter: new file · ctrl-v: vsplit'),
opt('--preview', PREVIEW_FILE),
opt('--preview-window', 'down:75%:border-top'),
opt('--bind', KEYS_NAV),
opt('--bind', KEYS_PREVIEW),
})
local out, code = run_fzf(cmd)
if not out or code == 130 then return end -- failed, or Esc / Ctrl-C
local query, key, choice = out[1], out[2], out[3]
local is_new = key == 'alt-enter' or not choice or choice == ''
local path = is_new and query or choice
if not path or path == '' then return end
if key == 'ctrl-v' then
vis:command(string.format("vsplit '%s'", path))
elseif not open_path(path) then
return
end
if is_new then
vis:info('New file: ' .. path .. ' (written to disk on :w)')
end
end
-- ---------------------------------------------------------------------------
-- grep: live ripgrep search across the project, jump to the match
-- M.grep() start empty, results appear as you type
-- M.grep_selection() visual mode: start with the selected text
-- ---------------------------------------------------------------------------
local function grep_with(query)
-- don't run rg on an empty query (it would match every line)
local reload = '[ -n {q} ] && rg --column --line-number --no-heading'
.. ' --color=always --smart-case --hidden --glob "!.git" -- {q} || true'
local opts = {
'--ansi --disabled', -- let rg do the matching, not fzf
'--delimiter :', -- fields: path:line:col:text
opt('--bind', 'change:reload:' .. reload),
opt('--preview', PREVIEW_AT),
opt('--preview-window', 'down:90%:border-top:+{2}/2'),
opt('--bind', KEYS_PREVIEW),
}
if query then
table.insert(opts, opt('--query', query))
table.insert(opts, opt('--bind', 'start:reload:' .. reload))
end
-- empty stdin: results only appear once there is a query
local out = run_fzf(': | ' .. fzf('rg> ', opts))
local choice = out and out[1]
if not choice or choice == '' then return end
choice = choice:gsub('\27%[[%d;]*m', '') -- strip colour codes
local path, line, col = choice:match('^(.-):(%d+):(%d+):')
if not path or not open_path(path) then return end
vis.win.selection:to(tonumber(line), tonumber(col))
end
function M.grep()
grep_with(nil)
end
function M.grep_selection()
local win = vis.win
local text
if win and win.file and win.selection.range then
text = win.file:content(win.selection.range)
end
-- leave visual mode before fzf takes over the screen
vis.mode = vis.modes.NORMAL
-- first line of the selection only; nothing usable -> plain grep
text = text and text:match('^[^\n]*')
if not text or text:match('^%s*$') then
grep_with(nil)
else
grep_with(text)
end
end
-- ---------------------------------------------------------------------------
-- errors: browse the errors from the last build (the log build.lua writes)
-- enter jump to the error
-- ---------------------------------------------------------------------------
function M.errors()
local f = io.open(require('build').log, 'r')
if not f then
vis:info('No build log yet — run a build first')
return
end
-- keep only the path.odin:line:col: lines; the log comes from a pty, so
-- strip colour escapes and carriage returns first
local errors = {}
for line in f:lines() do
local clean = line:gsub('\27%[[%d;?]*%a', ''):gsub('\r', '')
if clean:match(ERROR_PATTERN) then table.insert(errors, clean) end
end
f:close()
if #errors == 0 then
vis:info('No errors in the last build')
return
end
local tmp = os.tmpname()
f = io.open(tmp, 'w')
if not f then
vis:info('errors: cannot write temp file')
return
end
f:write(table.concat(errors, '\n'), '\n')
f:close()
local cmd = fzf('errors> ', {
'--no-sort', -- keep build order: the first error is the one to fix first
'--delimiter :',
opt('--preview', PREVIEW_AT),
opt('--preview-window', 'down:75%:border-top:+{2}/2'),
opt('--bind', KEYS_NAV),
opt('--bind', KEYS_PREVIEW),
}) .. ' < ' .. q(tmp)
local out = run_fzf(cmd)
os.remove(tmp)
local choice = out and out[1]
if not choice or choice == '' then return end
local path, line, col = choice:match(ERROR_PATTERN)
if not path or not open_path(path) then return end
vis.win.selection:to(tonumber(line), tonumber(col))
end
-- ---------------------------------------------------------------------------
-- fm: browse/create/rename/delete in yazi, then open the file you pick
-- ---------------------------------------------------------------------------
function M.fm()
local tmp = os.tmpname()
os.execute(string.format("yazi --chooser-file='%s' </dev/tty >/dev/tty 2>&1", tmp))
restore_screen()
local f = io.open(tmp, 'r')
local path = f and f:read('*l')
if f then f:close() end
os.remove(tmp)
if path and path ~= '' then open_path(path) end
end
-- ---------------------------------------------------------------------------
-- git: run gitu, then hand the screen back to vis
-- ---------------------------------------------------------------------------
function M.git()
tty_write('\27[?1049l')
os.execute('gitu </dev/tty >/dev/tty 2>&1')
restore_screen()
-- gitu may have changed files on disk (checkout, discard, stash, ...):
-- reload the current buffer if there's nothing unsaved to lose
local win = vis.win
if win and win.file and win.file.path and not win.file.modified then
vis:command('e!')
end
end
-- ---------------------------------------------------------------------------
-- events
-- ---------------------------------------------------------------------------
-- create missing parent directories on save, so a new file like
-- src/util/new.odin can be written even if src/util/ doesn't exist yet
vis.events.subscribe(vis.events.FILE_SAVE_PRE, function(file, path) -- luacheck: no unused args
local dir = path and path:match('^(.*)/[^/]+$')
if dir and dir ~= '' then
os.execute('mkdir -p ' .. q(dir))
end
return true
end)
return M
Submodule config/vis/plugins/vis-ctags deleted from 40a537beaf
-59
View File
@@ -1,59 +0,0 @@
-- vis-minimal-jellybeans-soft
-- Dark neutral background, warm off-white, and muted, low-brightness accents
local bg_dark = '#131313' -- Dark off-black background instead of harsh pure black
local black1 = '#303030'
local black2 = '#585858'
local offwhite0 = '#b9b9b9' -- Softened off-white base (reduces glare)
local offwhite1 = '#b0b0b0'
local offwhite2 = '#888888'
local clear0 = '#00000000'
-- Darkened, lower-contrast Jellybeans accents
local jelly_yellow_muted = '#d9b36a' -- Subdued warm yellow (Methods / Functions)
local jelly_blue_muted = '#75a0bc' -- Muted ice blue (Types / Classes)
local lexers = vis.lexers
-- Default Softened Base
lexers.STYLE_DEFAULT = 'back:'..clear0..',fore:'..offwhite0
lexers.STYLE_NOTHING = 'back:'..bg_dark
lexers.STYLE_COMMENT = 'fore:'..black2
lexers.STYLE_ERROR = 'fore:'..offwhite0
lexers.STYLE_WHITESPACE = ''
lexers.STYLE_EMBEDDED = 'back:'..black1
-- The ONLY Colored Highlights (Deepened / Softened)
lexers.STYLE_FUNCTION = 'fore:'..jelly_yellow_muted
lexers.STYLE_TYPE = 'fore:'..jelly_blue_muted
lexers.STYLE_CLASS = 'fore:'..jelly_blue_muted
-- Neutral / Off-White Text
lexers.STYLE_VARIABLE = 'fore:'..offwhite0
lexers.STYLE_IDENTIFIER = 'fore:'..offwhite0
lexers.STYLE_KEYWORD = 'fore:'..offwhite2
lexers.STYLE_CONSTANT = 'fore:'..offwhite0
lexers.STYLE_DEFINITION = 'fore:'..offwhite0
lexers.STYLE_LABEL = 'fore:'..offwhite0
lexers.STYLE_NUMBER = 'fore:'..offwhite1
lexers.STYLE_OPERATOR = 'fore:'..offwhite0
lexers.STYLE_REGEX = 'fore:'..offwhite1
lexers.STYLE_STRING = 'fore:'..offwhite1
lexers.STYLE_PREPROCESSOR= 'fore:'..offwhite0
lexers.STYLE_TAG = 'fore:'..offwhite0
-- Editor Interface UI
lexers.STYLE_LINENUMBER = 'fore:'..black2
lexers.STYLE_LINENUMBER_CURSOR = 'fore:'..offwhite1
lexers.STYLE_CURSOR = 'back:'..offwhite2
lexers.STYLE_CURSOR_PRIMARY = lexers.STYLE_CURSOR..',fore:'..bg_dark
lexers.STYLE_CURSOR_LINE = 'underlined'
lexers.STYLE_COLOR_COLUMN = 'back:'..black1
lexers.STYLE_SELECTION = 'back:'..black1
lexers.STYLE_STATUS = 'reverse'
lexers.STYLE_STATUS_FOCUSED = 'back:'..black1..',fore:'..offwhite1
lexers.STYLE_SEPARATOR = lexers.STYLE_DEFAULT
lexers.STYLE_INFO = 'fore:default,back:default'
lexers.STYLE_EOF = ''
-36
View File
@@ -1,36 +0,0 @@
local lexers = vis.lexers
lexers.STYLE_DEFAULT = 'fore:#d7d7d7,back:#131313'
lexers.STYLE_NOTHING = ''
lexers.STYLE_CLASS = 'fore:#c4bd97'
lexers.STYLE_COMMENT = 'fore:#7d6d5d'
lexers.STYLE_CONSTANT = 'fore:#cdadad,bold'
lexers.STYLE_DEFINITION = 'fore:#f2d1af,bold'
lexers.STYLE_ERROR = 'fore:#8bc8aa,back:#303030,bold'
lexers.STYLE_FUNCTION = 'fore:#7a96b0'
lexers.STYLE_KEYWORD = 'fore:#787879,bold'
lexers.STYLE_LABEL = 'fore:#7a96b0'
lexers.STYLE_NUMBER = 'fore:#87d7d7'
lexers.STYLE_OPERATOR = 'fore:#d5ce90'
lexers.STYLE_REGEX = 'fore:#d0c195'
lexers.STYLE_STRING = 'fore:#6c836c'
lexers.STYLE_PREPROCESSOR = 'fore:#f2d1af,bold'
lexers.STYLE_TAG = 'fore:#cdadad,bold'
lexers.STYLE_TYPE = 'fore:#7a96b0,bold'
lexers.STYLE_VARIABLE = 'fore:#c9ba9c'
lexers.STYLE_WHITESPACE = ''
lexers.STYLE_EMBEDDED = 'fore:#c09494'
lexers.STYLE_IDENTIFIER = ''
lexers.STYLE_LINENUMBER = 'fore:#585858'
lexers.STYLE_LINENUMBER_CURSOR = 'fore:#666666'
lexers.STYLE_CURSOR = 'back:#585858,keep_attribute'
lexers.STYLE_CURSOR_PRIMARY = 'fore:#1c1c1c,back:#87afaf,bold,keep_attribute'
lexers.STYLE_CURSOR_LINE = 'back:#444444,keep_attribute'
lexers.STYLE_COLOR_COLUMN = 'back:#444444'
lexers.STYLE_SELECTION = 'back:#4a604a'
lexers.STYLE_STATUS = 'back:#262626,fore:#6c836c'
lexers.STYLE_STATUS_FOCUSED = 'back:#303030,fore:#cacaca,bold'
lexers.STYLE_SEPARATOR = ''
lexers.STYLE_INFO = ''
lexers.STYLE_EOF = 'fore:#585858'
-108
View File
@@ -1,108 +0,0 @@
require('vis')
require('plugins/vis-ctags')
local build = require('build')
local pickers = require('pickers')
-- ---------------------------------------------------------------------------
-- Global options, key mappings and commands
-- ---------------------------------------------------------------------------
vis.events.subscribe(vis.events.INIT, function()
vis:command('set escdelay 1')
vis:command('set autoindent off')
vis:command('set ignorecase on')
vis:command('set theme custom_zen')
-- jump list
vis:map(vis.modes.NORMAL, "<M-j>", "g>")
vis:map(vis.modes.NORMAL, "<M-k>", "g<")
-- pickers
vis:map(vis.modes.NORMAL, '<C-p>', pickers.files, 'Fuzzy find / create a file')
vis:map(vis.modes.NORMAL, '<C-f>', pickers.grep, 'Live grep the project with ripgrep')
vis:map(vis.modes.VISUAL, '<C-f>', pickers.grep_selection, 'Live grep for the selected text')
vis:map(vis.modes.NORMAL, '<C-e>', pickers.fm, 'terminal file manager')
vis:map(vis.modes.NORMAL, '<M-e>', pickers.errors, 'Browse errors from the last build')
-- picker commands
vis:command_register('files', function() pickers.files() return true end, 'Fuzzy find / create a file')
vis:command_register('grep', function() pickers.grep() return true end, 'Live grep the project with ripgrep')
vis:command_register('fm', function() pickers.fm() return true end, 'Pick a file with a terminal file manager')
vis:command_register('git', function() pickers.git() return true end, 'Open gitu')
vis:command_register('errors', function() pickers.errors() return true end, 'Browse errors from the last build')
-- build.sh setup
vis:map(vis.modes.NORMAL, '<C-b>', function() build.run({}) end, 'Run ./build.sh')
vis:command_register('build', function(argv)
local args = {}
for i = 1, #argv do table.insert(args, argv[i]) end
build.run(args)
return true
end, 'Run ./build.sh with optional flags, e.g :build run')
-- system clipboard
local function clip_copy()
vis:feedkeys(':>wl-copy 2>/dev/null<Enter>')
vis:feedkeys('<Escape>') -- drop the selection / leave visual mode
end
local function clip_paste()
vis:feedkeys(':<wl-paste --no-newline<Enter>')
vis:feedkeys('<Escape>') -- clear any selection left on the pasted text
end
vis:map(vis.modes.VISUAL, '<C-c>', clip_copy, 'Copy selection to system clipboard')
vis:map(vis.modes.VISUAL, '<C-v>', clip_paste, 'Paste system clipboard at cursor')
vis:map(vis.modes.NORMAL, '<C-v>', clip_paste, 'Paste system clipboard at cursor')
-- ctag navigation
vis:map(vis.modes.NORMAL, 'gd', '<C-]>', 'Go to definition (ctags picker)')
end)
-- ---------------------------------------------------------------------------
-- Per-window options
-- ---------------------------------------------------------------------------
vis.events.subscribe(vis.events.WIN_OPEN, function(win)
vis:command('set tabwidth 4')
vis:command('set expandtab on')
vis:command('set number on')
end)
-- ---------------------------------------------------------------------------
-- Syntax highlighting: ~/.config/vis/lexers/odin.lua
-- ---------------------------------------------------------------------------
if vis.ftdetect then
-- vis 0.9 and earlier
if vis.ftdetect.filetypes then
vis.ftdetect.filetypes.odin = { ext = { '%.odin$' } }
end
end
-- ---------------------------------------------------------------------------
-- On save: strip trailing spaces/tabs and extra blank lines at the end of
-- the file. Skips files where trailing whitespace means something.
-- ---------------------------------------------------------------------------
local keep_whitespace = { md = true, markdown = true, diff = true, patch = true }
vis.events.subscribe(vis.events.FILE_SAVE_PRE, function(file, path)
local ext = path and path:match('%.(%w+)$')
if ext and keep_whitespace[ext:lower()] then return true end
-- trailing spaces/tabs (not \r, so CRLF files stay CRLF);
-- read every line first, then rewrite only the ones that changed
local trimmed, n = {}, 0
for line in file:lines_iterator() do
n = n + 1
local s = line:find('[ \t]+$')
if s then trimmed[n] = line:sub(1, s - 1) end
end
for i, line in pairs(trimmed) do file.lines[i] = line end
-- blank lines at the end: keep exactly one final newline
local size = file.size
local tail = file:content(math.max(0, size - 256), math.min(size, 256))
local extra = tail:match('\n(\n+)$')
if extra then file:delete(size - #extra, #extra) end
return true
end)
-119
View File
@@ -1,119 +0,0 @@
[app]
overall = { bg = "#131313" }
[icon]
dirs = []
prepend_conds = [ { if = "dir", text = ">", fg = "#7a96b0" } ]
[mgr]
cwd = { fg = "#7a96b0", bold = true }
find_keyword = { fg = "#f2d1af", bold = true }
find_position = { fg = "#87d7d7" }
symlink_target = { fg = "#87afaf", italic = true }
marker_copied = { fg = "#6c836c", bg = "#6c836c" }
marker_cut = { fg = "#cdadad", bg = "#cdadad" }
marker_marked = { fg = "#f2d1af", bg = "#f2d1af" }
marker_selected = { fg = "#7a96b0", bg = "#7a96b0" }
count_copied = { fg = "#131313", bg = "#6c836c" }
count_cut = { fg = "#131313", bg = "#cdadad" }
count_selected = { fg = "#131313", bg = "#7a96b0" }
border_style = { fg = "#585858" }
# code preview uses the same theme file as bat
syntect_theme = "/home/jason/.config/bat/themes/custom_zen.tmTheme"
[indicator]
parent = { bg = "#303030" }
current = { bg = "#444444" }
preview = { bg = "#303030" }
[tabs]
active = { fg = "#131313", bg = "#7a96b0", bold = true }
inactive = { fg = "#d7d7d7", bg = "#303030" }
[mode]
normal_main = { fg = "#131313", bg = "#7a96b0", bold = true }
normal_alt = { fg = "#7a96b0", bg = "#303030" }
select_main = { fg = "#131313", bg = "#6c836c", bold = true }
select_alt = { fg = "#6c836c", bg = "#303030" }
unset_main = { fg = "#131313", bg = "#cdadad", bold = true }
unset_alt = { fg = "#cdadad", bg = "#303030" }
[status]
overall = { fg = "#d7d7d7", bg = "#262626" }
perm_sep = { fg = "#585858" }
perm_type = { fg = "#7a96b0" }
perm_read = { fg = "#a0a0a0" }
perm_write = { fg = "#cdadad" }
perm_exec = { fg = "#6c836c" }
progress_label = { fg = "#d7d7d7", bold = true }
progress_normal = { fg = "#7a96b0", bg = "#303030" }
progress_error = { fg = "#cdadad", bg = "#303030" }
[which]
border = { fg = "#585858" }
mask = { bg = "#262626" }
cand = { fg = "#87d7d7" }
rest = { fg = "#585858" }
desc = { fg = "#a0a0a0" }
separator_style = { fg = "#585858" }
[confirm]
border = { fg = "#7a96b0" }
title = { fg = "#7a96b0" }
btn_yes = { fg = "#131313", bg = "#7a96b0" }
btn_no = { fg = "#d7d7d7", bg = "#303030" }
[spot]
border = { fg = "#7a96b0" }
title = { fg = "#7a96b0" }
tbl_col = { fg = "#7a96b0" }
tbl_cell = { fg = "#d7d7d7", bg = "#444444" }
[notify]
title_info = { fg = "#6c836c" }
title_warn = { fg = "#f2d1af" }
title_error = { fg = "#cdadad" }
[pick]
border = { fg = "#7a96b0" }
active = { fg = "#cdadad", bold = true }
[input]
border = { fg = "#7a96b0" }
selected = { bg = "#4a604a" }
[cmp]
border = { fg = "#7a96b0" }
active = { bg = "#444444" }
[tasks]
border = { fg = "#7a96b0" }
hovered = { fg = "#cdadad", underline = true }
[help]
border = { fg = "#585858" }
chord = { fg = "#87d7d7" }
action = { fg = "#6c836c" }
hovered = { bg = "#444444" }
[filetype]
rules = [
# images
{ mime = "image/*", fg = "#87d7d7" },
# audio / video
{ mime = "{audio,video}/*", fg = "#f2d1af" },
# archives
{ mime = "application/{zip,gzip,x-tar,x-bzip*,x-xz,x-7z-compressed,x-rar}", fg = "#cdadad" },
# documents
{ mime = "application/{pdf,doc,rtf}", fg = "#a0a0a0" },
# special files
{ url = "*", is = "orphan", fg = "#131313", bg = "#cdadad" },
{ url = "*", is = "exec", fg = "#6c836c" },
# directories
{ url = "*/", fg = "#7a96b0" },
]
-8
View File
@@ -1,8 +0,0 @@
{
"folders":
[
{
"path": "."
}
]
}
-516
View File
@@ -1,516 +0,0 @@
{
"auto_complete":
{
"selected_items":
[
]
},
"buffers":
[
{
"file": "config/fish/config.fish",
"file_size": 4388,
"file_write_time": 134353912965388191,
"settings":
{
"buffer_size": 4386,
"encoding": "UTF-8",
"line_ending": "Unix"
},
"undo_stack":
[
[
12,
1,
"nv_vi_o",
{
"count": 1,
"mode": "mode_internal_normal",
"register": "\""
},
"AQAAAOgCAAAAAAAA6QIAAAAAAAAAAAAA",
"CgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8L8AAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwvwAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8L8AAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwvwAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8L8AAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwvwAAAAABAAAA0wIAAAAAAADTAgAAAAAAAAAAAAAAAPC/"
],
[
14,
1,
"nv_vi_paste",
{
"before_cursor": false,
"count": 1,
"mode": "mode_internal_normal",
"register": "\""
},
"AQAAAOoCAAAAAAAAIAMAAAAAAAAAAAAA",
"CgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8L8AAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwvwAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8L8AAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwvwAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8L8AAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwvwAAAAABAAAA6QIAAAAAAADpAgAAAAAAAAAAAAAAAPC/"
],
[
17,
1,
"nv_vi_d",
{
"count": 1,
"mode": "mode_visual_line",
"register": "\""
},
"AQAAAOkCAAAAAAAA6QIAAAAAAAABAAAACg",
"CgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8L8AAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwvwAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8L8AAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwvwAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8L8AAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwvwAAAAABAAAA6QIAAAAAAADqAgAAAAAAAAAAAAAAAPC/"
],
[
18,
1,
"nv_vi_o",
{
"count": 1,
"mode": "mode_internal_normal",
"register": "\""
},
"AQAAAB8DAAAAAAAAIAMAAAAAAAAAAAAA",
"CgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8L8AAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwvwAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8L8AAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwvwAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8L8AAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwvwAAAAABAAAA6QIAAAAAAADpAgAAAAAAAAAAAAAAAPC/"
],
[
30,
1,
"sequence",
{
"commands":
[
[
"nv_vi_c",
{
"count": 1,
"mode": "mode_visual",
"register": "\""
}
],
[
"insert",
{
"characters": "home/jason/"
}
],
[
"insert",
{
"characters": "ThirdPart"
}
],
[
"insert",
{
"characters": "y/ols"
}
]
]
},
"GgAAAAYDAAAAAAAABgMAAAAAAAAQAAAAcGF0aC90by9vbHMvcmVwbwYDAAAAAAAABwMAAAAAAAAAAAAABwMAAAAAAAAIAwAAAAAAAAAAAAAIAwAAAAAAAAkDAAAAAAAAAAAAAAkDAAAAAAAACgMAAAAAAAAAAAAACgMAAAAAAAALAwAAAAAAAAAAAAALAwAAAAAAAAwDAAAAAAAAAAAAAAwDAAAAAAAADQMAAAAAAAAAAAAADQMAAAAAAAAOAwAAAAAAAAAAAAAOAwAAAAAAAA8DAAAAAAAAAAAAAA8DAAAAAAAAEAMAAAAAAAAAAAAAEAMAAAAAAAARAwAAAAAAAAAAAAARAwAAAAAAABIDAAAAAAAAAAAAABIDAAAAAAAAEwMAAAAAAAAAAAAAEwMAAAAAAAAUAwAAAAAAAAAAAAAUAwAAAAAAABUDAAAAAAAAAAAAABUDAAAAAAAAFgMAAAAAAAAAAAAAFgMAAAAAAAAXAwAAAAAAAAAAAAAXAwAAAAAAABgDAAAAAAAAAAAAABgDAAAAAAAAGQMAAAAAAAAAAAAAGQMAAAAAAAAaAwAAAAAAAAAAAAAaAwAAAAAAABsDAAAAAAAAAAAAABsDAAAAAAAAHAMAAAAAAAAAAAAAHAMAAAAAAAAdAwAAAAAAAAAAAAAdAwAAAAAAAB4DAAAAAAAAAAAAAB4DAAAAAAAAHwMAAAAAAAAAAAAA",
"CgAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8L8AAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwvwAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8L8AAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwvwAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPC/AAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA8L8AAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADwvwAAAAABAAAABgMAAAAAAAAWAwAAAAAAAAAAAAAAAPC/"
],
[
17,
1,
"nv_vi_d",
{
"count": 1,
"mode": "mode_visual",
"register": "\""
},
"AQAAAGYFAAAAAAAAZgUAAAAAAAAEAAAAZmluZA",
"AQAAAAAAAAABAAAAZgUAAAAAAABqBQAAAAAAAAAAAAAAAPC/"
]
]
},
{
"file": "config/vim/vimrc",
"file_size": 2854,
"file_write_time": 134345438284510269,
"settings":
{
"buffer_size": 2854,
"line_ending": "Unix"
},
"undo_stack":
[
]
}
],
"build_system": "",
"build_system_choices":
[
],
"build_varint": "",
"command_palette":
{
"height": 0.0,
"last_filter": "",
"selected_items":
[
[
"lsp res",
"LSP: Restart Server"
],
[
"lsp",
"Preferences: LSP Server Configurations"
],
[
"setting",
"Preferences: Settings"
],
[
"set",
"Selection: Expand to Block"
],
[
"lsp se",
"Preferences: LSP Server Configurations"
],
[
"lsp server",
"Preferences: LSP Server Configurations"
],
[
"inst",
"Package Control: Install Package"
],
[
"key",
"Preferences: Key Bindings"
],
[
"remo",
"Package Control: Remove Package"
],
[
"color",
"UI: Select Color Scheme"
],
[
"insta",
"Package Control: Install Package"
],
[
"remove",
"Package Control: Remove Package"
],
[
"theme",
"UI: Customize Theme"
],
[
"instal",
"Package Control: Install Package"
],
[
"color scheme",
"UI: Select Color Scheme"
],
[
"cust",
"UI: Customize Color Scheme"
],
[
"settings",
"Preferences: Settings"
],
[
"pack",
"Package Control: Install Package"
],
[
"list pac",
"Package Control: List Packages"
],
[
"package inst",
"Package Control: Install Package"
]
],
"width": 0.0
},
"console":
{
"height": 124.0,
"history":
[
]
},
"distraction_free":
{
"menu_visible": true,
"show_minimap": false,
"show_open_files": false,
"show_tabs": false,
"side_bar_visible": false,
"status_bar_visible": false
},
"expanded_folders":
[
"/home/jason/Projects/dotfiles",
"/home/jason/Projects/dotfiles/config"
],
"file_history":
[
"/home/jason/Projects/Games/pong_raylib/src/audio_manager.odin",
"/home/jason/Projects/Games/pong_raylib/src/main.odin",
"/home/jason/Projects/Games/pong_raylib/src/entity_player.odin",
"/home/jason/.config/sublime-text/Packages/User/LanguageServers.sublime-settings",
"/home/jason/.config/sublime-text/Packages/LSP/LanguageServers.sublime-settings",
"/home/jason/.config/sublime-text/Packages/User/Mariana.sublime-color-scheme",
"/home/jason/.config/sublime-text/Packages/Color Scheme - Default/Mariana.sublime-color-scheme"
],
"find":
{
"height": 42.0
},
"find_in_files":
{
"height": 158.0,
"where_history":
[
]
},
"find_state":
{
"case_sensitive": false,
"find_history":
[
],
"highlight": true,
"in_selection": false,
"preserve_case": false,
"regex": false,
"replace_history":
[
],
"reverse": false,
"scrollbar_highlights": true,
"show_context": true,
"use_buffer2": true,
"use_gitignore": true,
"whole_word": false,
"wrap": true
},
"groups":
[
{
"sheets":
[
{
"buffer": 0,
"file": "config/fish/config.fish",
"selected": true,
"semi_transient": false,
"settings":
{
"buffer_size": 4386,
"regions":
{
},
"selection":
[
[
1382,
1382
]
],
"settings":
{
"_nv_visual_sel_mode": "mode_visual",
"lsp_uri": "file:///home/jason/Projects/dotfiles/config/fish/config.fish",
"syntax": "Packages/Text/Plain text.tmLanguage",
"tab_size": 4,
"translate_tabs_to_spaces": true
},
"translation.x": 0.0,
"translation.y": 792.0,
"zoom_level": 1.0
},
"stack_index": 0,
"stack_multiselect": false,
"type": "text"
},
{
"buffer": 1,
"file": "config/vim/vimrc",
"semi_transient": false,
"settings":
{
"buffer_size": 2854,
"regions":
{
},
"selection":
[
[
0,
0
]
],
"settings":
{
"highlight_line": false,
"lsp_uri": "file:///home/jason/Projects/dotfiles/config/vim/vimrc",
"syntax": "Packages/Text/Plain text.tmLanguage",
"tab_size": 2,
"translate_tabs_to_spaces": true
},
"translation.x": 0.0,
"translation.y": 0.0,
"zoom_level": 1.0
},
"stack_index": 1,
"stack_multiselect": false,
"type": "text"
}
]
}
],
"incremental_find":
{
"height": 42.0
},
"input":
{
"height": 59.0
},
"layout":
{
"cells":
[
[
0,
0,
1,
1
]
],
"cols":
[
0.0,
1.0
],
"rows":
[
0.0,
1.0
]
},
"menu_visible": true,
"output.LSP Log Panel":
{
"height": 0.0,
"history":
[
]
},
"output.diagnostics":
{
"height": 0.0,
"history":
[
]
},
"output.exec":
{
"height": 1123.0,
"history":
[
]
},
"output.find_results":
{
"height": 0.0,
"history":
[
]
},
"output.mdpopups":
{
"height": 0.0,
"history":
[
]
},
"pinned_build_system": "",
"project": "dotfiles.sublime-project",
"replace":
{
"height": 80.0
},
"save_all_on_build": true,
"select_file":
{
"height": 0.0,
"last_filter": "",
"selected_items":
[
[
"fish",
"config/fish/config.fish"
],
[
"main",
"src/main.odin"
],
[
"ene",
"src/entity_player.odin"
]
],
"width": 0.0
},
"select_project":
{
"height": 500.0,
"last_filter": "peb",
"selected_items":
[
[
"peb",
"~/Projects/pebble/pebble.sublime-project"
],
[
"min",
"~/Projects/min_editor/min_editor.sublime-project"
],
[
"",
"~/Projects/dotfiles/dotfiles.sublime-project"
],
[
"pong",
"~/Projects/pong_raylib/pong_odin.sublime-project"
],
[
"dot",
"~/Projects/dotfiles/dotfiles.sublime-project"
]
],
"width": 380.0
},
"select_symbol":
{
"height": 0.0,
"last_filter": "",
"selected_items":
[
],
"width": 0.0
},
"selected_group": 0,
"settings":
{
"_vintageous_reset_during_init": true,
"lsp_show_inlay_hints": false
},
"show_minimap": true,
"show_open_files": false,
"show_tabs": false,
"side_bar_visible": true,
"side_bar_width": 328.0,
"status_bar_visible": true,
"template_settings":
{
}
}
+6 -8
View File
@@ -84,12 +84,9 @@ if [ "$DO_LINKS" = true ]; then
SKIPPED=0
# Modern shell & editor setup
link_file "$DOTFILES_DIR/config/niri/config.kdl" "$HOME/.config/niri/config.kdl"
link_file "$DOTFILES_DIR/config/niri/dms/binds.kdl" "$HOME/.config/niri/dms/binds.kdl"
link_file "$DOTFILES_DIR/config/fish/config.fish" "$HOME/.config/fish/config.fish"
link_file "$DOTFILES_DIR/config/kanata" "$HOME/.config/kanata"
link_file "$DOTFILES_DIR/config/vis" "$HOME/.config/vis"
link_file "$DOTFILES_DIR/config/yazi" "$HOME/.config/yazi"
link_file "$DOTFILES_DIR/config/emacs" "$HOME/.config/emacs"
link_file "$DOTFILES_DIR/scripts/bin" "$HOME/.local/bin"
echo ""
@@ -152,11 +149,12 @@ if [ "$DO_INSTALL" = true ]; then
# network & download
wget curl ca-certificates gnupg rsync
# core utilities & CLI tools
git vis yazi fd jq fzf ripgrep tree direnv make
bat ark unzip p7zip tar gzip bzip2 xz kanata
git fd jq fzf ripgrep tree direnv make
bat unzip 7zip tar gzip bzip2 xz ark
mpv xfce4-screenshooter xkill xwininfo
xdotool wmctrl
# build & dev tools
alacritty base-devel valgrind tree-sitter-cli go nodejs
# cli text editing
emacs-x11 alacritty base-devel valgrind go
)
MISSING_PACKAGES=()
+281
View File
@@ -0,0 +1,281 @@
#!/usr/bin/env bash
# focus - small window helpers for xfwm4
# focus next|prev move focus right/left through windows (stops at the ends)
# focus up|down focus the nearest window directly above/below, if any
# focus tile master/stack tiling: active window left, the rest stacked right
# (a single window is simply maximized)
# Deps: wmctrl xdotool xprop xwininfo
set -u
# ---- config -----------------------------------------------------------------
MASTER_RATIO=50 # master width as % of the usable width
GAP=0 # px between windows and around the screen edge
USE_WORKAREA=1 # 1 = ask xfwm4 for the usable area (already excludes panels)
PANEL_TOP=0 # extra px to keep free on each side; if USE_WORKAREA=0,
PANEL_BOTTOM=0 # put your panel sizes here instead
PANEL_LEFT=0
PANEL_RIGHT=0
SETTLE=0.05 # seconds to let xfwm4 finish un-maximizing before moving
VERIFY=1 # 1 = measure each window afterwards and fix pixel offsets
VERIFY_TRIES=2 # correction passes per window
# ---- generic helpers --------------------------------------------------------
die() { echo "${0##*/}: $*" >&2; exit 1; }
usage() { echo "usage: ${0##*/} next|prev|up|down|tile" >&2; exit 1; }
# window geometry from wmctrl, keyed by id (filled by collect_windows)
declare -A GX GY GW GH
need() {
local c
for c in "$@"; do command -v "$c" >/dev/null || die "missing dependency: $c"; done
}
# Active window id in wmctrl's 0x%08x format (0x00000000 if none)
active_window() {
local d
d=$(xdotool getactivewindow 2>/dev/null) || d=0
printf '0x%08x' "$d"
}
# index_of ID -> prints index in WINS, or -1
index_of() {
local i
for i in "${!WINS[@]}"; do
[[ ${WINS[i]} == "$1" ]] && { echo "$i"; return; }
done
echo -1
}
# collect_windows focus|tile -> fills WINS, ordered left to right on screen
# (then top to bottom), so "next" moves right and "prev" moves left.
# focus: normal windows + dialogs, minimized included (focusing restores them)
# vert: normal windows + dialogs, minimized skipped (they have no real position)
# tile: normal windows only, minimized skipped
collect_windows() {
local mode=$1 cur id desk x y w h props
local -a found=()
cur=$(xdotool get_desktop) || die "cannot read current desktop"
WINS=()
while read -r id desk x y w h _; do
[[ $desk == "$cur" || $desk == -1 ]] || continue # this desktop or sticky
props=$(xprop -id "$id" _NET_WM_WINDOW_TYPE _NET_WM_STATE 2>/dev/null) || continue
if [[ $props == *"_NET_WM_WINDOW_TYPE(ATOM)"* ]]; then
if [[ $mode == tile ]]; then
[[ $props == *_NET_WM_WINDOW_TYPE_NORMAL* ]] || continue
else
[[ $props == *_NET_WM_WINDOW_TYPE_NORMAL* || $props == *_NET_WM_WINDOW_TYPE_DIALOG* ]] || continue
fi
fi
[[ $props == *_NET_WM_STATE_SKIP_TASKBAR* ]] && continue # panels, desktop, notifyd
[[ $mode != focus && $props == *_NET_WM_STATE_HIDDEN* ]] && continue
GX[$id]=$x GY[$id]=$y GW[$id]=$w GH[$id]=$h
found+=("$x $y $id")
done < <(wmctrl -lG)
(( ${#found[@]} )) || return 0
while read -r _ _ id; do WINS+=("$id"); done < <(printf '%s\n' "${found[@]}" | sort -n -k1,1 -k2,2)
}
# ---- focus ------------------------------------------------------------------
focus_cycle() {
local dir=$1 n idx
collect_windows focus
n=${#WINS[@]}
(( n > 0 )) || return 0
idx=$(index_of "$(active_window)")
if (( idx < 0 )); then
# nothing of ours focused (desktop/panel): enter at the matching end
[[ $dir == next ]] && idx=0 || idx=$(( n - 1 ))
elif [[ $dir == next ]]; then
(( idx < n - 1 )) || return 0 # already rightmost: stop, no wrap
(( idx += 1 ))
else
(( idx > 0 )) || return 0 # already leftmost: stop, no wrap
(( idx -= 1 ))
fi
wmctrl -ia "${WINS[idx]}"
}
# focus_vertical up|down -- nearest window straight above/below the active one.
# "Straight above/below" = shares some horizontal span with it. No match: do nothing.
focus_vertical() {
local dir=$1 act id ax1 ax2 acy cy dy best="" best_dy=-1
collect_windows vert
act=$(active_window)
[[ -n ${GX[$act]+x} ]] || return 0
ax1=${GX[$act]}; ax2=$(( ax1 + GW[$act] ))
acy=$(( GY[$act] + GH[$act] / 2 ))
for id in "${WINS[@]}"; do
[[ $id == "$act" ]] && continue
(( GX[$id] < ax2 && GX[$id] + GW[$id] > ax1 )) || continue # no horizontal overlap
cy=$(( GY[$id] + GH[$id] / 2 ))
if [[ $dir == up ]]; then dy=$(( acy - cy )); else dy=$(( cy - acy )); fi
(( dy > 0 )) || continue # wrong side
if (( best_dy < 0 || dy < best_dy )); then best=$id best_dy=$dy; fi
done
[[ -n $best ]] && wmctrl -ia "$best"
return 0
}
# ---- geometry helpers -------------------------------------------------------
# workarea -> WA_X WA_Y WA_W WA_H (usable rectangle on this desktop)
workarea() {
local cur raw i
local -a a
cur=$(xdotool get_desktop)
raw=$(xprop -root _NET_WORKAREA 2>/dev/null)
if (( USE_WORKAREA )) && [[ $raw == *=* ]]; then
IFS=', ' read -r -a a <<< "${raw#*= }"
i=$(( cur * 4 ))
(( i + 3 < ${#a[@]} )) || i=0
WA_X=${a[i]} WA_Y=${a[i+1]} WA_W=${a[i+2]} WA_H=${a[i+3]}
else
read -r WA_W WA_H < <(xdotool getdisplaygeometry)
WA_X=0 WA_Y=0
fi
(( WA_X += PANEL_LEFT, WA_Y += PANEL_TOP,
WA_W -= PANEL_LEFT + PANEL_RIGHT, WA_H -= PANEL_TOP + PANEL_BOTTOM ))
}
# frame_extents ID -> FL FR FT FB (decoration size; 0 if not reported)
frame_extents() {
local raw
FL=0 FR=0 FT=0 FB=0
raw=$(xprop -id "$1" _NET_FRAME_EXTENTS 2>/dev/null) || return 0
[[ $raw == *=* ]] && IFS=', ' read -r FL FR FT FB <<< "${raw#*= }"
}
# client_position ID -> CX CY (real on-screen top-left of the client area)
client_position() {
read -r CX CY < <(xwininfo -id "$1" 2>/dev/null | awk '
/Absolute upper-left X/ {x=$NF} /Absolute upper-left Y/ {y=$NF}
END {print x+0, y+0}')
}
# ---- window actions ---------------------------------------------------------
# Drop states that make xfwm4 ignore move/resize (maximized, xfwm4 edge-tiling, fullscreen, shaded)
reset_state() {
wmctrl -ir "$1" -b remove,maximized_vert,maximized_horz
wmctrl -ir "$1" -b remove,fullscreen,shaded
}
# maximize ID -- clear fullscreen/shaded, then set the WM's maximized state
maximize() {
wmctrl -ir "$1" -b remove,fullscreen,shaded
wmctrl -ir "$1" -b add,maximized_vert,maximized_horz
wmctrl -ia "$1"
}
# place ID X Y W H -- X Y W H is the OUTER rectangle including decorations
place() {
local id=$1 x=$2 y=$3 w=$4 h=$5 cw ch
frame_extents "$id"
cw=$(( w - FL - FR )); (( cw < 1 )) && cw=1
ch=$(( h - FT - FB )); (( ch < 1 )) && ch=1
# gravity 1 (NorthWest): x,y = top-left of the frame, w,h = client size
wmctrl -ir "$id" -e "1,$x,$y,$cw,$ch"
}
# verify ID X Y W H -- measure where the window really landed and nudge it if off
verify() {
local id=$1 x=$2 y=$3 w=$4 h=$5 try dx dy
for (( try = 0; try < VERIFY_TRIES; try++ )); do
frame_extents "$id"
client_position "$id"
dx=$(( CX - (x + FL) ))
dy=$(( CY - (y + FT) ))
(( dx == 0 && dy == 0 )) && return 0
place "$id" $(( x - dx )) $(( y - dy )) "$w" "$h"
sleep "$SETTLE"
done
}
# ---- layout -----------------------------------------------------------------
# layout_master_stack N -> fills RECTS with "x y w h" outer rectangles, index 0 = master
# Integer remainders are handed out so the tiles cover the area exactly, no 1px gaps.
layout_master_stack() {
local n=$1 x0 y0 w0 h0 mw sw sx avail base rem i y hh
RECTS=()
x0=$(( WA_X + GAP )); y0=$(( WA_Y + GAP ))
w0=$(( WA_W - 2 * GAP )); h0=$(( WA_H - 2 * GAP ))
if (( n == 1 )); then
RECTS+=("$x0 $y0 $w0 $h0")
return
fi
mw=$(( (w0 - GAP) * MASTER_RATIO / 100 ))
sw=$(( w0 - GAP - mw ))
sx=$(( x0 + mw + GAP ))
RECTS+=("$x0 $y0 $mw $h0")
(( n -= 1 ))
avail=$(( h0 - (n - 1) * GAP ))
base=$(( avail / n )); rem=$(( avail % n ))
y=$y0
for (( i = 0; i < n; i++ )); do
hh=$(( base + (i < rem ? 1 : 0) ))
RECTS+=("$sx $y $sw $hh")
(( y += hh + GAP ))
done
}
# ---- tile -------------------------------------------------------------------
tile() {
local n idx master i
(( VERIFY )) && need xwininfo
collect_windows tile
n=${#WINS[@]}
(( n > 0 )) || return 0
# only one window: let xfwm4 maximize it (real maximized state, not fullscreen)
if (( n == 1 )); then
maximize "${WINS[0]}"
return 0
fi
# active window becomes master, the rest keep their order in the stack
idx=$(index_of "$(active_window)")
if (( idx > 0 )); then
master=${WINS[idx]}
WINS=("$master" "${WINS[@]:0:idx}" "${WINS[@]:idx+1}")
fi
workarea
layout_master_stack "$n"
for i in "${!WINS[@]}"; do reset_state "${WINS[i]}"; done
sleep "$SETTLE"
for i in "${!WINS[@]}"; do
# shellcheck disable=SC2086
place "${WINS[i]}" ${RECTS[i]}
done
if (( VERIFY )); then
sleep "$SETTLE"
for i in "${!WINS[@]}"; do
# shellcheck disable=SC2086
verify "${WINS[i]}" ${RECTS[i]}
done
fi
wmctrl -ia "${WINS[0]}"
}
# ---- main -------------------------------------------------------------------
need wmctrl xdotool xprop
case ${1:-next} in
next|prev) focus_cycle "${1:-next}" ;;
up|down) focus_vertical "$1" ;;
tile) tile ;;
*) usage ;;
esac
+64
View File
@@ -0,0 +1,64 @@
#!/bin/bash
# ---------------------------------------------------------------------------------
# Steam install recipe
# Target: Void Linux (x86_64 glibc only)
# Enables nonfree + multilib repos, installs Steam, 32-bit runtime libs, GPU drivers
# Usage: GPU=amd|nvidia|intel ./steam.sh (default: amd)
# ---------------------------------------------------------------------------------
set -e
GPU="${GPU:-amd}"
if [ "$(xbps-uhelper arch)" != "x86_64" ]; then
echo "Steam requires x86_64 glibc. Aborting." >&2
exit 1
fi
echo "Enabling nonfree and multilib repositories..."
sudo xbps-install -Sy void-repo-nonfree void-repo-multilib void-repo-multilib-nonfree
sudo xbps-install -Sy # sync the newly added repos
echo "Installing Steam and 32-bit runtime libraries..."
sudo xbps-install -y \
steam \
steam-udev-rules \
libgcc-32bit \
libstdc++-32bit \
libdrm-32bit \
libglvnd-32bit \
mesa-dri \
mesa-dri-32bit \
vulkan-loader \
vulkan-loader-32bit \
alsa-lib-32bit \
libpulseaudio-32bit
echo "Installing GPU drivers ($GPU)..."
case "$GPU" in
amd)
sudo xbps-install -y \
mesa-vulkan-radeon mesa-vulkan-radeon-32bit \
mesa-vaapi
;;
nvidia)
sudo xbps-install -y nvidia nvidia-libs nvidia-libs-32bit
;;
intel)
sudo xbps-install -y \
mesa-vulkan-intel mesa-vulkan-intel-32bit \
intel-video-accel
;;
*)
echo "Unknown GPU '$GPU' (amd|nvidia|intel)" >&2
exit 1
;;
esac
echo "Reloading udev rules..."
sudo udevadm control --reload
sudo udevadm trigger
echo ""
echo "✓ Steam installed. Log out/in (or reboot) before first launch."
echo ""
+67
View File
@@ -0,0 +1,67 @@
#!/bin/sh
# square-xfwm4-corners.sh (v2)
# Makes ~/.themes/Default-Square: an exact copy of the xfwm4 "Default" theme
# with square top corners, keeping the thin dark outline and highlight.
# Usage: ./square-xfwm4-corners.sh [ACTIVE_TITLE_COLOR]
# e.g. ./square-xfwm4-corners.sh '#ff9900' (or a name like orange)
# no argument = keep the theme's normal title colour
set -eu
ACTIVE_TITLE_COLOR="${1:-}"
case "$ACTIVE_TITLE_COLOR" in
''|\#[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]|\#[0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]) ;;
*[!a-zA-Z]*) echo "Colour must be #rgb, #rrggbb or a plain name like orange"; exit 1 ;;
esac
SRC=/usr/share/themes/Default/xfwm4
DST_THEME="$HOME/.themes/Default-Square"
DST="$DST_THEME/xfwm4"
[ -d "$SRC" ] || { echo "Missing $SRC"; exit 1; }
# Warn if the installed theme differs from the version these overlays were made from
want=349096e386ab06feddff55aa404bed0a
have=$(md5sum "$SRC/top-left-active.png" 2>/dev/null | cut -d' ' -f1 || true)
[ "$have" = "$want" ] || echo "Warning: your Default theme differs from the expected version; result may be slightly off."
# Fresh copy (also cleans up the result of the first version of this script)
rm -rf "$DST_THEME"
mkdir -p "$DST_THEME"
cp -r "$SRC" "$DST"
# 1) Base layer (XPM): make the transparent corner pixels use the frame colour
for f in "$DST"/top-left-*.xpm "$DST"/top-right-*.xpm; do
awk '
/"[^"]*"/ && !hdr { hdr = 1; split(substr($0, index($0,"\"")+1), a, /[ \t"]+/); nc = a[3]; cpp = a[4]; print; next }
hdr && seen < nc && /"/ {
seen++; s = substr($0, index($0,"\"")+1); s = substr(s, 1, index(s,"\"")-1)
key[seen] = substr(s, 1, cpp); spec[seen] = substr(s, cpp+1); pre[seen] = substr($0, 1, index($0,"\"")-1)
isnone[seen] = (tolower(spec[seen]) ~ /c[ \t]+none/)
if (!isnone[seen] && fill == "") fill = spec[seen]
if (seen == nc) for (i = 1; i <= nc; i++) print pre[i] "\"" key[i] (isnone[i] ? fill : spec[i]) "\","
next }
{ print }' "$f" > "$f.tmp" && mv "$f.tmp" "$f"
done
# 2) Overlay (PNG): square versions of the outline/highlight images
write_png() { printf '%s' "$2" | base64 -d > "$DST/$1"; }
write_png top-left-active.png 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAdCAYAAACXFC2jAAAASklEQVR42mNkYGBoYMADWLi5udm+fPnShlPB/////zMwMPzHqQBK41TAxEAAsPz79+8/ZSYQcgMdrBj1JpIbGBkZ/5FtwqgCCAAAJ9onK5/spQ0AAAAASUVORK5CYII='
write_png top-left-inactive.png 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAdCAYAAACXFC2jAAAASklEQVR42mNkYGBoYMADWLi5udm+fPnShlPB/////zMwMPzHqQBK41TAxEAAsPz79+8/ZSYQcgMdrBj1JpIbGBkZ/5FtwqgCCAAAJ9onK5/spQ0AAAAASUVORK5CYII='
write_png top-right-active.png 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAdCAYAAACXFC2jAAAAR0lEQVR42u2PMQ7AIAwDz6hjXszEy+MOZSUMlZi41dY5ETAokO1YhRHRH8Crgm2XBYDGhtKQmf5nODRx35wGSVnk+xtu4eMFmCwkTAAQh0MAAAAASUVORK5CYII='
write_png top-right-inactive.png 'iVBORw0KGgoAAAANSUhEUgAAAAgAAAAdCAYAAACXFC2jAAAAR0lEQVR42u2PMQ7AIAwDz6hjXszEy+MOZSUMlZi41dY5ETAokO1YhRHRH8Crgm2XBYDGhtKQmf5nODRx35wGSVnk+xtu4eMFmCwkTAAQh0MAAAAASUVORK5CYII='
# 3) Optional: colour of the focused window's title text
if [ -n "$ACTIVE_TITLE_COLOR" ]; then
rc="$DST/themerc"
if grep -q '^active_text_color=' "$rc"; then
sed -i "s|^active_text_color=.*|active_text_color=$ACTIVE_TITLE_COLOR|" "$rc"
else
echo "active_text_color=$ACTIVE_TITLE_COLOR" >> "$rc"
fi
echo "Active title colour: $ACTIVE_TITLE_COLOR"
fi
echo "Created $DST_THEME"
xfconf-query -c xfwm4 -p /general/theme -s Default 2>/dev/null || true
xfconf-query -c xfwm4 -p /general/theme -s Default-Square 2>/dev/null \
&& echo "Applied theme 'Default-Square'." \
|| echo "Select 'Default-Square' in Settings > Window Manager > Style."