summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bashrc6
-rw-r--r--.config/X11/xcompose5
-rw-r--r--.config/emacs/early-init.el9
-rw-r--r--.config/emacs/init.el42
-rw-r--r--.config/emacs/modules/mm-abbrev.el8
-rw-r--r--.config/emacs/modules/mm-buffer-menu.el15
-rw-r--r--.config/emacs/modules/mm-editing.el85
-rw-r--r--.config/emacs/modules/mm-humanwave.el141
-rw-r--r--.config/emacs/modules/mm-modeline.el5
-rw-r--r--.config/emacs/modules/mm-projects.el56
-rw-r--r--.config/emacs/modules/mm-theme.el30
-rw-r--r--.config/emacs/modules/mm-treesit.el96
-rw-r--r--.config/emacs/site-lisp/gh.el59
-rw-r--r--.config/emacs/site-lisp/highlighter.el128
-rw-r--r--.config/emacs/site-lisp/live-jq.el101
-rw-r--r--.config/emacs/templates10
-rw-r--r--.config/eww/eww.yuck15
-rwxr-xr-x.config/eww/scripts/datetime-listener28
-rwxr-xr-x.config/eww/scripts/email-listener31
-rwxr-xr-x.config/eww/scripts/timer-listener16
-rw-r--r--.config/foot/foot.ini26
-rw-r--r--.config/hypr/hyprland.conf.in19
-rw-r--r--.local/bin/Makefile30
-rwxr-xr-x.local/bin/alarm22
-rw-r--r--.local/bin/po/sv/vlt.po178
-rw-r--r--.local/bin/po/vlt.pot172
-rwxr-xr-x.local/bin/vlt89
-rw-r--r--.local/share/applications/Vault.desktop1
-rw-r--r--.local/share/vault/vault.secbin12810 -> 13096 bytes
-rw-r--r--.xkb/symbols/mango29
30 files changed, 1283 insertions, 169 deletions
diff --git a/.bashrc b/.bashrc
index 6d99bfb..458fc05 100644
--- a/.bashrc
+++ b/.bashrc
@@ -68,9 +68,9 @@ export CARGO_HOME="$XDG_DATA_HOME/cargo"
export GNUPGHOME="$XDG_DATA_HOME/gnupg"
export GOPATH="$XDG_DATA_HOME/go"
export GOROOT="/usr/lib/go"
+export GRADLE_USER_HOME="$XDG_DATA_HOME/gradle"
export HISTFILE="$XDG_STATE_HOME/bash/history"
export INPUTRC="$XDG_CONFIG_HOME/readline/inputrc"
-export _JAVA_OPTIONS="-Djava.util.prefs.userRoot=\"$XDG_CONFIG_HOME/java\""
export MAILDIR="$HOME/mail"
export NODE_REPL_HISTORY="$XDG_DATA_HOME/node_repl_history"
export NPM_CONFIG_USERCONFIG="$XDG_CONFIG_HOME/npm/npmrc"
@@ -81,10 +81,12 @@ export PYTHONSTARTUP="$XDG_CONFIG_HOME/python/startup.py"
export PYTHONUSERBASE="$XDG_DATA_HOME/python"
export RLWRAP_HOME="$XDG_CACHE_HOME/rlwrap"
export RUSTUP_HOME="$XDG_DATA_HOME/rustup"
+export SQLITE_HISTORY="$XDG_STATE_HOME/sqlite_history"
export W3M_DIR="$XDG_STATE_HOME/w3m"
export WGETRC="$XDG_CONFIG_HOME/wgetrc"
export XCOMPOSECACHE="$XDG_CACHE_HOME/X11/xcompose"
export XCOMPOSEFILE="$XDG_CONFIG_HOME/X11/xcompose"
+export _JAVA_OPTIONS="-Djava.util.prefs.userRoot=\"$XDG_CONFIG_HOME/java\" -Djavafx.cachedir=\"$XDG_CACHE_HOME/openjfx\""
export REPODIR="$HOME/code"
export LD_LIBRARY_PATH="/usr/local/lib:$LD_LIBRARY_PATH"
@@ -99,6 +101,7 @@ export PRINTER='HP_OfficeJet_8010'
export QT_IM_MODULE=xim
export VAULT_2FA='2 Factor Authentication'
+alias irssi='irssi --config="$XDG_CONFIG_HOME/irssi/config" --home="$XDG_DATA_HOME/irssi"'
alias ..='cd ..'
alias d='git --git-dir="$REPODIR/Mango0x45/dotfiles.git" --work-tree="$HOME"'
alias g=git
@@ -114,6 +117,7 @@ alias z='2>/dev/null zathura --fork "$@" 2>/dev/null'
PS1='\[\e[96;1m\]\u \[\e[39m\]\W \[\e[96m\]〉\[\e[0m\]'
eval "$(fzf --bash)"
+eval "$(eww shell-completions --shell bash)"
# Autocompletions for auto-cpufreq. Inlined from the generated output
# for performance reasons.
diff --git a/.config/X11/xcompose b/.config/X11/xcompose
index 4346e5a..8141415 100644
--- a/.config/X11/xcompose
+++ b/.config/X11/xcompose
@@ -54,6 +54,8 @@
<Multi_key> <x> <x> : "×"
# Typographical Symbols
+<Multi_key> <greater> <period> : "›"
+<Multi_key> <less> <period> : "‹"
<Multi_key> <greater> <greater> : "»"
<Multi_key> <less> <less> : "«"
<Multi_key> <less> <minus> : "←"
@@ -491,4 +493,5 @@
<Multi_key> <underscore> <t> : "ₜ"
<Multi_key> <underscore> <u> : "ᵤ"
<Multi_key> <underscore> <v> : "ᵥ"
-<Multi_key> <underscore> <x> : "ₓ" \ No newline at end of file
+<Multi_key> <underscore> <x> : "ₓ"
+
diff --git a/.config/emacs/early-init.el b/.config/emacs/early-init.el
index b48d643..0dfbfa6 100644
--- a/.config/emacs/early-init.el
+++ b/.config/emacs/early-init.el
@@ -76,17 +76,14 @@
;; set threshold to 8 MiB which seems to be a good middleground for now.
;; A higher threshold means less garbage collections but I’ve had issues
;; with those garbage collections causing long freezes when they occur.
-(let ((saved-file-name-handler-alist file-name-handler-alist)
- (saved-vc-handled-backends vc-handled-backends))
- (setopt file-name-handler-alist nil
- vc-handled-backends nil)
+(let ((saved-file-name-handler-alist file-name-handler-alist))
+ (setopt file-name-handler-alist nil)
(add-hook
'emacs-startup-hook
(defun mm-restore-emacs-settings ()
(setopt gc-cons-threshold (* 1024 1024 8)
gc-cons-percentage 0.1
- file-name-handler-alist saved-file-name-handler-alist
- vc-handled-backends saved-vc-handled-backends))))
+ file-name-handler-alist saved-file-name-handler-alist))))
;;; Avoid Flashbang
diff --git a/.config/emacs/init.el b/.config/emacs/init.el
index 9bb921c..eb0f23e 100644
--- a/.config/emacs/init.el
+++ b/.config/emacs/init.el
@@ -8,7 +8,7 @@
;; Lord knows why this needs to be so complicated…
;;
;; The ‘eval’ is required in the case that this file is byte-compiled.
-(if mm-darwin-p
+(if mm-humanwave-p
(eval '(setq inhibit-startup-echo-area-message "thomasvoss"))
(eval '(setq inhibit-startup-echo-area-message "thomas")))
@@ -177,17 +177,17 @@ buffer suppressed."
(echo-keystrokes 0.01) ; 0 disables echoing
(echo-keystrokes-help nil)
(extended-command-suggest-shorter nil)
- (initial-buffer-choice t)
+ (initial-buffer-choice nil)
(initial-scratch-message mm-initial-scratch-message)
(kill-do-not-save-duplicates t)
(large-file-warning-threshold nil)
(make-backup-files nil)
- (mode-require-final-newline nil)
+ (mode-require-final-newline t)
(next-error-recenter '(4)) ; ‘center of window’
(read-extended-command-predicate #'command-completion-default-include-p)
(remote-file-name-inhibit-auto-save t)
(remote-file-name-inhibit-delete-by-moving-to-trash t)
- (require-final-newline nil)
+ (require-final-newline mm-humanwave-p)
(save-interprogram-paste-before-kill t)
(user-full-name "Thomas Voss")
(user-mail-address "mail@thomasvoss.com")
@@ -294,18 +294,10 @@ the buffer without saving it."
(advice-add command :around #'mm-auto-create-directories))
-;;; Emacs Lisp Libraries
-
-;; The following are included here because they’re required by some of
-;; the modules.
-
-(use-package async
- :ensure t)
-
-
;;; Load Modules
(require 'mm-abbrev) ; Text Expansion
+(require 'mm-buffer-menu) ; Buffer Menu
(require 'mm-calc) ; Emacs Calc
(require 'mm-completion) ; Completions
(require 'mm-dired) ; Dired
@@ -325,6 +317,28 @@ the buffer without saving it."
(require 'mm-darwin)) ; MacOS
(when mm-lsp-p
(require 'mm-lsp)) ; Language Server Protocol
+(when mm-humanwave-p
+ (require 'mm-humanwave)) ; Humanwave Extras
+
+;; TODO: Put this somewhere reasonable
+
+(defvar mm-placeholder-text "‹XX›")
+
+(defun mm-placeholder-insert ()
+ (interactive)
+ (insert mm-placeholder-text))
+
+(defun mm-placeholder-next ()
+ (interactive)
+ (let ((point (point)))
+ (if (search-forward mm-placeholder-text nil :noerror)
+ (delete-region (match-beginning 0) (match-end 0))
+ (goto-char point)
+ (message "No more placeholders after point."))))
+
+(keymap-global-set "C-c i p" #'mm-placeholder-insert)
+(keymap-global-set "C-c n" #'mm-placeholder-next)
+
;;; Postamble
@@ -332,4 +346,4 @@ the buffer without saving it."
(add-hook 'after-init-hook
(defun mm-echo-init-time ()
(message (emacs-init-time "Emacs initialized in %.2f seconds")))
- 100) \ No newline at end of file
+ 100)
diff --git a/.config/emacs/modules/mm-abbrev.el b/.config/emacs/modules/mm-abbrev.el
index 937d558..66ca8d7 100644
--- a/.config/emacs/modules/mm-abbrev.el
+++ b/.config/emacs/modules/mm-abbrev.el
@@ -78,6 +78,14 @@ case-sensitive to avoid unexpected abbreviation expansions."
"sme" "save-mark-and-excursion"
"sr" "save-restriction")
+(when mm-humanwave-p
+ (with-eval-after-load 'python-ts-mode
+ (mm-define-abbreviations python-ts-mode-abbrev-table
+ "empb" "with emphasize.Block():"
+ "empf" "@emphasize.func"
+ "empi" "from shared.system import emphasize"
+ "empt" "emphasize.this")))
+
;;; Template Configuration
diff --git a/.config/emacs/modules/mm-buffer-menu.el b/.config/emacs/modules/mm-buffer-menu.el
new file mode 100644
index 0000000..7b725c6
--- /dev/null
+++ b/.config/emacs/modules/mm-buffer-menu.el
@@ -0,0 +1,15 @@
+;;; mm-buffer-menu.el --- Buffer Menu configuration -*- lexical-binding: t; -*-
+
+(defun mm-Buffer-menu-delete-all ()
+ "Mark all buffers for deletion."
+ (interactive nil Buffer-menu-mode)
+ (save-excursion
+ (goto-char (point-min))
+ (while (not (eobp))
+ (Buffer-menu-delete))))
+
+(use-package buff-menu
+ :bind ( :map Buffer-menu-mode-map
+ ("D" . mm-Buffer-menu-delete-all)))
+
+(provide 'mm-buffer-menu) \ No newline at end of file
diff --git a/.config/emacs/modules/mm-editing.el b/.config/emacs/modules/mm-editing.el
index dda5352..6054f5a 100644
--- a/.config/emacs/modules/mm-editing.el
+++ b/.config/emacs/modules/mm-editing.el
@@ -56,6 +56,7 @@
(go-ts-mode . (:extras go-ts-mode-indent-offset))
(gsp-ts-mode . (:width 2 :extras gsp-ts-mode-indent-rules))
(helpful-mode . (:width 8)) ; GNU code uses 8-column tabs
+ (json-ts-mode . (:extras json-ts-mode-indent-offset))
(latex-mode . (:width 2))
(lisp-data-mode . (:spaces t))
(lisp-interaction-mode . (:spaces t))
@@ -66,8 +67,12 @@
(python-ts-mode . (:extras python-indent-offset))
(sgml-mode . (:extras sgml-basic-offset))
(sh-mode . (:extras sh-basic-offset))
+ (sql-mode . (:extras sqlind-basic-offset))
(tex-mode . (:width 2))
- (vimscript-ts-mode . (:extras vimscript-ts-mode-indent-level)))
+ (typescript-ts-mode . (:extras typescript-ts-mode-indent-offset))
+ (vimscript-ts-mode . (:extras vimscript-ts-mode-indent-level))
+ (vue-ts-mode . (:extras (typescript-ts-mode-indent-offset
+ vue-ts-mode-indent-offset))))
"Alist of indentation settings.
Each pair in this alist is of the form (MODE . SETTINGS) where MODE
specifies the mode for which the given SETTINGS should apply.
@@ -122,21 +127,30 @@ those should be listed in `mm-editing-indentation-settings'."
extras))
(set (make-local-variable extra) tabsize)))))
+(use-package sh-mode
+ :custom
+ (sh-indent-for-case-label 0)
+ (sh-indent-for-case-alt #'+))
+
;;; Code Commenting
-(defun mm-c-comment-no-leading-stars ()
+(defun mm-c-comment-no-continue ()
(setq-local comment-continue " "))
+(defun mm-mhtml-comment-no-continue ()
+ (setq-local comment-continue " "))
+
(use-package newcomment
:custom
(comment-style 'multi-line)
:config
(dolist (mode '(c-mode c++-mode))
- (add-hook (mm-mode-to-hook mode) #'mm-c-comment-no-leading-stars)
+ (add-hook (mm-mode-to-hook mode) #'mm-c-comment-no-continue)
(when-let ((ts-mode (mm-mode-to-ts-mode mode))
((fboundp ts-mode)))
- (add-hook (mm-mode-to-hook ts-mode) #'mm-c-comment-no-leading-stars))))
+ (add-hook (mm-mode-to-hook ts-mode) #'mm-c-comment-no-continue)))
+ (add-hook 'mhtml-mode #'mm-mhtml-comment-no-continue))
;;; Multiple Cursors
@@ -197,6 +211,7 @@ and *Messages* buffer. This forces that to not happen."
(use-package multiple-cursors
:ensure t
+ :demand t
:bind (("C->" . #'mc/mark-next-like-this)
("C-<" . #'mc/mark-previous-like-this)
("C-M-<" . #'mc/mark-all-like-this-dwim)
@@ -290,6 +305,18 @@ surround with spaces."
(push '("`" . "'") surround-pairs))))
+;;; Insert Webpage Contents
+
+(defun mm-insert-from-url (url)
+ "Insert the contents of URL at point."
+ (interactive
+ (let ((url-at-point (thing-at-point 'url)))
+ (list (read-string
+ (format-prompt "URL" url-at-point)
+ nil nil url-at-point))))
+ (call-process "curl" nil '(t nil) nil url))
+
+
;;; Emmet Mode
(defun mm-editing-emmet-dwim (arg)
@@ -310,6 +337,36 @@ is as described by `emmet-expand-line'."
(emmet-self-closing-tag-style ""))
+;;; JQ Manipulation in JSON Mode
+
+(defun mm-jq-filter (query &optional beg end)
+ "TODO"
+ (interactive
+ (list
+ (read-string (format-prompt "Query" nil))
+ (when (use-region-p) (region-beginning))
+ (when (use-region-p) (region-end))))
+ (let* ((beg (or beg (point-min)))
+ (end (or end (point-max)))
+ (temp-buffer (generate-new-buffer "* jq temp*"))
+ (exit-code (call-process-region beg end "jq" nil temp-buffer nil
+ "--tab" query))
+ (output (with-current-buffer temp-buffer (buffer-string))))
+ (if (zerop exit-code)
+ (atomic-change-group
+ (delete-region beg end)
+ (insert output)
+ (indent-region beg (point)))
+ (message "%s" output))
+ (kill-buffer temp-buffer)))
+
+(use-package json-ts-mode
+ :bind ( :map json-ts-mode-map
+ ("C-|" . #'mm-jq-filter))
+ :config
+ (require 'live-jq))
+
+
;;; Number Formatting
(use-package number-format-mode
@@ -321,7 +378,9 @@ is as described by `emmet-expand-line'."
;;; Additional Major Modes
(use-package awk-ts-mode :ensure t)
+(use-package cmake-mode :ensure t)
(use-package git-modes :ensure t)
+(use-package po-mode :ensure t)
(use-package sed-mode :ensure t)
(use-package csv-mode
@@ -330,6 +389,13 @@ is as described by `emmet-expand-line'."
(csv-align-style 'auto)
(csv-align-padding 2))
+(use-package xcompose-mode
+ :vc ( :url "https://git.thomasvoss.com/xcompose-mode"
+ :branch "master"
+ :rev :newest
+ :vc-backend Git)
+ :ensure t)
+
;;; Mode-Specific Configurations
@@ -345,6 +411,13 @@ is as described by `emmet-expand-line'."
;;; Add Missing Extensions
-(add-to-list 'auto-mode-alist '("\\.tmac\\'" . nroff-mode))
+(dolist (pattern '("\\.tmac\\'" "\\.mom\\'"))
+ (add-to-list 'auto-mode-alist (cons pattern #'nroff-mode)))
+
+
+;;; Subword Navigation
+
+(use-package subword
+ :hook prog-mode)
-(provide 'mm-editing) \ No newline at end of file
+(provide 'mm-editing)
diff --git a/.config/emacs/modules/mm-humanwave.el b/.config/emacs/modules/mm-humanwave.el
new file mode 100644
index 0000000..3aa97b3
--- /dev/null
+++ b/.config/emacs/modules/mm-humanwave.el
@@ -0,0 +1,141 @@
+;;; mm-humanwave.el --- Humanwave extras -*- lexical-binding: t; -*-
+
+
+;;; Query the Backend
+
+(defvar mm--humanwave-query-history nil
+ "History for endpoints given to `mm-humanwave-query'.")
+
+(defun mm-humanwave-query (endpoint &optional method)
+ "Query and display the result of an HTTP reqiest on ENDPOINT.
+If METHOD is nil, a GET request is performed."
+ (interactive
+ (let* ((query (read-string (format-prompt "Query" nil)
+ (car-safe mm--humanwave-query-history)
+ 'mm--humanwave-query-history))
+ (parts (string-split (string-trim query) " " :omit-nulls)))
+ (when (length> parts 2)
+ (user-error "Queries must be of the form `METHOD ENDPOINT' or `ENDPOINT'."))
+ (nreverse parts)))
+ (let* ((project-root (project-root (project-current :maybe-prompt)))
+ (qry-path (expand-file-name "qry" project-root))
+ extras)
+ (unless (file-executable-p qry-path)
+ (user-error "No `qry' executable found in the project root"))
+ (let ((output-buffer (get-buffer-create "*Query Response*")))
+ (with-current-buffer output-buffer
+ (delete-region (point-min) (point-max))
+ (call-process qry-path nil t nil
+ (string-trim endpoint) "-X" (or method "GET"))
+ (unless (eq major-mode 'json-ts-mode)
+ (json-ts-mode))
+ (goto-char (point-min)))
+ (display-buffer output-buffer))))
+
+(keymap-set project-prefix-map "q" #'mm-humanwave-query)
+
+
+;;; IMenu Support for Handlers
+
+(defvar mm-humanwave-handler--regexp
+ (rx bol
+ (* blank)
+ (or "if" "elif")
+ (* blank)
+ (or "topic" "schedule")
+ (* blank)
+ "=="
+ (* blank)
+ (or (seq ?\' (* (not ?\')) ?\')
+ (seq ?\" (* (not ?\")) ?\"))
+ (* blank)
+ ?:
+ (* blank)
+ eol))
+
+(defun mm-humanwave-handler-topic-imenu-index ()
+ (let ((tree-index (when (fboundp 'treesit-simple-imenu--generic-function)
+ (treesit-simple-imenu--generic-function
+ treesit-simple-imenu-settings)))
+ (topic-index '()))
+ (save-excursion
+ (goto-char (point-min))
+ (while (re-search-forward mm-humanwave-handler--regexp nil :noerror)
+ (let ((label (match-string 0))
+ (pos (match-beginning 0)))
+ (push (cons (format "Topic: %s" (string-trim label)) pos) topic-index))))
+ (append (nreverse topic-index) tree-index)))
+
+(defun mm-humanwave-handler-topic-imenu-setup ()
+ "Setup custom imenu index for `python-ts-mode'."
+ (when (and (string-match-p "handlers?" (or (buffer-file-name) ""))
+ (derived-mode-p #'python-ts-mode))
+ (setq-local imenu-create-index-function
+ #'mm-humanwave-handler-topic-imenu-index)))
+
+(add-hook 'after-change-major-mode-hook
+ #'mm-humanwave-handler-topic-imenu-setup)
+
+
+;;; Insert Imports in Vue
+
+(defun mm-humanwave-insert-vue-import-path (base-directory target-file)
+ "Insert an import directive at POINT.
+The import directive imports TARGET-FILE relative from BASE-DIRECTORY.
+When called interactively BASE-DIRECTORY is the directory of the current
+open Vue file and TARGET-FILE is a file in the current project that is
+queried interactively.
+
+When called interactively the prefix argument can be used to emulate the
+behaviour of the INCLUDE-ALL-P argument to `mm-project-read-file-name'."
+ (interactive
+ (list
+ default-directory
+ (mm-humanwave-project-read-file-name current-prefix-arg)))
+ (let ((path (file-name-sans-extension
+ (file-relative-name target-file base-directory))))
+ (unless (string-match-p "/" path)
+ (setq path (concat "./" path)))
+ (insert "import ")
+ (save-excursion
+ (insert (thread-last
+ (file-name-base path)
+ (mm-string-split "-")
+ (mapconcat #'capitalize)))
+ (push-mark (point))
+ (insert (format " from '%s';" path)))))
+
+(defun mm-humanwave-project-read-file-name (&optional include-all-p)
+ "Prompt for a project file.
+This function is similar to `project-find-file', but it returns the path
+to the selected file instead of opening it.
+
+When called interactively the selected file is printed to the minibuffer,
+otherwise it is returned.
+
+The prefix argument INCLUDE-ALL-P is the same as the INCLUDE-ALL argument
+to the `project-find-file' command."
+ (interactive "P")
+ (let* ((project (project-current :maybe-prompt))
+ (root (project-root project))
+ (files (if include-all-p
+ (let ((vc-ignores (mapcar
+ (lambda (dir) (concat dir "/"))
+ vc-directory-exclusion-list)))
+ (project--files-in-directory root vc-ignores))
+ (project-files project)))
+ (canditates (mapcar (lambda (f) (file-relative-name f root))
+ files))
+ (table (lambda (string predicate action)
+ (if (eq action 'metadata)
+ '(metadata (category . file))
+ (complete-with-action action canditates string predicate))))
+ (default-directory root)
+ (choice (completing-read (format-prompt "Find project file" nil)
+ table nil :require-match)))
+ (let ((path (expand-file-name choice root)))
+ (if (called-interactively-p 'any)
+ (message "%s" path)
+ path))))
+
+(provide 'mm-humanwave)
diff --git a/.config/emacs/modules/mm-modeline.el b/.config/emacs/modules/mm-modeline.el
index dd42f6f..44fc115 100644
--- a/.config/emacs/modules/mm-modeline.el
+++ b/.config/emacs/modules/mm-modeline.el
@@ -52,7 +52,8 @@
("Imake" . "IMake")
("Js" . "JavaScript")
("Ts Mode" . "Tree-Sitter Mode")
- ("Wdired" . "WDired"))
+ ("Wdired" . "WDired")
+ ("Xcompose" . "XCompose"))
"Alist of substrings in major mode names that should be remapped.
Some major modes have substrings that would be better displayed in
another manner. For example expanding an abbreviation such as ‘Js’ to
@@ -89,7 +90,7 @@ the text it should be mapped to.")
((derived-mode-p 'comint-mode) "$ ")
((derived-mode-p 'conf-mode) "# ")
((derived-mode-p 'prog-mode) "λ ")
- ((derived-mode-p 'special-mode) "❇ ")
+ ((derived-mode-p 'special-mode) "★ ")
((derived-mode-p 'text-mode) "§ ")
(:default ""))
'face 'mm-modeline-major-mode-symbol-face))
diff --git a/.config/emacs/modules/mm-projects.el b/.config/emacs/modules/mm-projects.el
index be80789..02d7af4 100644
--- a/.config/emacs/modules/mm-projects.el
+++ b/.config/emacs/modules/mm-projects.el
@@ -19,24 +19,19 @@ This is intended to be called interactively via
(project-find-file "Find File" ?f)
(mm-projects-project-magit-status "Git Status" ?s)))
:config
- (unless mm-darwin-p
+ (unless mm-humanwave-p
(if-let ((repo-directory (getenv "REPODIR")))
- (with-eval-after-load 'async
- (async-start
- (lambda ()
- (require 'project)
- (let* ((list-dir
- (lambda (path)
- (directory-files path :full "\\`[^.]")))
- (directories
- (cl-loop for author in (funcall list-dir (getenv "REPODIR"))
- append (cl-loop for path in (funcall list-dir author)
- collect (list (concat path "/"))))))
- (with-temp-buffer
- (prin1 directories (current-buffer))
- (write-file project-list-file))))
- (lambda (_proc)
- (project--read-project-list))))
+ (let* ((list-dir
+ (lambda (path)
+ (directory-files path :full "\\`[^.]")))
+ (directories
+ (cl-loop for author in (funcall list-dir (getenv "REPODIR"))
+ append (cl-loop for path in (funcall list-dir author)
+ collect (list (concat path "/"))))))
+ (with-temp-buffer
+ (prin1 directories (current-buffer))
+ (write-file project-list-file))
+ (project--read-project-list))
(warn "The REPODIR environment variable is not set."))))
@@ -52,9 +47,10 @@ This is intended to be called interactively via
(use-package magit
:ensure t
- :bind ( :map magit-status-mode-map
- ("[" . magit-section-backward-sibling)
- ("]" . magit-section-forward-sibling))
+ :bind (("C-c b" . magit-blame-addition)
+ :map magit-status-mode-map
+ ("[" . magit-section-backward-sibling)
+ ("]" . magit-section-forward-sibling))
:custom
(git-commit-style-convention-checks
'(non-empty-second-line overlong-summary-line))
@@ -75,11 +71,18 @@ This is intended to be called interactively via
"push" "-v" args remote
(format "refs/heads/%s:refs/heads/%s" branch branch)))))
(transient-append-suffix #'magit-push '(1 -1)
- '("a" "all remotes" mm-projects-magit-push-current-to-all-remotes)))
+ '("a" "all remotes" mm-projects-magit-push-current-to-all-remotes))
+ (add-to-list 'magit-blame-styles
+ '(margin
+ (show-lines . t)
+ (margin-format . (" %C %a" " %s%f" " "))
+ (margin-width . 73)
+ (margin-face . magit-blame-margin)
+ (margin-body-face . (magit-blame-dimmed)))))
(use-package magit-repos
:ensure nil ; Part of ‘magit’
- :if (not mm-darwin-p)
+ :if (not mm-humanwave-p)
:commands (magit-list-repositories)
:init
(if-let ((directory (getenv "REPODIR")))
@@ -101,4 +104,11 @@ This is intended to be called interactively via
(require 'ansi-color)
(add-hook 'compilation-filter-hook #'ansi-color-compilation-filter))
-(provide 'mm-projects) \ No newline at end of file
+
+;;; GitHub Pull Requests
+
+(require 'gh)
+(keymap-global-set "C-c p c" #'gh-create-pr)
+(keymap-global-set "C-c p o" #'gh-open-previous-pr)
+
+(provide 'mm-projects)
diff --git a/.config/emacs/modules/mm-theme.el b/.config/emacs/modules/mm-theme.el
index d9b129c..cde214a 100644
--- a/.config/emacs/modules/mm-theme.el
+++ b/.config/emacs/modules/mm-theme.el
@@ -8,7 +8,7 @@
;;; Fonts
-(defvar mm-theme-monospace-font `(,(if mm-darwin-p
+(defvar mm-theme-monospace-font `(,(if mm-humanwave-p
"Iosevka Custom"
"Iosevka Smooth")
:weight regular
@@ -16,7 +16,9 @@
"The default monospace font.
This is a plist containing a font name, -weight, and -height.")
-(defvar mm-theme-proportional-font '("OpenSans" :weight regular :height 162)
+(defvar mm-theme-proportional-font
+ `(,(if mm-darwin-p "Microsoft Sans Serif" "OpenSans")
+ :weight regular :height 162)
"The default proportional font.
This is a plist containing a font name, -weight, and -height.")
@@ -164,6 +166,16 @@ See also the `mm-theme-background-opacity' variable."
(("C-c h l" . pulsar-highlight-dwim)))
+;;; In-buffer highlighting
+
+(require 'hi-lock) ; For extra face definitions
+(use-package highlighter
+ :bind (("C-c h m" . #'highlighter-mark)
+ ("C-c h u" . #'highlighter-unmark)
+ ("C-c h U" . #'highlighter-unmark-buffer))
+ :commands (highlighter-mark highlighter-unmark highlighter-unmark-buffer))
+
+
;;; Add Padding
(use-package spacious-padding
@@ -219,4 +231,16 @@ See also the `mm-theme-background-opacity' variable."
:custom
(hl-line-sticky-flag nil))
-(provide 'mm-theme) \ No newline at end of file
+
+;;; Indent Guides
+
+(when mm-humanwave-p
+ (use-package highlight-indent-guides
+ :ensure t
+ :hook ((jinja2-mode vue-ts-mode mhtml-mode) . highlight-indent-guides-mode)
+ :custom
+ (highlight-indent-guides-method 'fill)
+ (highlight-indent-guides-auto-even-face-perc 30)
+ (highlight-indent-guides-auto-odd-face-perc 0)))
+
+(provide 'mm-theme)
diff --git a/.config/emacs/modules/mm-treesit.el b/.config/emacs/modules/mm-treesit.el
index a41f52c..bd146ce 100644
--- a/.config/emacs/modules/mm-treesit.el
+++ b/.config/emacs/modules/mm-treesit.el
@@ -15,21 +15,46 @@
(setopt treesit-font-lock-level 4)
(setopt treesit-language-source-alist
- '((awk "https://github.com/Beaglefoot/tree-sitter-awk")
- (c "https://github.com/tree-sitter/tree-sitter-c")
- (cpp "https://github.com/tree-sitter/tree-sitter-cpp")
- (css "https://github.com/tree-sitter/tree-sitter-css")
- (go "https://github.com/tree-sitter/tree-sitter-go")
- (gomod "https://github.com/camdencheek/tree-sitter-go-mod")
- (gsp "git://git.thomasvoss.com/tree-sitter-gsp.git")
- (html "https://github.com/tree-sitter/tree-sitter-html")
- (java "https://github.com/tree-sitter/tree-sitter-java")
- (javascript "https://github.com/tree-sitter/tree-sitter-javascript")
- (python "https://github.com/tree-sitter/tree-sitter-python")
- (typescript "https://github.com/tree-sitter/tree-sitter-typescript"
- "master" "typescript/src")
- (vim "https://github.com/tree-sitter-grammars/tree-sitter-vim")
- (vue "https://github.com/ikatyang/tree-sitter-vue")))
+ '((awk
+ "https://github.com/Beaglefoot/tree-sitter-awk")
+ (c
+ "https://github.com/tree-sitter/tree-sitter-c")
+ (cpp
+ "https://github.com/tree-sitter/tree-sitter-cpp")
+ (css
+ "https://github.com/tree-sitter/tree-sitter-css")
+ (go
+ "https://github.com/tree-sitter/tree-sitter-go")
+ (gomod
+ "https://github.com/camdencheek/tree-sitter-go-mod")
+ (gsp
+ "git://git.thomasvoss.com/tree-sitter-gsp.git")
+ (html
+ "https://github.com/tree-sitter/tree-sitter-html")
+ (java
+ "https://github.com/tree-sitter/tree-sitter-java")
+ (javascript
+ "https://github.com/tree-sitter/tree-sitter-javascript")
+ (json
+ "https://github.com/tree-sitter/tree-sitter-json")
+ (markdown
+ "https://github.com/tree-sitter-grammars/tree-sitter-markdown"
+ "split_parser" "tree-sitter-markdown/src")
+ (markdown-inline
+ "https://github.com/tree-sitter-grammars/tree-sitter-markdown"
+ "split_parser" "tree-sitter-markdown-inline/src")
+ (python
+ "https://github.com/tree-sitter/tree-sitter-python")
+ (tsx
+ "https://github.com/tree-sitter/tree-sitter-typescript"
+ "master" "tsx/src")
+ (typescript
+ "https://github.com/tree-sitter/tree-sitter-typescript"
+ "master" "typescript/src")
+ (vim
+ "https://github.com/tree-sitter-grammars/tree-sitter-vim")
+ (vue
+ "https://github.com/ikatyang/tree-sitter-vue")))
;;; Install Missing Parsers
@@ -72,13 +97,18 @@ The parsers are taken from `treesit-language-source-alist'."
;; NOTE: This package doesn’t autoload its ‘auto-mode-alist’ entries
(use-package vue-ts-mode
- :vc (:url "https://github.com/8uff3r/vue-ts-mode.git"
- :branch "main"
- :rev :newest
- :vc-backend Git)
+ :vc ( :url "https://github.com/8uff3r/vue-ts-mode.git"
+ :branch "main"
+ :rev :newest
+ :vc-backend Git)
:ensure t
:mode "\\.vue\\'")
+;; NOTE: This package doesn’t autoload its ‘auto-mode-alist’ entries
+(use-package markdown-ts-mode
+ :ensure t
+ :mode "\\.md\\'")
+
;;; Prefer Tree-Sitter Modes
@@ -87,28 +117,39 @@ The parsers are taken from `treesit-language-source-alist'."
;; anyway. Same goes for ‘typescript-ts-mode’.
(defvar mm-treesit-language-file-name-alist
'((go . "\\.go\\'")
- (go-mod . "/go\\.mod\\'")
+ (gomod . "/go\\.mod\\'")
+ (json . "\\.json\\'")
+ (tsx . "\\.tsx\\'")
(typescript . "\\.ts\\'"))
"Alist mapping languages to their associated file-names.
This alist is a set of pairs of the form (LANG . REGEXP) where LANG is
-the symbol corresponding to a major mode with the ‘-ts-mode’ suffix
+the symbol corresponding to a major mode with the `-ts-mode' suffix
removed. REGEXP is a regular expression matching filenames for which
the associated language’s major-mode should be enabled.
This alist is used to configure `auto-mode-alist'.")
+(defvar mm-treesit-dont-have-modes
+ '(markdown-inline)
+ "List of languages that don't have modes.
+Some languages may come with multiple parsers, (e.g. `markdown' and
+`markdown-inline') and as a result one-or-more of the parsers won't be
+associated with a mode. To avoid breaking the configuration, these
+languages should be listed here.")
+
(dolist (spec treesit-language-source-alist)
(let* ((lang (car spec))
- (lang (alist-get lang mm-treesit-language-remap-alist lang))
- (symbol-name (symbol-name lang))
- (name-mode (intern (concat symbol-name "-mode")))
- (name-ts-mode (intern (concat symbol-name "-ts-mode"))))
+ (lang-remap (alist-get lang mm-treesit-language-remap-alist lang))
+ (name-mode (intern (format "%s-mode" lang-remap)))
+ (name-ts-mode (intern (format "%s-ts-mode" lang-remap))))
;; If ‘name-ts-mode’ is already in ‘auto-mode-alist’ then we don’t
;; need to do anything, however if that’s not the case then if
;; ‘name-ts-mode’ and ‘name-mode’ are both bound we do a simple
;; remap. If the above is not true then we lookup the extensions in
;; ‘mm-treesit-language-file-name-alist’.
(cond
+ ((memq lang mm-treesit-dont-have-modes)
+ nil)
((not (fboundp name-ts-mode))
(warn "`%s' is missing." name-ts-mode))
((rassq name-ts-mode auto-mode-alist)
@@ -121,6 +162,9 @@ This alist is used to configure `auto-mode-alist'.")
(add-to-list 'auto-mode-alist (cons file-regexp name-ts-mode))
(warn "Unable to determine the extension for `%s'." name-ts-mode))))))
+;; JavaScript being difficult as usual
+(add-to-list 'major-mode-remap-alist '(javascript-mode . js-ts-mode))
+
;;; Hack For C23
@@ -178,4 +222,4 @@ back to regular `expreg-expand'."
:commands (mm-expreg-expand mm-expreg-expand-dwim)
:bind ("M-SPC" . mm-expreg-expand-dwim))
-(provide 'mm-treesit) \ No newline at end of file
+(provide 'mm-treesit)
diff --git a/.config/emacs/site-lisp/gh.el b/.config/emacs/site-lisp/gh.el
new file mode 100644
index 0000000..23086e5
--- /dev/null
+++ b/.config/emacs/site-lisp/gh.el
@@ -0,0 +1,59 @@
+;;; gh.el --- GitHub integration for Emacs -*- lexical-binding: t; -*-
+
+(defun gh-get-labels ()
+ "Return a list of labels in the current GitHub repository."
+ (with-temp-buffer
+ (call-process "gh" nil t nil "label" "list" "--sort" "name" "--json" "name")
+ (goto-char (point-min))
+ (seq-map (lambda (x) (gethash "name" x))
+ (json-parse-buffer))))
+
+;; TODO: Set title and body in a buffer like Magit
+(defun gh-create-pr (title &optional labels draftp)
+ "Create a GitHub pull request.
+If DRAFTP is non-nil, the PR will be created as a draft.
+
+LABELS is a list of labels. A list of available labels can be fetched
+via `gh-get-labels'."
+ (interactive
+ (list
+ (read-string (format-prompt "PR Title" nil))
+ (completing-read-multiple (format-prompt "PR Labels" nil)
+ (gh-get-labels))
+ (y-or-n-p "Create PR as a draft? ")))
+ (let* ((project (project-name (project-current)))
+ (flags `("--fill-verbose" "--assignee" "@me"))
+ (label-string (mapconcat #'identity labels ",")))
+ ;; TODO: Remove this
+ (when (string= project "blixem")
+ (setq title (format "%s %s" (car (vc-git-branches)) title)))
+ (setq flags (append flags `("--title" ,title)))
+ (when draftp
+ (setq flags (append flags '("--draft"))))
+ (when labels
+ (setq flags (append flags `("--label" ,label-string))))
+ (with-temp-buffer
+ (apply #'call-process "gh" nil t nil "pr" "create" flags)
+ (message (buffer-string)))))
+
+(defvar gh-pr-regexp
+ "\\`https://\\(?:www\\.\\)?github\\.com/[^/]+/[^/]+/pull/[[:digit:]]+\\'")
+
+(defun gh--pr-link-p (s)
+ (declare (pure t) (side-effect-free t))
+ (string-match-p gh-pr-regexp s))
+
+(defun gh-open-previous-pr ()
+ "Open the previous GitHub pull request.
+Opens the previous pull request created by `gh-create-pr' by searching
+for the echoed URL in the `*Messages*' buffer."
+ (interactive)
+ (with-current-buffer "*Messages*"
+ (goto-char (point-max))
+ (while (not (gh--pr-link-p (buffer-substring-no-properties
+ (pos-bol) (pos-eol))))
+ (unless (line-move -1 :noerror)
+ (user-error "No previous pull request found.")))
+ (browse-url-at-point)))
+
+(provide 'gh)
diff --git a/.config/emacs/site-lisp/highlighter.el b/.config/emacs/site-lisp/highlighter.el
new file mode 100644
index 0000000..ce67ac8
--- /dev/null
+++ b/.config/emacs/site-lisp/highlighter.el
@@ -0,0 +1,128 @@
+;;; highlighter.el --- In-buffer highlighting commands -*- lexical-binding: t; -*-
+
+(require 'seq)
+
+(defgroup highlighter nil
+ "Customization group for `highlighter'."
+ :group 'convenience)
+
+(defcustom highlighter-default-face 'match
+ "The default face used by `highlighter-mark'."
+ :type 'face
+ :package-version '(highlighter . "1.0.0")
+ :group 'highlighter)
+
+(defun highlighter-mark (arg)
+ "Highlight text in the buffer.
+Highlight the current line or region if it is active. Text is
+highlighted using the face specified by `highlighter-default-face'.
+
+With ARG, interactively pick a face to highlight with."
+ (declare (interactive-only t))
+ (interactive "P")
+ (let ((bounds (if (use-region-p)
+ (region-bounds)
+ `((,(pos-bol) . ,(pos-eol)))))
+ (face (when arg
+ (highlighter--read-face-name "Highlight with face" #'facep))))
+ (highlighter-mark-region bounds face))
+ (when (region-active-p)
+ (deactivate-mark)))
+
+(defun highlighter-unmark (arg)
+ "Remove highlights in the buffer.
+
+Remove highlights from the current line or region if it is active.
+
+With ARG, interactively pick a face. Only highlights using the chosen
+face will be removed."
+ (declare (interactive-only t))
+ (interactive "P")
+ (let ((bounds (if (use-region-p)
+ (region-bounds)
+ `((,(pos-bol) . ,(pos-eol)))))
+ (face (when arg
+ (highlighter--read-face-name
+ "Clear highlights using face"
+ #'highlighter--used-face-p))))
+ (highlighter-unmark-region bounds face))
+ (when (region-active-p)
+ (deactivate-mark)))
+
+(defun highlighter-mark-region (bounds &optional face)
+ "Highlight text in the buffer within BOUNDS.
+BOUNDS uses the same format as returned by `region-bounds'.
+
+Text is highlighted using the face specified by `highlighter-default-face'.
+
+If FACE is nil or omitted, `highlighter-default-face' is used."
+ (dolist (x bounds) (highlighter--mark-region (car x) (cdr x) face)))
+
+(defun highlighter-unmark-region (bounds &optional face)
+ "Remove highlights in the buffer within BOUNDS.
+BOUNDS uses the same format as returned by `region-bounds'.
+
+If FACE is non-nil, only remove highlights using FACE."
+ (dolist (x bounds) (highlighter--unmark-region (car x) (cdr x) face)))
+
+(defun highlighter--mark-region (beg end &optional face)
+ (let ((ov (make-overlay beg end nil :front-advance))
+ (face (or face highlighter-default-face 'match)))
+ (overlay-put ov 'priority 1)
+ (overlay-put ov 'face face)
+ (overlay-put ov 'evaporate t)
+ (overlay-put ov 'highlighter--mark-p t)
+ (overlay-put ov 'highlighter--face face)))
+
+(defun highlighter--unmark-region (beg end &optional face)
+ (if face
+ (remove-overlays beg end 'highlighter--face face)
+ (remove-overlays beg end 'highlighter--mark-p t)))
+
+(defun highlighter-unmark-buffer (arg)
+ "Remove highlights in the buffer.
+
+With ARG, interactively pick a face. Only highlights using the chosen
+face will be removed."
+ (declare (interactive-only t))
+ (interactive "P")
+ (let ((face (when arg
+ (highlighter--read-face-name
+ "Clear highlights using face"
+ #'highlighter--used-face-p))))
+ (highlighter--unmark-region (point-min) (point-max) face)))
+
+(defun highlighter--read-face-name (prompt face-predicate)
+ (let (default defaults)
+ (let ((prompt (format "%s: " prompt))
+ (completion-extra-properties
+ `(:affixation-function
+ ,(lambda (faces)
+ (mapcar
+ (lambda (face)
+ (list face
+ (concat (propertize read-face-name-sample-text
+ 'face face)
+ "\t")
+ ""))
+ faces))))
+ aliasfaces nonaliasfaces faces)
+ ;; Build up the completion tables.
+ (mapatoms (lambda (s)
+ (when (apply face-predicate s nil)
+ (if (get s 'face-alias)
+ (push (symbol-name s) aliasfaces)
+ (push (symbol-name s) nonaliasfaces)))))
+ (let ((face (completing-read
+ prompt
+ (completion-table-in-turn nonaliasfaces aliasfaces)
+ nil t nil 'face-name-history defaults)))
+ (when (facep face) (if (stringp face)
+ (intern face)
+ face))))))
+
+(defun highlighter--used-face-p (face)
+ (seq-filter (lambda (ov) (eq face (overlay-get ov 'highlighter--face)))
+ (overlays-in (point-min) (point-max))))
+
+(provide 'highlighter)
diff --git a/.config/emacs/site-lisp/live-jq.el b/.config/emacs/site-lisp/live-jq.el
new file mode 100644
index 0000000..f8a0a7f
--- /dev/null
+++ b/.config/emacs/site-lisp/live-jq.el
@@ -0,0 +1,101 @@
+;; TODO: ‘defcustom’ this
+(defvar live-jq-major-mode
+ (cond ((fboundp #'json-ts-mode) #'json-ts-mode)
+ ((fboundp #'json-mode) #'json-mode))
+ "TODO")
+
+(defvar live-jq--input-buffer nil
+ "The buffer containing the original JSON data.")
+
+(defvar live-jq--preview-buffer "*JQ Preview*"
+ "The buffer showing the live jq results.")
+
+(defvar live-jq--last-query "")
+
+(defun live-jq--get-json-input ()
+ "Return the contents of the input buffer as a string."
+ (with-current-buffer live-jq--input-buffer
+ (buffer-substring-no-properties (point-min) (point-max))))
+
+(defun live-jq--run-jq (query)
+ "Run jq QUERY on the input buffer's content and return result string or nil on error."
+ (let ((json-input (live-jq--get-json-input)))
+ (with-temp-buffer
+ (insert json-input)
+ (let ((exit-code (call-process-region
+ (point-min) (point-max)
+ "jq" :delete t nil "--tab" query)))
+ (when (zerop exit-code)
+ (buffer-string))))))
+
+(defun live-jq--render-jq-preview (query)
+ "Update preview buffer with the result or error of jq QUERY."
+ (let* ((preview-buffer (get-buffer-create live-jq--preview-buffer))
+ (json-input (live-jq--get-json-input))
+ (inhibit-read-only t))
+ (with-current-buffer preview-buffer
+ (erase-buffer)
+ (condition-case err
+ (with-temp-buffer
+ (insert json-input)
+ (let ((exit-code (call-process-region
+ (point-min) (point-max)
+ "jq" nil preview-buffer nil "--tab" query)))
+ (when (not (zerop exit-code))
+ (erase-buffer)
+ (insert "%s\n%s"
+ (propertize (format "jq error (exit %d): %s" exit-code query)
+ 'face 'error)
+ json-input))))
+ (error
+ (insert "%s\n%s"
+ (propertize (format "Error: %s" err) 'face 'error)
+ input-json)))
+ (goto-char (point-min))
+ (when live-jq-major-mode
+ (funcall live-jq-major-mode))))
+ (display-buffer live-jq--preview-buffer))
+
+(defun live-jq--minibuffer-update ()
+ "Update preview as user types."
+ (let ((query (minibuffer-contents)))
+ (unless (equal query live-jq--last-query)
+ (setq live-jq--last-query query)
+ (live-jq--render-jq-preview query))))
+
+;;;###autoload
+(defun live-jq ()
+ "Prompt for a jq query, show live results, and replace buffer on confirmation."
+ (interactive)
+ (unless (executable-find "jq")
+ (user-error "`jq' not found in PATH."))
+
+ (setq live-jq--input-buffer (current-buffer))
+ (setq live-jq--last-query "")
+
+ ;; Clean up preview buffer if user cancels with C-g
+ (let ((minibuffer-setup-hook
+ (list (lambda ()
+ ;; Add post-command-hook for live preview
+ (add-hook 'post-command-hook #'live-jq--minibuffer-update nil t)
+ ;; Add abort cleanup
+ (add-hook 'minibuffer-exit-hook
+ (lambda ()
+ (when (get-buffer live-jq--preview-buffer)
+ (kill-buffer live-jq--preview-buffer)))
+ nil t)))))
+ (let ((query (read-from-minibuffer (format-prompt "Query" nil))))
+ (unwind-protect
+ (let ((result (live-jq--run-jq query)))
+ (if result
+ (with-current-buffer live-jq--input-buffer
+ (let ((inhibit-read-only t))
+ (erase-buffer)
+ (insert result))
+ (message "jq applied."))
+ (user-error "Invalid jq query: see *jq-preview* for details")))
+ ;; Cleanup preview buffer after any outcome
+ (when (get-buffer live-jq--preview-buffer)
+ (kill-buffer live-jq--preview-buffer))))))
+
+(provide 'live-jq)
diff --git a/.config/emacs/templates b/.config/emacs/templates
index 6749183..ad4e97a 100644
--- a/.config/emacs/templates
+++ b/.config/emacs/templates
@@ -27,4 +27,12 @@ emacs-lisp-mode
(buffer-name)))
" --- " p " -*- lexical-binding: t; -*-" n n q)
-(section "\f" n ";;; " p n n q) \ No newline at end of file
+(section "\f" n ";;; " p n n q)
+
+vue-ts-mode
+
+(fcrow
+ > "<div class=\"fc-row\">"
+ n> "<label>{{ _('" p "') }}</label>"
+ n> q
+ n> "</div>") \ No newline at end of file
diff --git a/.config/eww/eww.yuck b/.config/eww/eww.yuck
index e356959..72f0b06 100644
--- a/.config/eww/eww.yuck
+++ b/.config/eww/eww.yuck
@@ -14,7 +14,10 @@
`stdbuf -oL scripts/workspace-list-listener`)
(deflisten datetime
- `stdbuf -oL scripts/datetime-listener`)
+ `LANG=sv_SE.UTF-8 stdbuf -oL scripts/datetime-listener`)
+
+(deflisten timer-1
+ `stdbuf -oL scripts/timer-listener`)
(deflisten brightness
`stdbuf -oL scripts/backlight-listener`)
@@ -26,7 +29,7 @@
`stdbuf -oL scripts/keymap-listener`)
(deflisten emails :initial '{"icon":"","text":""}'
- `stdbuf -oL scripts/email-listener`)
+ `LANG=sv_SE.UTF-8 stdbuf -oL scripts/email-listener`)
;; Pollers
(defpoll user :interval "69h"
@@ -64,7 +67,9 @@
(workspaces :monitor monitor))
(defwidget middle []
- (clock))
+ (box :space-evenly false
+ (clock)
+ (timer)))
(defwidget end []
(box :halign "end"
@@ -89,6 +94,10 @@
(defwidget clock []
datetime)
+(defwidget timer []
+ (box :class "timer"
+ (label :text timer-1)))
+
(defwidget wireless []
(eventbox
:onhover "eww update wireless_rev=true"
diff --git a/.config/eww/scripts/datetime-listener b/.config/eww/scripts/datetime-listener
index 1cd8e39..47d7031 100755
--- a/.config/eww/scripts/datetime-listener
+++ b/.config/eww/scripts/datetime-listener
@@ -1,12 +1,22 @@
#!/bin/sh
-while :
+veckaord()
+{
+ case "${LANG:-LC_ALL}" in
+ sv_*.*)
+ echo vecka ;;
+ pt_*.*)
+ echo semana ;;
+ *)
+ echo Week ;;
+ esac
+}
+
+stdbuf -oL -- tiktok \
+ "%A, %-d %B %Y, $(veckaord) %V — %T %Z" \
+ TZ=Europe/Helsinki ' (%H:%M %Z,' \
+ TZ=Pacific/Auckland ' %H:%M %Z)' \
+| while read line
do
- # {
- # date +'%A, %-d of %B %Y — %T %Z'
- # TZ='Asia/Kolkata' date +'(%T %Z)'
- # } | paste -sd ' '
- date +'%A, %-d of %B %Y — %H:%M %Z'
- sleep 10
- # sleep "$(date +'%s.%N' | dc -e '? d 1 + 1 / r - p')"
-done | ordinal -p1
+ echo "$line" | ordinal -p '^\w+, (\d+)'
+done | sed 's/^./\U&/g'
diff --git a/.config/eww/scripts/email-listener b/.config/eww/scripts/email-listener
index 6889345..b5c1cf3 100755
--- a/.config/eww/scripts/email-listener
+++ b/.config/eww/scripts/email-listener
@@ -8,6 +8,17 @@ export NOTIFY_LONG=email
readonly CACHE="${XDG_CACHE_HOME:=$HOME/.cache}/email-listener"
touch "$CACHE"
+T()
+{
+ en="$1"
+ sv="$2"
+ shift 2
+ case "${LC_ALL:-$LANG}" in
+ en_*) printf "$en" "$@" ;;
+ sv_*) printf "$sv" "$@"
+ esac
+}
+
count()
{
local cur=`find "$MAILDIR"/*/Inbox/cur -type f | wc -l`
@@ -16,16 +27,16 @@ count()
case $sum in
0)
- printf 'icon=\ntext=No Email'
+ T 'icon=\ntext=No Emails' 'icon=\ntext=Inga meddelanden'
;;
1)
- printf 'icon=\ntext=1 Email'
+ T 'icon=\ntext=1 Email' 'icon=\ntext=1 meddelande'
;;
*)
- printf 'icon=\ntext=%d Emails' $sum
+ T 'icon=\ntext=%d Emails' 'icon=\ntext=%d meddelanden' $sum
;;
esac
- [ $new -gt 0 ] && printf ' (%d Unread)' $new
+ [ $new -gt 0 ] && T ' (%d Unread)' ' (%d ölasta)' $new
echo
echo $new >"$CACHE"
}
@@ -42,12 +53,14 @@ count()
case $((new_mails - prev)) in
1)
- title='New Email'
- desc='1 new email has been received'
+ title="$(T 'New Email' 'Nytt meddelande')"
+ desc="$(T '1 new email has been received'
+ '1 nytt meddelande har mottagits')"
;;
*)
- title='New Emails'
- desc="$new_mails new emails have been received"
+ title="$(T 'New Emails' 'Nya meddelanden')"
+ desc="$(T "$new_mails new emails have been received"
+ "$new_mails nya meddelanden har mottagits")"
;;
esac
@@ -67,4 +80,4 @@ do
count | jo
;;
esac
-done
+done \ No newline at end of file
diff --git a/.config/eww/scripts/timer-listener b/.config/eww/scripts/timer-listener
new file mode 100755
index 0000000..c604295
--- /dev/null
+++ b/.config/eww/scripts/timer-listener
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+cd "${0%/*}"
+
+while :
+do
+ while read line <../data/timer.pipe
+ do
+ if [ "$line" = "" ]
+ then
+ echo
+ else
+ printf '  %s\n' "$line"
+ fi
+ done
+done
diff --git a/.config/foot/foot.ini b/.config/foot/foot.ini
index a5db8b1..856afc9 100644
--- a/.config/foot/foot.ini
+++ b/.config/foot/foot.ini
@@ -12,21 +12,21 @@ foreground=C5C8C6
background=2B303B
[key-bindings]
-scrollback-up-line=Control+k
-scrollback-down-line=Control+j
-scrollback-up-half-page=Control+Shift+k
-scrollback-down-half-page=Control+Shift+j
-scrollback-end=Control+g
+# scrollback-up-line=Control+k
+# scrollback-down-line=Control+j
+# scrollback-up-half-page=Control+Shift+k
+# scrollback-down-half-page=Control+Shift+j
+# scrollback-end=Control+g
search-start=Control+slash
-[search-bindings]
-find-prev=Control+p
-find-next=Control+n
-cursor-left=Control+h
-cursor-right=Control+l
-cursor-left-word=Control+Shift+h
-cursor-right-word=Control+Shift+l
-delete-next-word=Control+Delete Control+Shift+Delete
+# [search-bindings]
+# find-prev=Control+p
+# find-next=Control+n
+# cursor-left=Control+h
+# cursor-right=Control+l
+# cursor-left-word=Control+Shift+h
+# cursor-right-word=Control+Shift+l
+# delete-next-word=Control+Delete Control+Shift+Delete
[tweak]
overflowing-glyphs=no
diff --git a/.config/hypr/hyprland.conf.in b/.config/hypr/hyprland.conf.in
index 6d49e45..17631ff 100644
--- a/.config/hypr/hyprland.conf.in
+++ b/.config/hypr/hyprland.conf.in
@@ -14,12 +14,12 @@ ecosystem {
no_update_news = yes
}
-# Remember to coppy settings to sddm.hyprland.conf
+# Remember to copy settings to sddm.hyprland.conf
input {
accel_profile = "adaptive"
follow_mouse = 1
kb_layout = mango, mango, mango, us
- kb_variant = basic, swedish, german, basic
+ kb_variant = swedish, basic, german, basic
kb_options = lv3:switch,compose:ralt
repeat_delay = 360 # 180
repeat_rate = 35
@@ -70,8 +70,7 @@ animations {
}
gestures {
- workspace_swipe = yes
- workspace_swipe_fingers = 4
+ gesture = 4, horizontal, workspace
workspace_swipe_distance = 400
workspace_swipe_min_speed_to_force = 10
}
@@ -92,10 +91,11 @@ bind = $modkey, Q, killactive
bind = $modkey SHIFT, F, togglefloating
# Global programs
-__bind(C, emacsclient -que '(popup-command-full-calc)')
+__bind(C, qalculate-gtk)
__bind(E, emacsclient -ca emacs)
__bind(M, foot aerc)
__bind(W, firefox)
+__bind(Z, woomer)
__bind(return, foot)
__bind(space, fuzzel -I --log-level=warning --show-actions)
__bind( , print, scst)
@@ -175,6 +175,10 @@ __bind(SHIFT, xf86monbrightnessup, lux -S 50)
__binde(, xf86monbrightnessdown, lux -D 5)
__bind(SHIFT, xf86monbrightnessdown, lux -s 0)
+__binde(, xf86audioplay, playerctl play-pause)
+__binde(, xf86audionext, playerctl next)
+__binde(, xf86audioprev, playerctl previous)
+
# Settings key on Framework laptop
__binde(, xf86audiomedia,
foot sh -c 'cd ~/.config/hypr && nvim hyprland.conf.in')
@@ -191,3 +195,8 @@ bindm = $modkey, mouse:273, resizewindow
windowrulev2 = float, class:^(org\.pwmt\.zathura)$
windowrulev2 = size 50% 90%, class:^(org\.pwmt\.zathura)$
windowrulev2 = center, class:^(org\.pwmt\.zathura)$
+
+# Open calculator in a floating window
+windowrulev2 = float, class:^(qalculate-gtk)$
+windowrulev2 = size 50% 50%, class:^(qalculate-gtk)$
+windowrulev2 = center, class:^(qalculate-gtk)$
diff --git a/.local/bin/Makefile b/.local/bin/Makefile
new file mode 100644
index 0000000..2f659f9
--- /dev/null
+++ b/.local/bin/Makefile
@@ -0,0 +1,30 @@
+TEXTDOMAINDIR=/usr/local/share/locale
+
+all:
+
+extract:
+ mkdir -p po/sv
+ for x in vlt; \
+ do \
+ xgettext -L Shell -k_ --from-code=UTF-8 -o "po/$$x.pot" "$$x"; \
+ for l in sv_SE; \
+ do \
+ d="$${l%_*}"; \
+ if [ -e "po/$$d/$$x.po" ]; \
+ then \
+ msgmerge -U "po/$$d/$$x.po" "po/$$x.pot"; \
+ else \
+ msginit -l "$$l.UTF-8" -i "po/$$x.pot" -o "po/$$d/$$x.po"; \
+ fi; \
+ done; \
+ done
+
+compile:
+ mkdir -p "$(TEXTDOMAINDIR)/sv/LC_MESSAGES"
+ for l in sv; \
+ do \
+ for x in vlt; \
+ do \
+ msgfmt "po/$$l/$$x.po" -o "$(TEXTDOMAINDIR)/$$l/LC_MESSAGES/$$x.mo"; \
+ done; \
+ done
diff --git a/.local/bin/alarm b/.local/bin/alarm
new file mode 100755
index 0000000..716ea1b
--- /dev/null
+++ b/.local/bin/alarm
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+set -e
+
+export NOTIFY_LONG=alarm
+export NOTIFY_SHORT="${0##*/}"
+
+readonly PIPE="${XDG_CONFIG_HOME:-$HOME/.config}/eww/data/timer.pipe"
+exec >"$PIPE"
+
+if [ $# -gt 1 ]
+then
+ >&2 printf 'Usage: %s [duration]\n' "${0##*/}"
+ exit 1
+fi
+
+stdbuf -oL -- patience -f '%m:%02S' "${1:-m=1}"
+echo ''
+exec >&-
+
+notify 'Timer Complete' 'The global timer has reached its end'
+aplay -q ~/media/sfx/alarm.wav
diff --git a/.local/bin/po/sv/vlt.po b/.local/bin/po/sv/vlt.po
new file mode 100644
index 0000000..a952f3d
--- /dev/null
+++ b/.local/bin/po/sv/vlt.po
@@ -0,0 +1,178 @@
+# Swedish translations for PACKAGE package
+# Svenska översättningar för paket PACKAGE.
+# Copyright (C) 2025 THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# Thomas Voss <mail@thomasvoss.com>, 2025.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2025-09-01 22:58+0200\n"
+"PO-Revision-Date: 2025-09-01 22:45+0200\n"
+"Last-Translator: Thomas Voss <mail@thomasvoss.com>\n"
+"Language-Team: Swedish <tp-sv@listor.tp-sv.se>\n"
+"Language: sv\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: vlt:25
+msgid "Invalid input"
+msgstr "Ogiltig inmatning"
+
+#: vlt:26
+msgid "Empty strings do not constitute valid input"
+msgstr "Tomma strängar utgör ej giltig indata"
+
+#: vlt:34
+#, sh-printf-format
+msgid ""
+"Usage: %s add [-c]\n"
+" %s edit [-c]\n"
+" %s get\n"
+" %s raw category password\n"
+" %s rm [-c]\\n"
+msgstr ""
+"Användning: %s add [-c]\n"
+" %s edit [-c]\n"
+" %s get\n"
+" %s raw kategori lösenord\n"
+" %s rm [-c]\\n"
+
+#: vlt:97
+msgid "Password name"
+msgstr "Lösenordsnamn"
+
+#: vlt:102
+msgid "Failed to add password"
+msgstr "Misslyckades med att skapa lösenord"
+
+#: vlt:103
+#, sh-printf-format
+msgid "The password ‘%s’ already exists"
+msgstr "Lösenordet ”%s” finns redan"
+
+#: vlt:109 vlt:259
+msgid "Secret key"
+msgstr "Hemlig nyckel"
+
+#: vlt:110 vlt:260
+msgid "Digits"
+msgstr "Siffror"
+
+#: vlt:111 vlt:261
+msgid "Period"
+msgstr "Period"
+
+#: vlt:125 vlt:275
+msgid "2FA key added"
+msgstr "2FA-nyckel tillagd"
+
+#: vlt:126 vlt:276
+#, sh-printf-format
+msgid "The 2FA key ‘%s’ was added with the digit length ‘%d’ and period ‘%d’"
+msgstr "2FA-nyckeln ”%s” lades till med siffralägden ”%d” och perioden ”%d”"
+
+#: vlt:128 vlt:278
+msgid "Password"
+msgstr "Lösenord"
+
+#: vlt:133
+msgid "Password added"
+msgstr "Lösenord tillagt"
+
+#: vlt:134
+#, sh-printf-format
+msgid "The password ‘%s’ was added to the category ‘%s’"
+msgstr "Lösenordet ”%s” tillagt till kategorin ”%s”"
+
+#: vlt:142
+msgid "Category to create"
+msgstr "Kategori att skapa"
+
+#: vlt:145
+msgid "Failed to Create Category"
+msgstr "Misslyckades med att skapa kategori"
+
+#: vlt:145
+#, sh-printf-format
+msgid "The category ‘%s’ already exists"
+msgstr "Kategorin ”%s” finns redan"
+
+#: vlt:153
+msgid "Category created"
+msgstr "Kategori skapad"
+
+#: vlt:153
+#, sh-printf-format
+msgid "The password category ‘%s’ was created"
+msgstr "Lösenordskategorin ”%s” skapades"
+
+#: vlt:177
+msgid "2FA code copied to the clipboard"
+msgstr "2FA-kod kopierad till urklipp"
+
+#: vlt:178
+#, sh-printf-format
+msgid "The 2FA code for ‘%s’ was copied to the clipboard"
+msgstr "2FA-koden för ”%s” har kopierats till urklipp"
+
+#: vlt:183
+msgid "Password copied to the clipboard"
+msgstr "Lösenord kopierat till urklipp"
+
+#: vlt:184
+#, sh-printf-format
+msgid "The password for ‘%s’ was copied to the clipboard"
+msgstr "Lösenordet ”%s” har kopierats till urklipp"
+
+#: vlt:213
+msgid "Password removed"
+msgstr "Lösenord borttaget"
+
+#: vlt:214
+#, sh-printf-format
+msgid "The password ‘%s’ was removed from the category ‘%s’"
+msgstr "Lösenordet ”%s” har tagits bort från kategorin ”%s”"
+
+#: vlt:225
+msgid "Failed to remove category"
+msgstr "Kategorin kunde ej tas bort"
+
+#: vlt:225
+#, sh-printf-format
+msgid "The category ‘%s’ is not empty"
+msgstr "Kategorin ”%s” är ej tom"
+
+#: vlt:232
+msgid "Category removed"
+msgstr "Kategori borttagen"
+
+#: vlt:232
+#, sh-printf-format
+msgid "The category ‘%s’ was removed"
+msgstr "Kategorin ”%s” har tagits bort"
+
+#: vlt:282
+msgid "Password edited"
+msgstr "Lösenord redigerat"
+
+#: vlt:283
+#, sh-printf-format
+msgid "The password ‘%s’ in the category ‘%s’ was edited"
+msgstr "Lösenordet ”%s” i kategorin ”%s” har redigerats"
+
+#: vlt:292
+msgid "Category name"
+msgstr "Kategorinamn"
+
+#: vlt:297
+msgid "Category edited"
+msgstr "Kategori redigerad"
+
+#: vlt:297
+#, sh-printf-format
+msgid "The category ‘%s’ was renamed"
+msgstr "Kategorin ”%s” har bytt namn"
diff --git a/.local/bin/po/vlt.pot b/.local/bin/po/vlt.pot
new file mode 100644
index 0000000..c93caf5
--- /dev/null
+++ b/.local/bin/po/vlt.pot
@@ -0,0 +1,172 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2025-09-01 22:58+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: vlt:25
+msgid "Invalid input"
+msgstr ""
+
+#: vlt:26
+msgid "Empty strings do not constitute valid input"
+msgstr ""
+
+#: vlt:34
+#, sh-printf-format
+msgid ""
+"Usage: %s add [-c]\n"
+" %s edit [-c]\n"
+" %s get\n"
+" %s raw category password\n"
+" %s rm [-c]\\n"
+msgstr ""
+
+#: vlt:97
+msgid "Password name"
+msgstr ""
+
+#: vlt:102
+msgid "Failed to add password"
+msgstr ""
+
+#: vlt:103
+#, sh-printf-format
+msgid "The password ‘%s’ already exists"
+msgstr ""
+
+#: vlt:109 vlt:259
+msgid "Secret key"
+msgstr ""
+
+#: vlt:110 vlt:260
+msgid "Digits"
+msgstr ""
+
+#: vlt:111 vlt:261
+msgid "Period"
+msgstr ""
+
+#: vlt:125 vlt:275
+msgid "2FA key added"
+msgstr ""
+
+#: vlt:126 vlt:276
+#, sh-printf-format
+msgid "The 2FA key ‘%s’ was added with the digit length ‘%d’ and period ‘%d’"
+msgstr ""
+
+#: vlt:128 vlt:278
+msgid "Password"
+msgstr ""
+
+#: vlt:133
+msgid "Password added"
+msgstr ""
+
+#: vlt:134
+#, sh-printf-format
+msgid "The password ‘%s’ was added to the category ‘%s’"
+msgstr ""
+
+#: vlt:142
+msgid "Category to create"
+msgstr ""
+
+#: vlt:145
+msgid "Failed to Create Category"
+msgstr ""
+
+#: vlt:145
+#, sh-printf-format
+msgid "The category ‘%s’ already exists"
+msgstr ""
+
+#: vlt:153
+msgid "Category created"
+msgstr ""
+
+#: vlt:153
+#, sh-printf-format
+msgid "The password category ‘%s’ was created"
+msgstr ""
+
+#: vlt:177
+msgid "2FA code copied to the clipboard"
+msgstr ""
+
+#: vlt:178
+#, sh-printf-format
+msgid "The 2FA code for ‘%s’ was copied to the clipboard"
+msgstr ""
+
+#: vlt:183
+msgid "Password copied to the clipboard"
+msgstr ""
+
+#: vlt:184
+#, sh-printf-format
+msgid "The password for ‘%s’ was copied to the clipboard"
+msgstr ""
+
+#: vlt:213
+msgid "Password removed"
+msgstr ""
+
+#: vlt:214
+#, sh-printf-format
+msgid "The password ‘%s’ was removed from the category ‘%s’"
+msgstr ""
+
+#: vlt:225
+msgid "Failed to remove category"
+msgstr ""
+
+#: vlt:225
+#, sh-printf-format
+msgid "The category ‘%s’ is not empty"
+msgstr ""
+
+#: vlt:232
+msgid "Category removed"
+msgstr ""
+
+#: vlt:232
+#, sh-printf-format
+msgid "The category ‘%s’ was removed"
+msgstr ""
+
+#: vlt:282
+msgid "Password edited"
+msgstr ""
+
+#: vlt:283
+#, sh-printf-format
+msgid "The password ‘%s’ in the category ‘%s’ was edited"
+msgstr ""
+
+#: vlt:292
+msgid "Category name"
+msgstr ""
+
+#: vlt:297
+msgid "Category edited"
+msgstr ""
+
+#: vlt:297
+#, sh-printf-format
+msgid "The category ‘%s’ was renamed"
+msgstr ""
diff --git a/.local/bin/vlt b/.local/bin/vlt
index 20219d8..9bf7f60 100755
--- a/.local/bin/vlt
+++ b/.local/bin/vlt
@@ -5,25 +5,37 @@ set -e
export NOTIFY_LONG=vault
export NOTIFY_SHORT="${0##*/}"
+export TEXTDOMAIN=vlt
+export TEXTDOMAINDIR=/usr/local/share/locale
+
+. gettext.sh
+_()
+{
+ fmt="$1"
+ shift
+ printf "$(eval_gettext "$fmt")" "$@"
+}
+
sanitize()
{
s="${1#"${1%%[![:space:]]*}"}"
s="${s%"${s##*[![:space:]]}"}"
if [ "${#s}" -eq 0 ]
then
- notify 'Invalid Input' 'Empty strings do not constitute valid input'
+ notify "$(_ 'Invalid input')" \
+ "$(_ 'Empty strings do not constitute valid input')"
exit 1
fi
}
usage()
{
- cat <<-EOF >&2
- Usage: ${0##*/} add [-c]
- ${0##*/} edit [-c]
- ${0##*/} get
- ${0##*/} rm [-c]
- EOF
+ s="${0##*/}"
+ _ "Usage: %s add [-c]
+ %s edit [-c]
+ %s get
+ %s raw category password
+ %s rm [-c]\n" "$s" "$s" "$s" "$s" "$s"
exit 1
}
@@ -82,20 +94,21 @@ add()
c="$(xecho "$data" | jq -r 'keys | .[]' | osel)"
- prompt 'Password name'
+ prompt "$(_ 'Password name')"
n="$s"
xecho "$data" \
| jq -e --arg c "$c" --arg n "$n" '.[$c] | has($n) | not' >/dev/null || {
- notify 'Failed To Add Password' "The password ‘$n’ already exists"
+ notify "$(_ 'Failed to add password')" \
+ "$(_ "The password ‘%s’ already exists" "$n")"
exit 1
}
if [ "$VAULT_2FA" = "$c" ]
then
- xprompt 'Secret key'; k="$s"
- prompt 'Digits'; d="$s"
- prompt 'Period'
+ xprompt "$(_ 'Secret key')"; k="$s"
+ prompt "$(_ 'Digits')"; d="$s"
+ prompt "$(_ 'Period')"
xecho "$data" \
| jq --arg c "$c" \
@@ -109,16 +122,16 @@ add()
"period": ($p | tonumber)
}}' \
| enchive archive >"$VAULT"
- [ ! -t 2 ] && notify '2FA Key Added' \
- "The 2FA key ‘$n’ was added with the digit length ‘$d’ and period ‘$p’"
+ [ ! -t 2 ] && notify "$(_ '2FA key added')" \
+ "$(_ "The 2FA key ‘%s’ was added with the digit length ‘%d’ and period ‘%d’" "$n" "$d" "$p")"
else
- xprompt 'Password'
+ xprompt "$(_ 'Password')"
xecho "$data" \
| jq --arg c "$c" --arg n "$n" --arg s "$s" '.[$c] += {($n): $s}' \
| enchive archive >"$VAULT"
- [ ! -t 2 ] && notify 'Password Added' \
- "The password ‘$n’ was added to the category ‘$c’"
+ [ ! -t 2 ] && notify "$(_ 'Password added')" \
+ "$(_ "The password ‘%s’ was added to the category ‘%s’" "$n" "$c")"
fi
}
@@ -126,10 +139,10 @@ add_c()
{
readonly data="$(enchive extract <"$VAULT")"
- prompt 'Category to create'
+ prompt "$(_ 'Category to create')"
xecho "$data" | jq -e --arg s "$s" 'has($s) | not' >/dev/null || {
- notify 'Failed To Create Category' "The category ‘$s’ already exists"
+ notify "$(_ 'Failed to Create Category')" "$(_ "The category ‘%s’ already exists" "$s")"
exit 1
}
@@ -137,7 +150,7 @@ add_c()
| jq --arg s "$s" '. + {($s): {}}' \
| enchive archive >"$VAULT"
[ ! -t 2 ] && \
- notify 'Category Created' "The password category ‘$s’ was created"
+ notify "$(_ 'Category created')" "$(_ "The password category ‘%s’ was created" "$s")"
}
get()
@@ -161,14 +174,14 @@ get()
')" \
| wl-copy -no \
&& [ ! -t 2 ] \
- && notify '2FA Code Copied To The Clipboard' \
- "The 2FA code for ‘$o’ was copied to the clipboard"
+ && notify "$(_ '2FA code copied to the clipboard')" \
+ "$(_ "The 2FA code for ‘%s’ was copied to the clipboard" "$o")"
else
jq -r --arg c "$c" --arg o "$o" '.[$c] | .[$o]' \
| wl-copy -no \
&& [ ! -t 2 ] \
- && notify 'Password Copied To The Clipboard' \
- "The password for ‘$o’ was copied to the clipboard"
+ && notify "$(_ 'Password copied to the clipboard')" \
+ "$(_ "The password for ‘%s’ was copied to the clipboard" "$o")"
fi
}
@@ -197,8 +210,8 @@ rm_()
xecho "$data" \
| jq --arg c "$c" --arg n "$n" 'del(.[$c] | .[$n])' \
| enchive archive >"$VAULT"
- [ ! -t 2 ] && notify 'Removed Password' \
- "The password ‘$n’ was removed from the category ‘$c’"
+ [ ! -t 2 ] && notify "$(_ 'Password removed')" \
+ "$(_ "The password ‘%s’ was removed from the category ‘%s’" "$n" "$c")"
}
rm_c()
@@ -209,14 +222,14 @@ rm_c()
xecho "$data" \
| jq -e --arg c "$c" '.[$c] | length == 0' >/dev/null || {
- notify 'Failed To Remove Category' "The category ‘$c’ is not empty"
+ notify "$(_ 'Failed to remove category')" "$(_ "The category ‘%s’ is not empty" "$c")"
exit 1
}
xecho "$data" \
| jq --arg c "$c" 'del(.[$c])' \
| enchive archive >"$VAULT"
- [ ! -t 2 ] && notify 'Removed Category' "The category ‘$c’ was removed"
+ [ ! -t 2 ] && notify "$(_ 'Category removed')" "$(_ "The category ‘%s’ was removed" "$c")"
}
edit()
@@ -243,9 +256,9 @@ edit()
if [ "$VAULT_2FA" = "$c" ]
then
- xprompt 'Secret key'; k="$s"
- prompt 'Digits'; d="$s"
- prompt 'Period'
+ xprompt "$(_ 'Secret key')"; k="$s"
+ prompt "$(_ 'Digits')"; d="$s"
+ prompt "$(_ 'Period')"
xecho "$data" \
| jq --arg c "$c" \
@@ -259,15 +272,15 @@ edit()
"period": ($p | tonumber)
}}' \
| enchive archive >"$VAULT"
- [ ! -t 2 ] && notify '2FA Key Added' \
- "The 2FA key ‘$n’ was added with the digit length ‘$d’ and period ‘$p’"
+ [ ! -t 2 ] && notify "$(_ '2FA key added')" \
+ "$(_ "The 2FA key ‘%s’ was added with the digit length ‘%d’ and period ‘%d’" "$n" "$d" "$p")"
else
- xprompt 'Password'
+ xprompt "$(_ 'Password')"
xecho "$data" \
| jq --arg c "$c" --arg n "$n" --arg s "$s" '.[$c] += {($n): $s}' \
| enchive archive >"$VAULT"
- [ ! -t 2 ] && notify 'Password Edit' \
- "The password ‘$n’ in the category ‘$c’ was changed"
+ [ ! -t 2 ] && notify "$(_ 'Password edited')" \
+ "$(_ "The password ‘%s’ in the category ‘%s’ was edited" "$n" "$c")"
fi
}
@@ -276,12 +289,12 @@ edit_c()
readonly data="$(enchive extract <"$VAULT")"
c="$(xecho "$data" | jq -r 'keys | .[]' | osel)"
- prompt 'Category name'
+ prompt "$(_ 'Category name')"
xecho "$data" \
| jq --arg o "$c" --arg n "$s" \
'with_entries(if .key == $o then .key = $n else . end)' \
| enchive archive >"$VAULT"
- [ ! -t 2 ] && notify 'Category Edit' "The category ‘$c’ was renamed"
+ [ ! -t 2 ] && notify "$(_ 'Category edited')" "$(_ "The category ‘%s’ was renamed" "$c")"
}
raw()
diff --git a/.local/share/applications/Vault.desktop b/.local/share/applications/Vault.desktop
index 76ef164..f296257 100644
--- a/.local/share/applications/Vault.desktop
+++ b/.local/share/applications/Vault.desktop
@@ -4,7 +4,6 @@ Categories=Utility;Database;TextTools;Archiving;Security
Comment=Store and Retrieve Passwords
Exec=vlt get
GenericName=Password Store
-Icon=/home/thomas/down/lock.png
Keywords=Password
Name=Vault
Terminal=false
diff --git a/.local/share/vault/vault.sec b/.local/share/vault/vault.sec
index d23cf22..e341e4b 100644
--- a/.local/share/vault/vault.sec
+++ b/.local/share/vault/vault.sec
Binary files differ
diff --git a/.xkb/symbols/mango b/.xkb/symbols/mango
index 642dc7e..d62ede2 100644
--- a/.xkb/symbols/mango
+++ b/.xkb/symbols/mango
@@ -8,9 +8,8 @@ xkb_symbols "basic" {
key <AE01> { [ exclam, 1, exclam, 1 ] };
key <AE02> { [ at, 2, at, 2 ] };
key <AE03> { [ numbersign, 3, numbersign, 3 ] };
- key <AE04> { [ EuroSign, 4, dollar, 4 ] };
key <AE04> { [ dollar, 4, EuroSign, EuroSign ] };
- key <AE05> { [ percent, 5, dollar, 5 ] };
+ key <AE05> { [ percent, 5, EuroSign, EuroSign ] };
key <AE06> { [ asciicircum, 6, asciicircum, 6 ] };
key <AE07> { [ ampersand, 7, logicaland, 7 ] };
key <AE08> { [ asterisk, 8, U22C5, 8 ] }; // ⋅
@@ -19,6 +18,7 @@ xkb_symbols "basic" {
key <AE11> { [ minus, underscore, endash, emdash ] };
key <AD03> { [ e, E, eacute, Eacute ] };
+ key <AD05> { [ t, T, thorn, THORN ] };
key <AD07> { [ u, U, udiaeresis, Udiaeresis ] };
// key <AD08> { [ i, I, U0133, U0132 ] }; // ij and IJ
key <AD08> { [ i, I, idiaeresis, Idiaeresis ] };
@@ -46,16 +46,29 @@ xkb_symbols "german" {
include "mango(basic)"
name[Group1] = "Mango (German)";
- key <AC10> { [ NoSymbol, NoSymbol, singlelowquotemark, doublelowquotemark ] };
- key <AC11> { [ NoSymbol, NoSymbol, leftsinglequotemark, leftdoublequotemark ] };
+ key <AD11> { [ udiaeresis, Udiaeresis, bracketleft, braceleft ] };
+ key <AD12> { [ ssharp, U1E9E, bracketright, braceright ] }; // ẞ
+ key <AC10> { [ odiaeresis, Odiaeresis, semicolon, colon ] };
+ key <AC11> { [ adiaeresis, Adiaeresis, apostrophe, quotedbl ] };
+
+ key <AC08> { [ k, K, singlelowquotemark, doublelowquotemark ] };
+ key <AC09> { [ l, L, leftsinglequotemark, leftdoublequotemark ] };
};
partial
xkb_symbols "swedish" {
include "mango(basic)"
- name[Group1] = "Mango (Swedish)";
+ name[Group1] = "Mango (Swedish)";
+
+ key <AD11> { [ aring, Aring, bracketleft, braceleft ] };
+ key <AD12> { [ U0301, U0300, bracketright, braceright ] }; // ◌́ and ◌̀
+ key <AC10> { [ odiaeresis, Odiaeresis, semicolon, colon ] };
+ key <AC11> { [ adiaeresis, Adiaeresis, apostrophe, quotedbl ] };
+
+ key <AC08> { [ k, K, rightsinglequotemark, rightdoublequotemark ] };
+ key <AC09> { [ l, L, rightsinglequotemark, rightdoublequotemark ] };
- key <AD11> { [ aring, Aring, bracketleft, braceleft ] };
- key <AC10> { [ odiaeresis, Odiaeresis, semicolon, colon ] };
- key <AC11> { [ adiaeresis, Adiaeresis, apostrophe, quotedbl ] };
+ // Danska och norska bokstäver på nivå 3
+ key <AC01> { [ a, A, ae, AE ] };
+ key <AD09> { [ o, O, oslash, Oslash ] };
};