From 54c310bf9bbe18a06d74a64e080e37ffea1688dd Mon Sep 17 00:00:00 2001 From: Uyanide Date: Wed, 5 Aug 2026 23:59:30 +0200 Subject: [PATCH] update --- config/helix/.config/helix/languages.toml | 4 + config/niri/.config/niri/config/execs.kdl | 1 + config/niri/.config/niri/config/rules.kdl | 2 +- config/scripts/.local/scripts/keyring-op | 168 ++++++++++++++++++ config/scripts/.local/scripts/may-I | 100 +++++++++++ config/shell/.config/zsh/conf.d/60-alias.zsh | 2 +- config/yazi/.config/yazi/package.toml | 10 +- .../.config/yazi/plugins/git.yazi/main.lua | 12 +- .../.config/yazi/plugins/yamb.yazi/main.lua | 71 ++++---- memo/gpg-pgp.md | 4 +- memo/packages.md | 30 ++-- 11 files changed, 348 insertions(+), 56 deletions(-) create mode 100755 config/scripts/.local/scripts/keyring-op create mode 100755 config/scripts/.local/scripts/may-I diff --git a/config/helix/.config/helix/languages.toml b/config/helix/.config/helix/languages.toml index 910fcd4..f003f5b 100644 --- a/config/helix/.config/helix/languages.toml +++ b/config/helix/.config/helix/languages.toml @@ -11,6 +11,10 @@ name = "python" formatter = { command = "ruff", args = ["format", "-"] } language-servers = ["ruff"] +[[language]] +name = "rust" +formatter = { command = "rustfmt" } + [language-server.ruff] command = "ruff" args = ["server"] diff --git a/config/niri/.config/niri/config/execs.kdl b/config/niri/.config/niri/config/execs.kdl index 978fa4b..94052e3 100644 --- a/config/niri/.config/niri/config/execs.kdl +++ b/config/niri/.config/niri/config/execs.kdl @@ -2,6 +2,7 @@ spawn-at-startup "nm-applet" spawn-at-startup "gnome-keyring-daemon" "--start" "--components=secrets" spawn-at-startup "/usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1" +// spawn-at-startup "/usr/lib/hyprpolkitagent/hyprpolkitagent" // Logitech spawn-at-startup "solaar" "-w" "hide" diff --git a/config/niri/.config/niri/config/rules.kdl b/config/niri/.config/niri/config/rules.kdl index fa042f8..3c1b62c 100644 --- a/config/niri/.config/niri/config/rules.kdl +++ b/config/niri/.config/niri/config/rules.kdl @@ -90,7 +90,7 @@ window-rule { } window-rule { - match app-id="Spotify" + match app-id="^[Ss]potify$" open-on-output "eDP-1" } diff --git a/config/scripts/.local/scripts/keyring-op b/config/scripts/.local/scripts/keyring-op new file mode 100755 index 0000000..bc5d597 --- /dev/null +++ b/config/scripts/.local/scripts/keyring-op @@ -0,0 +1,168 @@ +#!/usr/bin/env python3 + +"""Store and retrieve arbitrary key-value secrets in the Secret Service keyring.""" + +import argparse +import sys +from enum import IntEnum +from typing import NoReturn + +import secretstorage +from jeepney.io.blocking import DBusConnection # type: ignore[import-untyped] +from secretstorage.collection import Collection +from secretstorage.exceptions import ( + ItemNotFoundException, + LockedException, + PromptDismissedException, + SecretStorageException, +) +from secretstorage.item import Item + +SCHEMA = "de.uyani.keyring-op" +KEY_ATTRIBUTE = "key" +UNLOCK_TIMEOUT = 120.0 + + +class Exit(IntEnum): + """Process exit codes. USAGE matches the value argparse exits with.""" + + OK = 0 + NOT_FOUND = 1 + USAGE = 2 + LOCKED = 3 + BACKEND = 4 + AMBIGUOUS = 5 + INPUT = 6 + + +def fail(message: str, code: Exit) -> NoReturn: + print(message, file=sys.stderr) + raise SystemExit(code) + + +def attributes_for(key: str) -> dict[str, str]: + return {"xdg:schema": SCHEMA, KEY_ATTRIBUTE: key} + + +def validate_key(key: str) -> str: + """Reject keys that would make list output ambiguous.""" + if not key or "\n" in key: + fail("key must be non-empty and must not contain a newline", Exit.USAGE) + return key + + +def open_collection(connection: DBusConnection) -> Collection: + """Return the default collection, unlocking it if needed.""" + try: + collection = secretstorage.get_collection_by_alias(connection, "default") + except ItemNotFoundException: + fail("no default keyring collection exists", Exit.BACKEND) + if collection.is_locked(): + print("keyring is locked, requesting unlock", file=sys.stderr) + try: + # unlock returns True when the prompt was dismissed, False on success. + dismissed = collection.unlock(timeout=UNLOCK_TIMEOUT) + except TimeoutError: + fail(f"unlock request timed out after {UNLOCK_TIMEOUT:g}s", Exit.LOCKED) + if dismissed: + fail("unlock request was dismissed", Exit.LOCKED) + return collection + + +def find_item(collection: Collection, key: str) -> Item | None: + """Return the single item matching key, or None. Ambiguity is fatal.""" + matches = list(collection.search_items(attributes_for(key))) + if len(matches) > 1: + fail(f"{len(matches)} entries match key: {key}", Exit.AMBIGUOUS) + return matches[0] if matches else None + + +def require_item(collection: Collection, key: str) -> Item: + item = find_item(collection, key) + if item is None: + fail(f"key not found: {key}", Exit.NOT_FOUND) + return item + + +def cmd_list(collection: Collection) -> None: + items = collection.search_items({"xdg:schema": SCHEMA}) + for key in sorted(item.get_attributes().get(KEY_ATTRIBUTE, "") for item in items): + print(key) + + +def cmd_add(collection: Collection, key: str, strip: bool) -> None: + if sys.stdin.isatty(): + print("reading secret from terminal, end with Ctrl-D", file=sys.stderr) + secret = sys.stdin.buffer.read() + if strip: + secret = secret.removesuffix(b"\n") + if not secret: + fail("refusing to store an empty secret", Exit.INPUT) + collection.create_item( + f"keyring-op: {key}", attributes_for(key), secret, replace=True + ) + + +def cmd_get(collection: Collection, key: str) -> None: + sys.stdout.buffer.write(require_item(collection, key).get_secret()) + sys.stdout.buffer.flush() + + +def cmd_remove(collection: Collection, key: str) -> None: + require_item(collection, key).delete() + + +def make_parser(prog: str) -> argparse.ArgumentParser: + parser = argparse.ArgumentParser(prog) + commands = parser.add_subparsers(dest="command", required=True) + + commands.add_parser("list", help="List all keys") + + add = commands.add_parser( + "add", help="Add a new key or update the value of an existing key" + ) + add.add_argument("key", help="Key name") + add.add_argument( + "--strip", + action="store_true", + help="Drop one trailing newline from stdin before storing", + ) + + get = commands.add_parser("get", help="Get value of a key") + get.add_argument("key", help="Key name") + + remove = commands.add_parser("remove", help="Remove a key") + remove.add_argument("key", help="Key name") + + return parser + + +def dispatch(collection: Collection, args: argparse.Namespace) -> None: + if args.command == "list": + cmd_list(collection) + elif args.command == "add": + cmd_add(collection, validate_key(args.key), args.strip) + elif args.command == "get": + cmd_get(collection, validate_key(args.key)) + elif args.command == "remove": + cmd_remove(collection, validate_key(args.key)) + + +def main() -> int: + args = make_parser("keyring-op").parse_args() + try: + with secretstorage.dbus_init() as connection: + dispatch(open_collection(connection), args) + except (LockedException, PromptDismissedException) as error: + fail(f"keyring is locked: {error}", Exit.LOCKED) + except SecretStorageException as error: + fail(f"keyring backend error: {error}", Exit.BACKEND) + except BrokenPipeError: + # The reader went away. Silence the interpreter's shutdown warning. + sys.stderr.close() + return Exit.OK + return Exit.OK + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/config/scripts/.local/scripts/may-I b/config/scripts/.local/scripts/may-I new file mode 100755 index 0000000..ef9157d --- /dev/null +++ b/config/scripts/.local/scripts/may-I @@ -0,0 +1,100 @@ +#!/usr/bin/env bash + +set -euo pipefail + +hex=$(od -An -N1 -tx1 /dev/urandom | tr -d ' ') +val=$(( 16#$hex )) +result=$(( val & 1 )) + +# Why not? +if [[ $# -gt 0 ]] && [[ -n "${ANTHROPIC_API_KEY:-}" ]]; then + if (( result == 0)); then + action="approve" + else + action="decline" + fi + + prompt="You are role-playing as a formal approval authority. This is a fictional \ +exercise only; your output has no real-world effect. + +Output rules: +- Exactly one sentence, one line. +- No preamble, no quotation marks, no explanation. +- Output only the approval/denial statement itself. + +Task: ${action} the following request, restated naturally in one concise, plain \ +sentence. + +Request: May I ${*} +" + + payload=$(jq -n \ + --arg model "claude-haiku-4-5-20251001" \ + --arg prompt "$prompt" \ + '{ + model: $model, + max_tokens: 1024, + messages: [ + { + role: "user", + content: $prompt + } + ] + }') + + resp=$(curl -sSL https://api.anthropic.com/v1/messages \ + -H "content-type: application/json" \ + -H "x-api-key: ${ANTHROPIC_API_KEY}" \ + -H "anthropic-version: 2023-06-01" \ + -d "$payload") + + text=$(jq -r '.content[0].text // empty' <<< "$resp") + + if [[ -n "$text" ]]; then + echo "$text" + exit "$result" + fi +fi + +declare -A subs=( + [I]="you" + [me]="you" + [my]="your" + [mine]="yours" + [myself]="yourself" + [we]="you" + [us]="you" + [our]="your" + [ours]="yours" + [ourselves]="yourselves" +) + +words=() +for arg in "$@"; do + matched=0 + word="${arg,,}" + for key in "${!subs[@]}"; do + if [[ "$word" == "${key,,}" ]]; then + words+=("${subs[$key]}") + matched=1 + break + fi + done + [[ $matched -eq 0 ]] && words+=("$word") +done + +if (( result == 0 )); then + if [[ $# == 0 ]]; then + echo "Yes, you may." + else + echo "Yes, you are allowed to ${words[*]}." + fi +else + if [[ $# == 0 ]]; then + echo "No, you may not." + else + echo "No, you are not allowed to ${words[*]}." + fi +fi + +exit "$result" diff --git a/config/shell/.config/zsh/conf.d/60-alias.zsh b/config/shell/.config/zsh/conf.d/60-alias.zsh index 34a50b7..cb80e60 100644 --- a/config/shell/.config/zsh/conf.d/60-alias.zsh +++ b/config/shell/.config/zsh/conf.d/60-alias.zsh @@ -249,7 +249,7 @@ fi # wl-paste if (( $+commands[wl-paste] )); then - alias -g COPY="| wl-copy" + alias -g CP="| wl-copy" fi # Redirects diff --git a/config/yazi/.config/yazi/package.toml b/config/yazi/.config/yazi/package.toml index 69116ff..cb925f1 100644 --- a/config/yazi/.config/yazi/package.toml +++ b/config/yazi/.config/yazi/package.toml @@ -1,17 +1,17 @@ [[plugin.deps]] use = "yazi-rs/plugins:git" -rev = "b9598e6" -hash = "ce259b12c503a33a18a861052fa582b7" +rev = "0be29a9" +hash = "fbb53c3c04f816d737f9f4b1d29f3310" [[plugin.deps]] use = "yazi-rs/plugins:smart-enter" -rev = "b9598e6" +rev = "0be29a9" hash = "187cc58ba7ac3befd49c342129e6f1b6" [[plugin.deps]] use = "h-hg/yamb" -rev = "5576bd7" -hash = "2cf0dcda0e16e77342ae50c50134d238" +rev = "971b858" +hash = "da4534745930e827a44d4585bbd965eb" [[plugin.deps]] use = "KKV9/compress" diff --git a/config/yazi/.config/yazi/plugins/git.yazi/main.lua b/config/yazi/.config/yazi/plugins/git.yazi/main.lua index 865e08d..5a519bd 100644 --- a/config/yazi/.config/yazi/plugins/git.yazi/main.lua +++ b/config/yazi/.config/yazi/plugins/git.yazi/main.lua @@ -262,4 +262,14 @@ local function fetch(_, job) return false end -return { setup = setup, fetch = fetch } +-- TODO: remove +local function fetch_compact(self, job) + if ya.throttle then + fetch(self, job) + return require("noop"):fetch(job) + else + return fetch(self, job) + end +end + +return { setup = setup, fetch = fetch_compact } diff --git a/config/yazi/.config/yazi/plugins/yamb.yazi/main.lua b/config/yazi/.config/yazi/plugins/yamb.yazi/main.lua index dc3666e..561725b 100644 --- a/config/yazi/.config/yazi/plugins/yamb.yazi/main.lua +++ b/config/yazi/.config/yazi/plugins/yamb.yazi/main.lua @@ -1,10 +1,13 @@ --- @since 25.6.11 local path_sep = package.config:sub(1, 1) -local get_hovered_path = ya.sync(function(state) +local get_hovered_path = ya.sync(function(_) + local is_virtual = (Url(cx.active.current.hovered.url).spec and Url(cx.active.current.hovered.url).spec.is_virtual) + or (not Url(cx.active.current.hovered.url).spec and Url(cx.active.current.hovered.url).scheme.is_virtual) + local h = cx.active.current.hovered if h then - local path = tostring(h.url) + local path = tostring(is_virtual and h.url or h.url.path) if h.cha.is_dir then return path .. path_sep end @@ -65,7 +68,7 @@ local save_to_file = function(mb_path, bookmarks) end local fzf_find = function(cli, mb_path) - local permit = ya.hide() + local permit = (ui.hide or ya.hide)() local cmd = string.format('%s < "%s"', cli, mb_path) local handle = io.popen(cmd, "r") local result = "" @@ -79,9 +82,37 @@ local fzf_find = function(cli, mb_path) return path end +local generate_key = function(bookmarks) + local keys = get_state_attr("keys") + local key2rank = get_state_attr("key2rank") + local mb = {} + for _, item in pairs(bookmarks) do + if #item.key == 1 and key2rank[item.key] then + table.insert(mb, item.key) + end + end + if #mb == 0 then + return keys[1] + end + table.sort(mb, function(a, b) + return key2rank[a] < key2rank[b] + end) + local idx = 1 + for _, key in ipairs(keys) do + if idx > #mb or key2rank[key] < key2rank[mb[idx]] then + return key + end + idx = idx + 1 + end + return nil +end + local which_find = function(bookmarks) local cands = {} - for path, item in pairs(bookmarks) do + for _, item in pairs(bookmarks) do + if not item.key or item.key == "" then + item.key = generate_key(bookmarks) + end if #item.tag ~= 0 then table.insert(cands, { desc = item.tag, on = item.key, path = item.path }) end @@ -111,7 +142,7 @@ local action_jump = function(bookmarks, path, jump_notify) if string.sub(path, -1) == path_sep then ya.emit("cd", { path, raw = true }) else - ya.emit("reveal", { path, no_dummy = true, raw = true }) + ya.emit("reveal", { path, no_dummy = not fs.cha(Url(path), false), raw = true }) end if jump_notify then ya.notify({ @@ -123,31 +154,6 @@ local action_jump = function(bookmarks, path, jump_notify) end end -local generate_key = function(bookmarks) - local keys = get_state_attr("keys") - local key2rank = get_state_attr("key2rank") - local mb = {} - for _, item in pairs(bookmarks) do - if #item.key == 1 and key2rank[item.key] then - table.insert(mb, item.key) - end - end - if #mb == 0 then - return keys[1] - end - table.sort(mb, function(a, b) - return key2rank[a] < key2rank[b] - end) - local idx = 1 - for _, key in ipairs(keys) do - if idx > #mb or key2rank[key] < key2rank[mb[idx]] then - return key - end - idx = idx + 1 - end - return nil -end - local action_save = function(mb_path, bookmarks, path) if path == nil or #path == 0 then return @@ -208,11 +214,10 @@ local action_save = function(mb_path, bookmarks, path) if event ~= 1 then return end - key = value or "" - if key == "" then - key = "" + if not value or value == "" then break elseif #key == 1 then + key = value -- check the key local key_obj = nil for _, item in pairs(bookmarks) do diff --git a/memo/gpg-pgp.md b/memo/gpg-pgp.md index 17afa94..5335188 100644 --- a/memo/gpg-pgp.md +++ b/memo/gpg-pgp.md @@ -169,7 +169,7 @@ PGP (Pretty Good Privacy) 是一种数据加密和解密的程序, GnuPG (GNU Pr 进入交互式界面后, 使用以下命令: - - `trust`: 选择 `5 = I trust ultimately` 作为信任等级. + - `trust`: 选择信任等级, 例如 `5 = I trust ultimately`. - `save`: 保存并退出. ## Why? @@ -194,7 +194,7 @@ PGP (Pretty Good Privacy) 是一种数据加密和解密的程序, GnuPG (GNU Pr enable-ssh-support ``` - 可选地, 设置缓存有效期 (这对于拥有密码管理系统的桌面来说用处不大): + 可选地, 设置缓存有效期 (这对于拥有密码管理系统的桌面环境来说用处不大): ```plain default-cache-ttl 600 diff --git a/memo/packages.md b/memo/packages.md index 21ae1d3..f0bb3fc 100644 --- a/memo/packages.md +++ b/memo/packages.md @@ -131,6 +131,7 @@ brightnessctl bsd-games btop btrfs-assistant +btrfs-heatmap btrfs-progs busybox cachyos-keyring @@ -144,7 +145,6 @@ cbonsai chafa chaotic-keyring chaotic-mirrorlist -chromium chwd claude-code cli11 @@ -177,6 +177,7 @@ drm-info dwarfs ed efibootmgr +element-desktop elisa ethtool euphonica-git @@ -212,7 +213,7 @@ foot freeze-bin frei0r-plugins fssimu2 -fuzzel +fvm fzf gamemode gdb @@ -232,6 +233,7 @@ gnome-connections gnome-keyring gnome-text-editor go +google-chrome gping gradia gradle @@ -247,13 +249,14 @@ handbrake helix hmcl htop +hunspell-de +hunspell-en_us hwinfo hyperfine hyperv hypridle hyprlock hyprpicker -hyprpolkitagent hyprutils imagemagick inetutils @@ -306,10 +309,9 @@ linux-firmware-intel linux-firmware-other linux-firmware-realtek linux-firmware-whence -linuxqq-nt -llama.cpp-cuda-git +linuxqq-nt-bwrap lldb -llmfit-bin +llmfit localsend lolcat lua-socket @@ -340,13 +342,13 @@ nethogs network-manager-applet networkmanager networkmanager-openvpn -nextcloud-client nfs-utils niri nmap nordvpn-bin noto-fonts-cjk ntfsprogs +nvchecker nvidia-container-toolkit nvidia-open-dkms nvidia-prime @@ -355,16 +357,15 @@ nvidia-utils nvme-cli nvtop nwg-look -oavif-git +oavif obs-studio obsidian okular -omp-bin -opam openbsd-netcat opencl-headers opencl-nvidia opencode +openlist-bin openssh os-prober pacman-contrib @@ -393,13 +394,13 @@ python-chardet python-colorthief python-darkdetect python-fonttools -python-huggingface-hub python-lxml python-opencv-cuda python-pygments python-pyqt6 python-pytest python-pytz +python-secretstorage python-virtualenv python-watchdog python-yaml @@ -429,7 +430,6 @@ qt6-scxml qt6-serialbus qt6ct qtcreator -qtrvsim quickemu quickshell-git rclone @@ -448,6 +448,7 @@ sd seahorse shellcheck-bin sl +slirp4netns slurp snapper solaar @@ -516,6 +517,7 @@ tty-clock turbostat unarchiver unrar +upx usbip uv valgrind @@ -527,6 +529,7 @@ vim visual-studio-code-bin vk-hdr-layer-kwin6-git vlc +voicefox-bin vulkan-extra-layers vulkan-extra-tools vulkan-gfxstream @@ -546,8 +549,10 @@ wezterm wf-recorder-git wget whisper.cpp-model-large-v3-turbo +whois wine winetricks +wireless-regdb wireshark-qt wl-clipboard wl-mirror @@ -603,7 +608,6 @@ zellij zen-browser-bin zenity zig -zig0.15-bin zoxide zram-generator zsh