summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bashrc4
-rw-r--r--.config/X11/xcompose5
-rw-r--r--.config/emacs/early-init.el9
-rw-r--r--.config/emacs/init.el41
-rw-r--r--.config/emacs/modules/mm-abbrev.el8
-rw-r--r--.config/emacs/modules/mm-editing.el66
-rw-r--r--.config/emacs/modules/mm-humanwave.el141
-rw-r--r--.config/emacs/modules/mm-modeline.el2
-rw-r--r--.config/emacs/modules/mm-projects.el29
-rw-r--r--.config/emacs/modules/mm-theme.el20
-rw-r--r--.config/emacs/modules/mm-treesit.el22
-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/hypr/hyprland.conf.in5
-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.secbin12914 -> 13096 bytes
-rw-r--r--.xkb/symbols/mango28
28 files changed, 1145 insertions, 115 deletions
diff --git a/.bashrc b/.bashrc
index cfdc40b..458fc05 100644
--- a/.bashrc
+++ b/.bashrc
@@ -71,7 +71,6 @@ 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\" -Djavafx.cachedir=\"$XDG_CACHE_HOME/openjfx\""
export MAILDIR="$HOME/mail"
export NODE_REPL_HISTORY="$XDG_DATA_HOME/node_repl_history"
export NPM_CONFIG_USERCONFIG="$XDG_CONFIG_HOME/npm/npmrc"
@@ -82,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"
@@ -116,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 a8620d6..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,15 +294,6 @@ 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
@@ -326,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
@@ -333,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-editing.el b/.config/emacs/modules/mm-editing.el
index 14f5211..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,6 +127,11 @@ 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
@@ -295,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)
@@ -315,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
@@ -326,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
@@ -358,6 +412,12 @@ is as described by `emmet-expand-line'."
;;; Add Missing Extensions
(dolist (pattern '("\\.tmac\\'" "\\.mom\\'"))
- (add-to-list 'auto-mode-alist (cons pattern 'nroff-mode)))
+ (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 d1ec6f0..44fc115 100644
--- a/.config/emacs/modules/mm-modeline.el
+++ b/.config/emacs/modules/mm-modeline.el
@@ -90,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 00342f5..02d7af4 100644
--- a/.config/emacs/modules/mm-projects.el
+++ b/.config/emacs/modules/mm-projects.el
@@ -19,7 +19,7 @@ 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")))
(let* ((list-dir
(lambda (path)
@@ -47,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))
@@ -70,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")))
@@ -96,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 95c96c9..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
@@ -222,7 +234,7 @@ See also the `mm-theme-background-opacity' variable."
;;; Indent Guides
-(when mm-darwin-p
+(when mm-humanwave-p
(use-package highlight-indent-guides
:ensure t
:hook ((jinja2-mode vue-ts-mode mhtml-mode) . highlight-indent-guides-mode)
@@ -231,4 +243,4 @@ See also the `mm-theme-background-opacity' variable."
(highlight-indent-guides-auto-even-face-perc 30)
(highlight-indent-guides-auto-odd-face-perc 0)))
-(provide 'mm-theme) \ No newline at end of file
+(provide 'mm-theme)
diff --git a/.config/emacs/modules/mm-treesit.el b/.config/emacs/modules/mm-treesit.el
index 4e052de..bd146ce 100644
--- a/.config/emacs/modules/mm-treesit.el
+++ b/.config/emacs/modules/mm-treesit.el
@@ -35,6 +35,8 @@
"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")
@@ -43,6 +45,9 @@
"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")
@@ -112,11 +117,13 @@ 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.
@@ -132,9 +139,9 @@ 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))
- (name-mode (intern (format "%s-mode" lang)))
- (name-ts-mode (intern (format "%s-ts-mode" lang))))
+ (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
@@ -155,6 +162,9 @@ languages should be listed here.")
(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
@@ -212,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/hypr/hyprland.conf.in b/.config/hypr/hyprland.conf.in
index dd22153..17631ff 100644
--- a/.config/hypr/hyprland.conf.in
+++ b/.config/hypr/hyprland.conf.in
@@ -19,7 +19,7 @@ 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
}
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 5f1a25f..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 f2ab38f..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 ] }; // ⋅
@@ -47,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 ] };
};