From 33b541dbd80a55f75222db47d47c4146bd6f54cd Mon Sep 17 00:00:00 2001 From: sudacode Date: Sun, 7 Dec 2025 21:45:34 -0800 Subject: [PATCH 01/17] update --- .config/hypr/keybindings.conf | 2 + .config/rofi/scripts/rofi-docs.sh | 78 +++----- .config/rofi/scripts/rofi-menu-helpers.sh | 68 +++++++ projects/scripts/record-audio.sh | 226 +++++++++++++--------- projects/scripts/screenshot-anki.sh | 171 +++++++++------- 5 files changed, 344 insertions(+), 201 deletions(-) create mode 100644 .config/rofi/scripts/rofi-menu-helpers.sh mode change 100644 => 100755 projects/scripts/record-audio.sh mode change 100644 => 100755 projects/scripts/screenshot-anki.sh diff --git a/.config/hypr/keybindings.conf b/.config/hypr/keybindings.conf index 3f71e56..5342154 100644 --- a/.config/hypr/keybindings.conf +++ b/.config/hypr/keybindings.conf @@ -139,3 +139,5 @@ bind = $mainMod, code:112, submap, reset submap = reset bind = SUPER, l, exec, hyprlock + +bind = $mainMod SHIFT, a, exec, ~/.config/rofi/scripts/rofi-anki-script.sh diff --git a/.config/rofi/scripts/rofi-docs.sh b/.config/rofi/scripts/rofi-docs.sh index b307bd7..589c01c 100755 --- a/.config/rofi/scripts/rofi-docs.sh +++ b/.config/rofi/scripts/rofi-docs.sh @@ -1,9 +1,12 @@ #!/usr/bin/env bash +SCRIPT_DIR="$(cd -- "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +source "$SCRIPT_DIR/rofi-menu-helpers.sh" + BROWSER=/usr/bin/zen-browser -OPTIONS=( - "Arch Linux (btw)" - "Hyprland" +DOC_GROUPS=( + "Arch Linux (btw)|ARCH" + "Hyprland|HYPRLAND" ) ARCH=( "Archlinux Wiki|https://wiki.archlinux.org/title/Main_page" @@ -13,56 +16,37 @@ HYPRLAND=( "Hyprland Window Rules|https://wiki.hypr.land/Configuring/Window-Rules/" ) -get_url() { - urls=("$@") - display_urls=() - declare -A url_map - for url in "${urls[@]}"; do - display_urls+=("${url%%|*}") - label="${url%%|*}" - url_map["$label"]="${url##*|}" - done - display_urls+=("Back") - url_map["Back"]="Back" - - selection="$(printf "%s\n" "${display_urls[@]}" | rofi -theme-str 'window {width: 25%;} listview {columns: 1; lines: 10;}' -theme ~/.config/rofi/launchers/type-2/style-2.rasi -dmenu -l 5 -i -p "Select Documentation")" - url="${url_map[$selection]}" - - if [ -z "$url" ]; then - exit 0 - fi - - printf "%s\n" "$url" +select_group() { + rofi_select_label_value "Select Documentation Group" DOC_GROUPS } -get_docs_list() { - selection="$(printf "%s\n" "${OPTIONS[@]}" | rofi -theme-str 'window {width: 25%;} listview {columns: 1; lines: 10;}' -theme ~/.config/rofi/launchers/type-2/style-2.rasi -dmenu -l 5 -i -p "Select Documentation Group")" - case "$selection" in - "Arch Linux (btw)") - urls=("${ARCH[@]}") - ;; - "Hyprland") - urls=("${HYPRLAND[@]}") - ;; - *) - exit 0 - ;; - esac - - printf "%s\n" "${urls[@]}" +select_url() { + local urls_array="$1" + rofi_select_label_value "Select Documentation" "$urls_array" "Back" } main() { - mapfile -t urls < <(get_docs_list) - url="$(get_url "${urls[@]}")" - if [ -z "$url" ]; then - printf "No URL selected.\n" + while true; do + group_key="$(select_group)" || exit 0 + case "$group_key" in + ARCH) + urls_ref=ARCH + ;; + HYPRLAND) + urls_ref=HYPRLAND + ;; + *) + exit 0 + ;; + esac + + selection="$(select_url "$urls_ref")" || exit 0 + if [[ "$selection" == "Back" ]]; then + continue + fi + $BROWSER "$selection" &>/dev/null & exit 0 - elif [ "$url" == "Back" ]; then - main - exit 0 - fi - $BROWSER "$url" &>/dev/null & + done } main diff --git a/.config/rofi/scripts/rofi-menu-helpers.sh b/.config/rofi/scripts/rofi-menu-helpers.sh new file mode 100644 index 0000000..80c6f5b --- /dev/null +++ b/.config/rofi/scripts/rofi-menu-helpers.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash + +# Lightweight helpers to build rofi menus with label/value pairs. +# Intended to be sourced from other scripts. + +# Allow callers to override theme/args without touching code. +: "${ROFI_THEME:=$HOME/.config/rofi/launchers/type-2/style-2.rasi}" +: "${ROFI_THEME_STR:="window {width: 25%;} listview {columns: 1; lines: 10;}"}" +: "${ROFI_DMENU_ARGS:=-i -l 5}" + +# rofi_menu prompt option... +# Prints the selected option to stdout and propagates the rofi exit code +# (1 when the user cancels). +rofi_menu() { + local prompt="$1" + shift + local -a options=("$@") + + local selection + selection="$(printf "%s\n" "${options[@]}" | rofi -dmenu $ROFI_DMENU_ARGS \ + ${ROFI_THEME:+-theme "$ROFI_THEME"} \ + ${ROFI_THEME_STR:+-theme-str "$ROFI_THEME_STR"} \ + -p "$prompt")" + local status=$? + [[ $status -ne 0 ]] && return "$status" + printf "%s\n" "$selection" +} + +# rofi_select_label_value prompt array_name [back_label] +# array_name should contain entries shaped as "Label|Value". +# Prints the mapped value (or the back label when chosen). Returns 1 on cancel. +rofi_select_label_value() { + local prompt="$1" + local array_name="$2" + local back_label="${3:-}" + + # Access caller's array by name + local -n kv_source="$array_name" + local -A kv_map=() + local -a display=() + + for entry in "${kv_source[@]}"; do + local label="${entry%%|*}" + local value="${entry#*|}" + kv_map["$label"]="$value" + display+=("$label") + done + + if [[ -n "$back_label" ]]; then + kv_map["$back_label"]="$back_label" + display+=("$back_label") + fi + + local selection + selection="$(rofi_menu "$prompt" "${display[@]}")" || return "$?" + [[ -z "$selection" ]] && return 1 + printf "%s\n" "${kv_map[$selection]}" +} + +# rofi_select_list prompt array_name +# Convenience wrapper for plain lists (no label/value mapping). +rofi_select_list() { + local prompt="$1" + local array_name="$2" + local -n list_source="$array_name" + rofi_menu "$prompt" "${list_source[@]}" +} + diff --git a/projects/scripts/record-audio.sh b/projects/scripts/record-audio.sh old mode 100644 new mode 100755 index 30947fc..66898d8 --- a/projects/scripts/record-audio.sh +++ b/projects/scripts/record-audio.sh @@ -1,93 +1,145 @@ -#!/bin/sh +#!/usr/bin/env bash -# Version 1.2 -# shoutout to https://gist.github.com/Cephian/f849e326e3522be9a4386b60b85f2f23 for the original script, -# https://github.com/xythh/ added the ankiConnect functionality -# toggle record computer audio (run once to start, run again to stop) -# dependencies: ffmpeg, pulseaudio, curl +# Toggle desktop audio recording and attach the result to the newest Anki note +# (as tagged by Yomichan). Run once to start recording, run again to stop. +# Dependencies: jq, curl, ffmpeg/ffprobe, pulseaudio (parec+pactl), bc, notify-send -# where recording gets saved, gets deleted after being imported to anki -DIRECTORY="$HOME/.cache/" -FORMAT="mp3" # ogg or mp3 -# cut file since it glitches a bit at the end sometimes -CUT_DURATION="0.1" -#port used by ankiconnect -ankiConnectPort="8765" -# gets the newest created card, so make sure to create the card first with yomichan -newestNoteId=$(curl -s localhost:$ankiConnectPort -X POST -d '{"action": "findNotes", "version": 6, "params": { "query": "is:new"}}' | jq '.result[-1]') -#Audio field name -audioFieldName="SentenceAudio" +set -euo pipefail -#if there is no newest note, you either have a complete empty anki or ankiconnect isn't running -if [ "$newestNoteId" = "" ]; then - notify-send "anki connect not found" - exit 1 -fi +ANKI_CONNECT_PORT="${ANKI_CONNECT_PORT:-8765}" +AUDIO_FIELD_NAME="${AUDIO_FIELD_NAME:-SentenceAudio}" +FORMAT="${FORMAT:-mp3}" # mp3 or ogg +CUT_DURATION="${CUT_DURATION:-0.1}" +CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/record-audio" +RECORD_TIMEOUT="${RECORD_TIMEOUT:-60}" +ANKI_URL="http://localhost:${ANKI_CONNECT_PORT}" -if pgrep -f "parec"; then - pkill -f "parec" -else - time=$(date +%s) - name="$DIRECTORY/$time" - wav_file="$name.wav" - out_file="$name.$FORMAT" - - if ! [ -d "$DIRECTORY" ]; then - mkdir "$DIRECTORY" - fi - notify-send -t 1000 "Audio recording started" - #timeout 1m arecord -t wav -f cd "$wav_file" - - # just grabs last running source... may not always work if your pulseaudio setup is complicated - if ! timeout 1m parec -d"$(pactl list sinks | grep -B1 'State: RUNNING' | sed -nE 's/Sink #(.*)/\1/p' | tail -n 1)" --file-format=wav "$wav_file"; then - - notify-send "Error recording " "most likely no audio playing" - rm "$wav_file" - exit 1 - fi - - input_duration=$(ffprobe -v error -select_streams a:0 -show_entries stream=duration -of default=noprint_wrappers=1:nokey=1 "$wav_file") - output_duration=$(echo "$input_duration"-"$CUT_DURATION" | bc) - - # encode file and delete OG - if [ $FORMAT = "ogg" ]; then - ffmpeg -i "$wav_file" -vn -codec:a libvorbis -b:a 64k -t "$output_duration" "$out_file" - elif [ $FORMAT = "mp3" ]; then - ffmpeg -i "$wav_file" -vn -codec:a libmp3lame -qscale:a 1 -t "$output_duration" "$out_file" - else - notify-send "Record Error" "Unknown format $FORMAT" - fi - rm "$wav_file" - - # Update newest note with recorded audio - curl -s localhost:$ankiConnectPort -X POST -d '{ - - "action": "updateNoteFields", - "version": 6, - "params": { - "note": { - "id": '"$newestNoteId"', - "fields": { - "'$audioFieldName'": "" - }, - "audio": [{ - "path": "'"$out_file"'", - "filename": "'"$time"'.'$FORMAT'", - "fields": [ - "'$audioFieldName'" - ] - }] +require_cmd() { + command -v "$1" >/dev/null 2>&1 || { + echo "Missing dependency: $1" >&2 + exit 1 } } -}' - # opens changed note, comment if you don't want it. - curl -s localhost:$ankiConnectPort -X POST -d '{ - "action": "guiBrowse", - "version": 6, - "params": { - "query": "nid:'"$newestNoteId"'" - } -}' - notify-send -t 1000 "Audio recording copied" - rm "$out_file" -fi + +notify() { + # Best-effort notification; keep script running if notify-send is missing. + if command -v notify-send >/dev/null 2>&1; then + notify-send -t 1000 "$@" + fi +} + +get_active_sink() { + pactl list sinks short 2>/dev/null | awk '$6=="RUNNING"{print $1; exit 0}' +} + +get_newest_note_id() { + local response + response=$(curl -sS "$ANKI_URL" -X POST -H 'Content-Type: application/json' \ + -d '{"action":"findNotes","version":6,"params":{"query":"is:new"}}') + jq -r '.result[-1] // empty' <<<"$response" +} + +update_anki_note() { + local note_id="$1" audio_path="$2" filename="$3" + + local payload + payload=$(jq -n --argjson noteId "$note_id" --arg field "$AUDIO_FIELD_NAME" \ + --arg path "$audio_path" --arg filename "$filename" ' + {action:"updateNoteFields",version:6, + params:{note:{id:$noteId,fields:{($field):""}, + audio:[{path:$path,filename:$filename,fields:[$field]}]}}}') + + curl -sS "$ANKI_URL" -X POST -H 'Content-Type: application/json' -d "$payload" >/dev/null +} + +open_note_in_browser() { + local note_id="$1" + local payload + payload=$(jq -n --argjson noteId "$note_id" ' + {action:"guiBrowse",version:6,params:{query:("nid:" + ($noteId|tostring))}}') + curl -sS "$ANKI_URL" -X POST -H 'Content-Type: application/json' -d "$payload" >/dev/null +} + +record_audio() { + local note_id="$1" + local sink + sink=$(get_active_sink) || true + + if [[ -z "$sink" ]]; then + notify "Record Error" "No running PulseAudio sink found" + exit 1 + fi + + mkdir -p "$CACHE_DIR" + + local timestamp wav_file out_file + timestamp=$(date +%s) + wav_file="$CACHE_DIR/$timestamp.wav" + out_file="$CACHE_DIR/$timestamp.$FORMAT" + + notify "Audio recording started" + + if ! timeout "$RECORD_TIMEOUT" parec -d"$sink" --file-format=wav "$wav_file"; then + notify "Record Error" "No audio captured (timeout or sink issue)" + rm -f "$wav_file" + exit 1 + fi + + local input_duration output_duration + input_duration=$(ffprobe -v error -select_streams a:0 \ + -show_entries stream=duration -of default=noprint_wrappers=1:nokey=1 "$wav_file") + output_duration=$(echo "$input_duration - $CUT_DURATION" | bc -l) + + # Guard against negative durations + if [[ $(echo "$output_duration < 0" | bc -l) -eq 1 ]]; then + output_duration="0" + fi + + case "$FORMAT" in + ogg) + ffmpeg -nostdin -y -i "$wav_file" -vn -codec:a libvorbis -b:a 64k \ + -t "$output_duration" "$out_file" + ;; + mp3) + ffmpeg -nostdin -y -i "$wav_file" -vn -codec:a libmp3lame -qscale:a 1 \ + -t "$output_duration" "$out_file" + ;; + *) + notify "Record Error" "Unknown format: $FORMAT" + rm -f "$wav_file" + exit 1 + ;; + esac + + rm -f "$wav_file" + + update_anki_note "$note_id" "$out_file" "$timestamp.$FORMAT" + open_note_in_browser "$note_id" + + notify "Audio recording copied" + rm -f "$out_file" +} + +main() { + for cmd in curl jq ffmpeg ffprobe parec pactl bc; do + require_cmd "$cmd" + done + + if pgrep -x parec >/dev/null 2>&1; then + pkill -x parec + notify "Audio recording stopped" + exit 0 + fi + + local newest_note + newest_note=$(get_newest_note_id) + + if [[ -z "$newest_note" ]]; then + notify "Anki Connect" "No new notes found or AnkiConnect unavailable" + exit 1 + fi + + record_audio "$newest_note" +} + +main "$@" diff --git a/projects/scripts/screenshot-anki.sh b/projects/scripts/screenshot-anki.sh old mode 100644 new mode 100755 index 464bbc0..87a7212 --- a/projects/scripts/screenshot-anki.sh +++ b/projects/scripts/screenshot-anki.sh @@ -1,75 +1,112 @@ -#!/bin/sh +#!/usr/bin/env bash -# Version 1.2 -# click and drag to screenshot dragged portion -# click on specific window to screenshot window area -# dependencies: imagemagick, xclip,curl maybe xdotool (see comment below) -# shoutout to https://gist.github.com/Cephian/f849e326e3522be9a4386b60b85f2f23 for the original script, -# https://github.com/xythh/ added the ankiConnect functionality -# if anki is running the image is added to your latest note as a jpg, if anki is not running it's added to your clipboard as a png -time=$(date +%s) -tmp_file="$HOME/.cache/$time" -ankiConnectPort="8765" -pictureField="Picture" -quality="90" +# Capture a region with slurp+grim. If AnkiConnect is available, attach the +# JPEG to the newest note; otherwise copy a PNG to the clipboard. -# This gets your notes marked as new and returns the newest one. -newestNoteId=$(curl -s localhost:$ankiConnectPort -X POST -d '{"action": "findNotes", "version": 6, "params": { "query": "is:new"}}' | jq '.result[-1]') +set -euo pipefail -# you can remove these two lines if you don't have software which -# makes your mouse disappear when you use the keyboard (e.g. xbanish, unclutter) -# https://github.com/ImageMagick/ImageMagick/issues/1745#issuecomment-777747494 -xdotool mousemove_relative 1 1 -xdotool mousemove_relative -- -1 -1 +ANKI_CONNECT_PORT="${ANKI_CONNECT_PORT:-8765}" +PICTURE_FIELD="${PICTURE_FIELD:-Picture}" +QUALITY="${QUALITY:-90}" +CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/screenshot-anki" +ANKI_URL="http://localhost:${ANKI_CONNECT_PORT}" +REQUIREMENTS=(slurp grim wl-copy xdotool curl jq) -# if anki connect is running it will return your latest note id, and the following code will run, if anki connect is not running nothing is return. -if [ "$newestNoteId" != "" ]; then - if ! import -quality $quality "$tmp_file.jpg"; then - # most likley reason this returns a error, is for fullscreen applications that take full control which does not allowing imagemagick to select the area, use windowed fullscreen or if running wine use a virtual desktop to avoid this. - notify-send "Error screenshoting " "most likely unable to find selection" - exit 1 - fi +notify() { + if command -v notify-send >/dev/null 2>&1; then + notify-send "$@" + fi +} - curl -s localhost:$ankiConnectPort -X POST -d '{ - "action": "updateNoteFields", - "version": 6, - "params": { - "note": { - "id": '"$newestNoteId"', - "fields": { - "'$pictureField'": "" - }, - "picture": [{ - "path": "'"$tmp_file"'.jpg", - "filename": "paste-'"$time"'.jpg", - "fields": [ - "'$pictureField'" - ] - }] - } +require_cmd() { + command -v "$1" >/dev/null 2>&1 || { + notify "Missing dependency" "$1 is required" + exit 1 } -}' +} - #remove if you don't want anki to show you the card you just edited - curl -s localhost:$ankiConnectPort -X POST -d '{ - "action": "guiBrowse", - "version": 6, - "params": { - "query": "nid:'"$newestNoteId"'" - } -}' +wiggle_mouse() { + # Avoid disappearing cursor on some compositors + xdotool mousemove_relative 1 1 + xdotool mousemove_relative -- -1 -1 +} - #you can comment this if you do not use notifcations. - notify-send "Screenshot Taken" "Added to note" - rm "$tmp_file.jpg" -else - if ! import -quality $quality "$tmp_file.png"; then - notify-send "Error screenshoting " "most likely unable to find selection" - exit 1 - fi - # we use pngs when copying to clipboard because they have greater support when pasting. - xclip -selection clipboard -target image/png -i "$tmp_file.png" - rm "$tmp_file.png" - #you can comment this if you do not use notifcations. - notify-send "Screenshot Taken" "Copied to clipboard" -fi +capture_region() { + local fmt="$1" quality="$2" output="$3" + local geometry + geometry=$(slurp) + if [[ -z "$geometry" ]]; then + notify "Screenshot cancelled" "No region selected" + exit 1 + fi + if [[ "$fmt" == "jpeg" ]]; then + grim -g "$geometry" -t jpeg -q "$quality" "$output" + else + grim -g "$geometry" -t png "$output" + fi +} + +copy_to_clipboard() { + local file="$1" + if ! wl-copy <"$file"; then + notify "Error copying screenshot" "wl-copy failed" + exit 1 + fi +} + +get_newest_note_id() { + local response + response=$(curl -sS "$ANKI_URL" -X POST -H 'Content-Type: application/json' \ + -d '{"action":"findNotes","version":6,"params":{"query":"is:new"}}') + jq -r '.result[-1] // empty' <<<"$response" +} + +update_note_with_image() { + local note_id="$1" image_path="$2" filename="$3" + local payload + payload=$(jq -n --argjson noteId "$note_id" --arg field "$PICTURE_FIELD" \ + --arg path "$image_path" --arg filename "$filename" ' + {action:"updateNoteFields",version:6, + params:{note:{id:$noteId,fields:{($field):""}, + picture:[{path:$path,filename:$filename,fields:[$field]}]}}}') + curl -sS "$ANKI_URL" -X POST -H 'Content-Type: application/json' -d "$payload" >/dev/null +} + +open_note_in_browser() { + local note_id="$1" + local payload + payload=$(jq -n --argjson noteId "$note_id" ' + {action:"guiBrowse",version:6,params:{query:("nid:" + ($noteId|tostring))}}') + curl -sS "$ANKI_URL" -X POST -H 'Content-Type: application/json' -d "$payload" >/dev/null +} + +main() { + for cmd in "${REQUIREMENTS[@]}"; do + require_cmd "$cmd" + done + + mkdir -p "$CACHE_DIR" + local timestamp base newest_note image_path + timestamp=$(date +%s) + base="$CACHE_DIR/$timestamp" + + wiggle_mouse + newest_note=$(get_newest_note_id) + + if [[ -n "$newest_note" ]]; then + image_path="$base.jpg" + capture_region "jpeg" "$QUALITY" "$image_path" + update_note_with_image "$newest_note" "$image_path" "paste-$timestamp.jpg" + open_note_in_browser "$newest_note" + notify -i "$image_path" "Screenshot Taken" "Added to Anki note" + rm -f "$image_path" + else + image_path="$base.png" + capture_region "png" "" "$image_path" + copy_to_clipboard "$image_path" + notify -i "$image_path" "Screenshot Taken" "Copied to clipboard" + rm -f "$image_path" + fi +} + +main "$@" From baed6a3196c00b47e28a73d2afa1fe16b7721964 Mon Sep 17 00:00:00 2001 From: sudacode Date: Sun, 7 Dec 2025 23:46:20 -0800 Subject: [PATCH 02/17] add rofi anki script --- .config/rofi/scripts/rofi-anki-script.sh | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100755 .config/rofi/scripts/rofi-anki-script.sh diff --git a/.config/rofi/scripts/rofi-anki-script.sh b/.config/rofi/scripts/rofi-anki-script.sh new file mode 100755 index 0000000..1c8767f --- /dev/null +++ b/.config/rofi/scripts/rofi-anki-script.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +CHOICES=( + "1. Screenshot (Lapis)" + "2. Screenshot (Luna)" + "3. Record Audio" +) +CHOICE=$(printf "%s\n" "${CHOICES[@]}" | rofi -dmenu -i -theme "$HOME/.config/rofi/launchers/type-2/style-2.rasi" -theme-str 'window {width: 25%;} listview {columns: 1; lines: 5;}' -p "Select an option") + +case "$CHOICE" in +"1. Screenshot (Lapis)") + PICTURE_FIELD=Picture "$HOME/projects/scripts/screenshot-anki.sh" + ;; +"2. Screenshot (Luna)") + PICTURE_FIELD=screenshot "$HOME/projects/scripts/screenshot-anki.sh" + ;; +"3. Record Audio") + "$HOME/projects/scripts/record-audio.sh" + ;; +*) + exit 1 + ;; +esac + From ff8a2a982bed1d6c7fd5bf24722f2da8b07c14be Mon Sep 17 00:00:00 2001 From: sudacode Date: Mon, 8 Dec 2025 23:38:11 -0800 Subject: [PATCH 03/17] update --- .config/rofi/scripts/rofi-wallpaper.sh | 5 +- projects/scripts/screenshot-anki.sh | 86 ++++++++++++++++++++++++-- projects/scripts/songinfo.sh | 10 +++ 3 files changed, 95 insertions(+), 6 deletions(-) create mode 100755 projects/scripts/songinfo.sh diff --git a/.config/rofi/scripts/rofi-wallpaper.sh b/.config/rofi/scripts/rofi-wallpaper.sh index 0993f05..29b40ed 100755 --- a/.config/rofi/scripts/rofi-wallpaper.sh +++ b/.config/rofi/scripts/rofi-wallpaper.sh @@ -6,8 +6,9 @@ THEME="$HOME/.config/rofi/launchers/type-3/style-4.rasi" DIR="$HOME/Pictures/wallpapers/favorites" SELECTED_WALL=$(cd "$DIR" && for a in *.jpg *.png; do echo -en "$a\0icon\x1f$a\n"; done | rofi -dmenu -i -no-custom -theme "$THEME" -p "Select a wallpaper" -theme-str 'configuration {icon-size: 128; dpi: 96;} window {width: 45%; height: 45%;}') PTH="$(printf "%s" "$DIR/$SELECTED_WALL" | tr -s '/')" -notify-send -a "rofi-wallpaper" "Wallpaper set to" -i "$PTH" "$PTH" hyprctl hyprpaper preload "$PTH" -hyprctl hyprpaper wallpaper "DP-1,$PTH" +hyprctl hyprpaper wallpaper "$PTH" hyprctl hyprpaper unload "$(cat "$HOME/.wallpaper")" +# hyprctl hyprpaper wallpaper "DP-1, $PTH" +notify-send -a "rofi-wallpaper" "Wallpaper set to" -i "$PTH" "$PTH" echo "$PTH" >"$HOME/.wallpaper" diff --git a/projects/scripts/screenshot-anki.sh b/projects/scripts/screenshot-anki.sh index 87a7212..b912f6d 100755 --- a/projects/scripts/screenshot-anki.sh +++ b/projects/scripts/screenshot-anki.sh @@ -10,7 +10,12 @@ PICTURE_FIELD="${PICTURE_FIELD:-Picture}" QUALITY="${QUALITY:-90}" CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/screenshot-anki" ANKI_URL="http://localhost:${ANKI_CONNECT_PORT}" -REQUIREMENTS=(slurp grim wl-copy xdotool curl jq) +HOSTNAME_SHORT="$(hostname -s 2>/dev/null || hostname)" +HYPRLAND_GEOMETRY_FILTER='"\\(.at[0]),\\(.at[1]) \\(.size[0])x\\(.size[1])"' +REQUIREMENTS=(slurp grim wl-copy xdotool curl jq rofi) +ROFI_THEME_STR='listview {columns: 2; lines: 3;} window {width: 45%;}' +ROFI_THEME="$HOME/.config/rofi/launchers/type-2/style-2.rasi" +CAPTURE_MODE="" notify() { if command -v notify-send >/dev/null 2>&1; then @@ -31,6 +36,13 @@ wiggle_mouse() { xdotool mousemove_relative -- -1 -1 } +drain_enter_key() { + # Release lingering Enter press from launching via rofi so it + # doesn't reach the next focused window (e.g., a game). + xdotool keyup Return 2>/dev/null || true + xdotool keyup KP_Enter 2>/dev/null || true +} + capture_region() { local fmt="$1" quality="$2" output="$3" local geometry @@ -46,6 +58,59 @@ capture_region() { fi } +capture_current_window() { + local fmt="$1" quality="$2" output="$3" geometry + + if [[ "$HOSTNAME_SHORT" != "luna" && "$HOSTNAME_SHORT" != "lapis" ]]; then + notify "Window capture unavailable" "Falling back to region selection" + capture_region "$fmt" "$quality" "$output" + return + fi + + geometry=$(hyprctl -j activewindow 2>/dev/null | jq -r "$HYPRLAND_GEOMETRY_FILTER" 2>/dev/null || true) + if [[ -z "$geometry" || "$geometry" == "null" ]]; then + notify "Window capture failed" "Falling back to region selection" + capture_region "$fmt" "$quality" "$output" + return + fi + + if [[ "$fmt" == "jpeg" ]]; then + grim -g "$geometry" -t jpeg -q "$quality" "$output" + else + grim -g "$geometry" -t png "$output" + fi +} + +choose_capture_mode() { + local selection + selection=$(printf "%s\n%s\n" "Region (slurp)" "Current window (Hyprland)" | + rofi -dmenu -i \ + -p "Capture mode" \ + -mesg "Select capture target" \ + -no-custom \ + -no-lazy-grab \ + -location 0 -yoffset 30 -xoffset 30 \ + -theme "$ROFI_THEME" \ + -theme-str "$ROFI_THEME_STR" \ + -window-title "screenshot-anki") + + if [[ -z "$selection" ]]; then + notify "Screenshot cancelled" "No capture mode selected" + exit 0 + fi + + if [[ "$selection" == "Current window (Hyprland)" ]]; then + if [[ "$HOSTNAME_SHORT" != "luna" && "$HOSTNAME_SHORT" != "lapis" ]]; then + notify "Window capture unavailable" "Using region instead (host: $HOSTNAME_SHORT)" + CAPTURE_MODE="region" + else + CAPTURE_MODE="window" + fi + else + CAPTURE_MODE="region" + fi +} + copy_to_clipboard() { local file="$1" if ! wl-copy <"$file"; then @@ -81,7 +146,8 @@ open_note_in_browser() { } main() { - for cmd in "${REQUIREMENTS[@]}"; do + local requirements=("${REQUIREMENTS[@]}") + for cmd in "${requirements[@]}"; do require_cmd "$cmd" done @@ -90,19 +156,31 @@ main() { timestamp=$(date +%s) base="$CACHE_DIR/$timestamp" + drain_enter_key + choose_capture_mode + + if [[ "$CAPTURE_MODE" == "window" ]]; then + require_cmd hyprctl + fi + wiggle_mouse newest_note=$(get_newest_note_id) + local capture_fn="capture_region" + if [[ "$CAPTURE_MODE" == "window" ]]; then + capture_fn="capture_current_window" + fi + if [[ -n "$newest_note" ]]; then image_path="$base.jpg" - capture_region "jpeg" "$QUALITY" "$image_path" + "$capture_fn" "jpeg" "$QUALITY" "$image_path" update_note_with_image "$newest_note" "$image_path" "paste-$timestamp.jpg" open_note_in_browser "$newest_note" notify -i "$image_path" "Screenshot Taken" "Added to Anki note" rm -f "$image_path" else image_path="$base.png" - capture_region "png" "" "$image_path" + "$capture_fn" "png" "" "$image_path" copy_to_clipboard "$image_path" notify -i "$image_path" "Screenshot Taken" "Copied to clipboard" rm -f "$image_path" diff --git a/projects/scripts/songinfo.sh b/projects/scripts/songinfo.sh new file mode 100755 index 0000000..7239765 --- /dev/null +++ b/projects/scripts/songinfo.sh @@ -0,0 +1,10 @@ +#!/bin/sh + +music_dir="/jellyfin/music" +previewdir="$XDG_CONFIG_HOME/ncmpcpp/previews" +filename="$(mpc --format "$music_dir"/%file% current)" +previewname="$previewdir/$(mpc --format %album% current | base64).png" + +[ -e "$previewname" ] || ffmpeg -y -i "$filename" -an -vf scale=128:128 "$previewname" > /dev/null 2>&1 + +notify-send -a "ncmpcpp" "Now Playing" "$(mpc --format '%title% \n%artist% - %album%' current)" -i "$previewname" From 4ddcf12d5835749a60607f9e1d395d6be8dbc495 Mon Sep 17 00:00:00 2001 From: sudacode Date: Wed, 10 Dec 2025 00:41:15 -0800 Subject: [PATCH 04/17] update screenshot scripts --- .config/hypr/keybindings.conf | 3 +- projects/scripts/screenshot-anki.sh | 78 ++++++++------ projects/scripts/screenshot.sh | 154 ++++++++++++++-------------- 3 files changed, 124 insertions(+), 111 deletions(-) diff --git a/.config/hypr/keybindings.conf b/.config/hypr/keybindings.conf index 5342154..e71dfd0 100644 --- a/.config/hypr/keybindings.conf +++ b/.config/hypr/keybindings.conf @@ -140,4 +140,5 @@ submap = reset bind = SUPER, l, exec, hyprlock -bind = $mainMod SHIFT, a, exec, ~/.config/rofi/scripts/rofi-anki-script.sh +bind = $mainMod, a, exec, ~/.config/rofi/scripts/rofi-anki-script.sh +bind = $mainMod SHIFT, a, exec, ~/projects/scripts/screenshot-anki.sh -cdMinecraft diff --git a/projects/scripts/screenshot-anki.sh b/projects/scripts/screenshot-anki.sh index b912f6d..2766e9a 100755 --- a/projects/scripts/screenshot-anki.sh +++ b/projects/scripts/screenshot-anki.sh @@ -11,11 +11,32 @@ QUALITY="${QUALITY:-90}" CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/screenshot-anki" ANKI_URL="http://localhost:${ANKI_CONNECT_PORT}" HOSTNAME_SHORT="$(hostname -s 2>/dev/null || hostname)" -HYPRLAND_GEOMETRY_FILTER='"\\(.at[0]),\\(.at[1]) \\(.size[0])x\\(.size[1])"' REQUIREMENTS=(slurp grim wl-copy xdotool curl jq rofi) ROFI_THEME_STR='listview {columns: 2; lines: 3;} window {width: 45%;}' ROFI_THEME="$HOME/.config/rofi/launchers/type-2/style-2.rasi" CAPTURE_MODE="" +DECK_NAME="" +AUTO_MODE=false + +parse_opts() { + while getopts "cd:" opt; do + case "$opt" in + c) + CAPTURE_MODE="window" + AUTO_MODE=true + ;; + d) + DECK_NAME="$OPTARG" + ;; + *) + echo "Usage: $0 [-c] [-n DECK_NAME]" >&2 + echo " -c: Capture current window" >&2 + echo " -n: Specify note name (e.g., Lapis)" >&2 + exit 1 + ;; + esac + done +} notify() { if command -v notify-send >/dev/null 2>&1; then @@ -61,23 +82,10 @@ capture_region() { capture_current_window() { local fmt="$1" quality="$2" output="$3" geometry - if [[ "$HOSTNAME_SHORT" != "luna" && "$HOSTNAME_SHORT" != "lapis" ]]; then - notify "Window capture unavailable" "Falling back to region selection" - capture_region "$fmt" "$quality" "$output" - return - fi - - geometry=$(hyprctl -j activewindow 2>/dev/null | jq -r "$HYPRLAND_GEOMETRY_FILTER" 2>/dev/null || true) - if [[ -z "$geometry" || "$geometry" == "null" ]]; then - notify "Window capture failed" "Falling back to region selection" - capture_region "$fmt" "$quality" "$output" - return - fi - if [[ "$fmt" == "jpeg" ]]; then - grim -g "$geometry" -t jpeg -q "$quality" "$output" + grim -w "$(hyprctl activewindow -j | jq -r '.address')" -t jpeg -q "$quality" "$output" else - grim -g "$geometry" -t png "$output" + grim -w "$(hyprctl activewindow -j | jq -r '.address')" -t png "$output" fi } @@ -85,14 +93,14 @@ choose_capture_mode() { local selection selection=$(printf "%s\n%s\n" "Region (slurp)" "Current window (Hyprland)" | rofi -dmenu -i \ - -p "Capture mode" \ - -mesg "Select capture target" \ - -no-custom \ - -no-lazy-grab \ - -location 0 -yoffset 30 -xoffset 30 \ - -theme "$ROFI_THEME" \ - -theme-str "$ROFI_THEME_STR" \ - -window-title "screenshot-anki") + -p "Capture mode" \ + -mesg "Select capture target" \ + -no-custom \ + -no-lazy-grab \ + -location 0 -yoffset 30 -xoffset 30 \ + -theme "$ROFI_THEME" \ + -theme-str "$ROFI_THEME_STR" \ + -window-title "screenshot-anki") if [[ -z "$selection" ]]; then notify "Screenshot cancelled" "No capture mode selected" @@ -100,12 +108,7 @@ choose_capture_mode() { fi if [[ "$selection" == "Current window (Hyprland)" ]]; then - if [[ "$HOSTNAME_SHORT" != "luna" && "$HOSTNAME_SHORT" != "lapis" ]]; then - notify "Window capture unavailable" "Using region instead (host: $HOSTNAME_SHORT)" - CAPTURE_MODE="region" - else - CAPTURE_MODE="window" - fi + CAPTURE_MODE="window" else CAPTURE_MODE="region" fi @@ -120,9 +123,12 @@ copy_to_clipboard() { } get_newest_note_id() { - local response + local response query="is:new" + if [[ -n "$DECK_NAME" ]]; then + query="is:new deck:$DECK_NAME" + fi response=$(curl -sS "$ANKI_URL" -X POST -H 'Content-Type: application/json' \ - -d '{"action":"findNotes","version":6,"params":{"query":"is:new"}}') + -d "{\"action\":\"findNotes\",\"version\":6,\"params\":{\"query\":\"$query\"}}") jq -r '.result[-1] // empty' <<<"$response" } @@ -146,6 +152,8 @@ open_note_in_browser() { } main() { + parse_opts "$@" + local requirements=("${REQUIREMENTS[@]}") for cmd in "${requirements[@]}"; do require_cmd "$cmd" @@ -157,7 +165,11 @@ main() { base="$CACHE_DIR/$timestamp" drain_enter_key - choose_capture_mode + + # Only show interactive menu if not in auto mode + if [[ "$AUTO_MODE" == false ]]; then + choose_capture_mode + fi if [[ "$CAPTURE_MODE" == "window" ]]; then require_cmd hyprctl diff --git a/projects/scripts/screenshot.sh b/projects/scripts/screenshot.sh index 939684c..7220740 100755 --- a/projects/scripts/screenshot.sh +++ b/projects/scripts/screenshot.sh @@ -10,99 +10,99 @@ HYPRLAND_REGEX='.at[0],(.at[1]) .size[0]x(.size[1])' REQUIREMENTS=(grim slurp rofi zenity wl-copy) USE_NOTIFICATIONS=1 CHOICES=( - "1. Select a region and save - slurp | grim -g - \"$TMP_SCREENSHOT\"" - "2. Select a region and copy to clipboard - slurp | grim -g - - | wl-copy" - "3. Whole screen - grim \"$TMP_SCREENSHOT\"" - "4. Current window - hyprctl -j activewindow | jq -r \"${HYPRLAND_REGEX}\" | grim -g - \"$TMP_SCREENSHOT\"" - "5. Edit - slurp | grim -g - - | swappy -f -" - "6. Quit - exit 0" + "1. Select a region and save - slurp | grim -g - \"$TMP_SCREENSHOT\"" + "2. Select a region and copy to clipboard - slurp | grim -g - - | wl-copy" + "3. Whole screen - grim \"$TMP_SCREENSHOT\"" + "4. Current window - hyprctl -j activewindow | jq -r \"${HYPRLAND_REGEX}\" | grim -g - \"$TMP_SCREENSHOT\"" + "5. Edit - slurp | grim -g - - | swappy -f -" + "6. Quit - exit 0" ) notify() { - local body="$1" - local title="$2" - if [[ -z "$body" ]]; then - echo "notify: No message provided" - return 1 - fi - if [[ -z "$title" ]]; then - title="$SCRIPT_NAME" - fi + local body="$1" + local title="$2" + if [[ -z "$body" ]]; then + echo "notify: No message provided" + return 1 + fi + if [[ -z "$title" ]]; then + title="$SCRIPT_NAME" + fi - if ((USE_NOTIFICATIONS)); then - notify-send "$title" "$body" - else - printf "%s\n%s\n" "$title" "$body" - fi - return 0 + if ((USE_NOTIFICATIONS)); then + notify-send "$title" "$body" + else + printf "%s\n%s\n" "$title" "$body" + fi + return 0 } check_deps() { - for cmd in "${REQUIREMENTS[@]}"; do - if ! command -v "$cmd" &> /dev/null; then - echo "Error: $cmd is not installed. Please install it first." - exit 1 - fi - done + for cmd in "${REQUIREMENTS[@]}"; do + if ! command -v "$cmd" &>/dev/null; then + echo "Error: $cmd is not installed. Please install it first." + exit 1 + fi + done } main() { - CHOICE="$(rofi -dmenu -i -p "Enter option or select from the list" \ - -mesg "Select a Screenshot Option" \ - -a 0 -no-custom -location 0 \ - -yoffset 30 -xoffset 30 \ - -theme-str 'listview {columns: 2; lines: 3;} window {width: 45%;}' \ - -window-title "$SCRIPT_NAME" \ - -format 'i' \ - <<< "$(printf "%s\n" "${CHOICES[@]%% - *}")")" + CHOICE="$(rofi -dmenu -i -p "Enter option or select from the list" \ + -mesg "Select a Screenshot Option" \ + -a 0 -no-custom -location 0 \ + -yoffset 30 -xoffset 30 \ + -theme-str 'listview {columns: 2; lines: 3;} window {width: 45%;}' \ + -window-title "$SCRIPT_NAME" \ + -format 'i' \ + <<<"$(printf "%s\n" "${CHOICES[@]%% - *}")")" - if [[ -z "$CHOICE" ]]; then - notify "No option selected." "" - exit 0 - fi + if [[ -z "$CHOICE" ]]; then + notify "No option selected." "" + exit 0 + fi - sleep 0.2 # give time for the rofi window to close - CMD="${CHOICES[$CHOICE]#* -}" - if [[ -z "$CMD" ]]; then - notify "No option selected." "" - exit 0 - fi + sleep 0.2 # give time for the rofi window to close + CMD="${CHOICES[$CHOICE]#* -}" + if [[ -z "$CMD" ]]; then + notify "No option selected." "" + exit 0 + fi - # For option 2 (copy to clipboard), handle differently - if [[ "$CHOICE" == "1" ]]; then - if eval "$CMD"; then - notify "Screenshot copied to clipboard" - exit 0 - else - notify "An error occurred while taking the screenshot." - exit 1 - fi - fi + # For option 2 (copy to clipboard), handle differently + if [[ "$CHOICE" == "1" ]]; then + if eval "$CMD"; then + notify "Screenshot copied to clipboard" + exit 0 + else + notify "An error occurred while taking the screenshot." + exit 1 + fi + fi - if ! eval "$CMD"; then - notify "An error occurred while taking the screenshot." - exit 1 - fi + if ! eval "$CMD"; then + notify "An error occurred while taking the screenshot." + exit 1 + fi - notify "screenshot.sh" "Screenshot saved temporarily.\nChoose where to save it permanently" + notify "screenshot.sh" "Screenshot saved temporarily.\nChoose where to save it permanently" - FILE=$(zenity --file-selection --title="Save Screenshot" --filename="$DEFAULT_FILENAME" --save 2> /dev/null) - case "$?" in - 0) - if mv "$TMP_SCREENSHOT" "$FILE"; then - notify "Screenshot saved to $FILE" - else - notify "Failed to save screenshot to $FILE" - fi - ;; - 1) - rm -f "$TMP_SCREENSHOT" - notify "Screenshot discarded" - ;; - -1) - notify "An unexpected error has occurred." - ;; - esac + FILE=$(zenity --file-selection --title="Save Screenshot" --filename="$DEFAULT_FILENAME" --save 2>/dev/null) + case "$?" in + 0) + if mv "$TMP_SCREENSHOT" "$FILE"; then + notify "Screenshot saved to $FILE" + else + notify "Failed to save screenshot to $FILE" + fi + ;; + 1) + rm -f "$TMP_SCREENSHOT" + notify "Screenshot discarded" + ;; + -1) + notify "An unexpected error has occurred." + ;; + esac } check_deps From cdedc4ced28c975e677d0fc290c21cdaad4b1436 Mon Sep 17 00:00:00 2001 From: sudacode Date: Thu, 11 Dec 2025 10:50:53 -0800 Subject: [PATCH 05/17] update --- .config/rofi/scripts/rofi-wallpaper.sh | 5 +---- .config/uwsm/env | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.config/rofi/scripts/rofi-wallpaper.sh b/.config/rofi/scripts/rofi-wallpaper.sh index 29b40ed..0e81e44 100755 --- a/.config/rofi/scripts/rofi-wallpaper.sh +++ b/.config/rofi/scripts/rofi-wallpaper.sh @@ -6,9 +6,6 @@ THEME="$HOME/.config/rofi/launchers/type-3/style-4.rasi" DIR="$HOME/Pictures/wallpapers/favorites" SELECTED_WALL=$(cd "$DIR" && for a in *.jpg *.png; do echo -en "$a\0icon\x1f$a\n"; done | rofi -dmenu -i -no-custom -theme "$THEME" -p "Select a wallpaper" -theme-str 'configuration {icon-size: 128; dpi: 96;} window {width: 45%; height: 45%;}') PTH="$(printf "%s" "$DIR/$SELECTED_WALL" | tr -s '/')" -hyprctl hyprpaper preload "$PTH" -hyprctl hyprpaper wallpaper "$PTH" -hyprctl hyprpaper unload "$(cat "$HOME/.wallpaper")" -# hyprctl hyprpaper wallpaper "DP-1, $PTH" +hyprctl hyprpaper wallpaper "DP-1, $PTH" notify-send -a "rofi-wallpaper" "Wallpaper set to" -i "$PTH" "$PTH" echo "$PTH" >"$HOME/.wallpaper" diff --git a/.config/uwsm/env b/.config/uwsm/env index a868364..0446a31 100644 --- a/.config/uwsm/env +++ b/.config/uwsm/env @@ -15,6 +15,7 @@ export QT_QPA_PLATFORMTHEME=qt5ct export GTK_THEME=Dracula export XDG_CONFIG_HOME=$HOME/.config export COMPOSE_BAKE=true +export ANKI_WAYLAND=1 # nvidia export NVD_BACKEND=direct From 332c7f683241801cc9e48d87470864c706e18495 Mon Sep 17 00:00:00 2001 From: sudacode Date: Sun, 14 Dec 2025 12:10:53 -0800 Subject: [PATCH 06/17] update to new version --- .config/nvim/lua/plugins/codecompanion.lua | 38 ++++++++++++++++++- .../codecompanion/fidget-spinner-notify.lua | 2 +- .../utils/codecompanion/fidget-spinner.lua | 4 +- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/.config/nvim/lua/plugins/codecompanion.lua b/.config/nvim/lua/plugins/codecompanion.lua index 3c555ca..a040394 100644 --- a/.config/nvim/lua/plugins/codecompanion.lua +++ b/.config/nvim/lua/plugins/codecompanion.lua @@ -131,7 +131,7 @@ return { }, -- }}} }, - strategies = { + interactions = { chat = { adapter = "copilot", -- adapter = "openrouter", @@ -182,11 +182,21 @@ return { end, completion_provider = "cmp", }, + fold_reasoning = true, + show_reasoning = true, }, inline = { adapter = "copilot", -- adapter = "openrouter", }, + cmd = { + adapter = "copilot", + }, + background = { + adapter = { + name = "copilot", + }, + }, }, display = { action_palette = { @@ -232,7 +242,6 @@ return { -- Options for inline diff provider inline = { layout = "buffer", -- float|buffer - Where to display the diff - diff_signs = { signs = { text = "▌", -- Sign text for normal changes @@ -307,6 +316,31 @@ return { }, }, }, + rules = { + default = { + description = "Collection of common files for all projects", + files = { + ".clinerules", + ".cursorrules", + ".goosehints", + ".rules", + ".windsurfrules", + ".github/copilot-instructions.md", + "AGENT.md", + "AGENTS.md", + { path = "CLAUDE.md", parser = "claude" }, + { path = "CLAUDE.local.md", parser = "claude" }, + { path = "~/.claude/CLAUDE.md", parser = "claude" }, + }, + is_preset = true, + }, + opts = { + chat = { + enabled = true, + default_rules = "default", -- The rule groups to load + }, + }, + }, }, init = function() require("utils.codecompanion.fidget-spinner"):init() diff --git a/.config/nvim/lua/utils/codecompanion/fidget-spinner-notify.lua b/.config/nvim/lua/utils/codecompanion/fidget-spinner-notify.lua index d6aaa50..8cfbb29 100644 --- a/.config/nvim/lua/utils/codecompanion/fidget-spinner-notify.lua +++ b/.config/nvim/lua/utils/codecompanion/fidget-spinner-notify.lua @@ -50,7 +50,7 @@ end function M:create_progress_handle(request) local title = " Requesting assistance" .. " (" - .. request.data.strategy + .. request.data.interaction .. ") from " .. request.data.adapter.formatted_name .. " using " diff --git a/.config/nvim/lua/utils/codecompanion/fidget-spinner.lua b/.config/nvim/lua/utils/codecompanion/fidget-spinner.lua index 59c3c5f..bc2bb90 100644 --- a/.config/nvim/lua/utils/codecompanion/fidget-spinner.lua +++ b/.config/nvim/lua/utils/codecompanion/fidget-spinner.lua @@ -43,10 +43,10 @@ end function M:create_progress_handle(request) return progress.handle.create({ - title = " Requesting assistance (" .. request.data.strategy .. ")", + title = " Requesting assistance (" .. request.data.adapter.model .. ")", message = "In progress...", lsp_client = { - name = M:llm_role_title(request.data.adapter), + name = M:llm_role_title(request.data.adapter.name), }, }) end From 40fa19cedc1895cd4b7604cac0a4df72e05f44f1 Mon Sep 17 00:00:00 2001 From: sudacode Date: Sun, 14 Dec 2025 12:11:07 -0800 Subject: [PATCH 07/17] update --- .bash_aliases | 4 ++-- .config/rofi-open/config.json | 2 +- projects/scripts/ocr.sh | 8 +++++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.bash_aliases b/.bash_aliases index d2b9022..302e6b8 100644 --- a/.bash_aliases +++ b/.bash_aliases @@ -14,8 +14,8 @@ alias aniwrapper='aniwrapper -D 144' ## Colorls alias ls='eza -M --group-directories-first --icons --color=always --group --git' -alias ll='ls -l' -alias la='ls -la' +alias ll='ls -lh' +alias la='ls -lah' alias vimf='vim $(fzf --height=45% --layout=reverse --preview="bat --style=numbers --color=always --line-range :500 {}")' diff --git a/.config/rofi-open/config.json b/.config/rofi-open/config.json index ab3d960..a1a0fef 100644 --- a/.config/rofi-open/config.json +++ b/.config/rofi-open/config.json @@ -35,7 +35,7 @@ "Pihole2 - https://pihole2.suda.codes/admin", "Proxmox - https://thebox.unicorn-ilish.ts.net", "qBittorrent - https://qbittorrent.suda.codes", - "qui - https://qui.suda.codes", + "qui - http://pve-main:7476", "Plausible - https://plausible.sudacode.com", "Paperless - https://paperless.suda.codes", "Prometheus - http://prometheus:9090", diff --git a/projects/scripts/ocr.sh b/projects/scripts/ocr.sh index 8f1e97c..2e9137e 100755 --- a/projects/scripts/ocr.sh +++ b/projects/scripts/ocr.sh @@ -10,6 +10,8 @@ set -Eeuo pipefail # notify-send "GAZOU" "Text: $DISPLAY_RES" # echo "$RES" | wl-copy -slurp | grim -g - /tmp/ocr.png || exit 1 -transformers_ocr recognize --image-path /tmp/ocr.png || exit 1 -notify-send "tramsformers_ocr" "Text: $DISPLAY_RES" +# grim -g "$(slurp)" /tmp/ocr.png || exit 1 +# slurp | grim -g - /tmp/ocr/ocr.png || exit 1 +owocr -r clipboard -w clipboard -of text -n || exit 1 +slurp | grim -g - | wl-copy +notify-send "ocr.sh" "Text: $DISPLAY_RES" From c2ce03d7e6dc230a560c7f59616646d32a090a0a Mon Sep 17 00:00:00 2001 From: sudacode Date: Fri, 19 Dec 2025 23:58:24 -0800 Subject: [PATCH 08/17] update to owocr and meikiocr --- projects/scripts/ocr.sh | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/projects/scripts/ocr.sh b/projects/scripts/ocr.sh index 2e9137e..758816e 100755 --- a/projects/scripts/ocr.sh +++ b/projects/scripts/ocr.sh @@ -1,17 +1,9 @@ #!/bin/bash set -Eeuo pipefail -# RES="$(slurp | grim -g - - | gazou | sed '1d;$d')" -# # Truncate RES for display if it's longer than 100 characters -# DISPLAY_RES="${RES:0:100}" -# if [ ${#RES} -gt 100 ]; then -# DISPLAY_RES="${DISPLAY_RES}..." -# fi -# notify-send "GAZOU" "Text: $DISPLAY_RES" -# echo "$RES" | wl-copy - -# grim -g "$(slurp)" /tmp/ocr.png || exit 1 -# slurp | grim -g - /tmp/ocr/ocr.png || exit 1 -owocr -r clipboard -w clipboard -of text -n || exit 1 +if ! pgrep -af owocr; then + notify-send "ocr.sh" "Starting owocr daemon..." + owocr -e meikiocr -r clipboard -w clipboard -l ja -n &>/dev/null & +fi slurp | grim -g - | wl-copy notify-send "ocr.sh" "Text: $DISPLAY_RES" From 58de3517c121b574c6add84633a67e0637b2e434 Mon Sep 17 00:00:00 2001 From: sudacode Date: Sat, 20 Dec 2025 00:42:46 -0800 Subject: [PATCH 09/17] update --- projects/scripts/ocr.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/projects/scripts/ocr.sh b/projects/scripts/ocr.sh index 758816e..2ae9426 100755 --- a/projects/scripts/ocr.sh +++ b/projects/scripts/ocr.sh @@ -5,5 +5,5 @@ if ! pgrep -af owocr; then notify-send "ocr.sh" "Starting owocr daemon..." owocr -e meikiocr -r clipboard -w clipboard -l ja -n &>/dev/null & fi -slurp | grim -g - | wl-copy +slurp | grim -g - - | wl-copy notify-send "ocr.sh" "Text: $DISPLAY_RES" From 2cc3cc363c5b03bc1db77a12a559ac3115a00792 Mon Sep 17 00:00:00 2001 From: sudacode Date: Sat, 20 Dec 2025 15:28:29 -0800 Subject: [PATCH 10/17] add jellyfin dir helper --- .config/hypr/keybindings.conf | 2 + .config/rofi/scripts/rofi-jellyfin-dir.sh | 89 +++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100755 .config/rofi/scripts/rofi-jellyfin-dir.sh diff --git a/.config/hypr/keybindings.conf b/.config/hypr/keybindings.conf index e71dfd0..94016f7 100644 --- a/.config/hypr/keybindings.conf +++ b/.config/hypr/keybindings.conf @@ -95,6 +95,7 @@ bind = $mainMod SHIFT, v, exec, uwsm app -sb -- rofi-rbw bind = $mainMod, w, exec, rofi -show window -theme $HOME/.config/rofi/launchers/type-2/style-2.rasi -dpi 96 -theme-str 'window {width: 35%;}' bind = $mainMod SHIFT, w, exec, $HOME/.config/rofi/scripts/rofi-wallpaper.sh bind = $mainMod SHIFT, d, exec, $HOME/.config/rofi/scripts/rofi-docs.sh +bind = SUPER, j, exec, "$HOME/.config/rofi/scripts/rofi-jellyfin-dir.sh" # ncmcppp bind = $mainMod, n, exec, uwsm app -sb -- ghostty --command=/usr/bin/ncmpcpp @@ -140,5 +141,6 @@ submap = reset bind = SUPER, l, exec, hyprlock +# ANKI bind = $mainMod, a, exec, ~/.config/rofi/scripts/rofi-anki-script.sh bind = $mainMod SHIFT, a, exec, ~/projects/scripts/screenshot-anki.sh -cdMinecraft diff --git a/.config/rofi/scripts/rofi-jellyfin-dir.sh b/.config/rofi/scripts/rofi-jellyfin-dir.sh new file mode 100755 index 0000000..c58d932 --- /dev/null +++ b/.config/rofi/scripts/rofi-jellyfin-dir.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash + +set -Eeuo pipefail + +BASE_DIR="/truenas/jellyfin" + +. "$HOME/.config/rofi/scripts/rofi-menu-helpers.sh" + +ACTION="xdg-open" + +# Theme for icon display +ICON_THEME="$HOME/.config/rofi/launchers/type-3/style-4.rasi" +ICON_THEME_STR='configuration {show-icons: true; icon-size: 128; dpi: 96;} window {width: 50%; height: 60%;} listview {columns: 3; lines: 5;}' + +# Map display names to actual directory names +declare -A DIR_MAP=( + ["Anime"]="anime" + ["Movies"]="movies" + ["Manga"]="manga" + ["TV"]="tv" + ["YouTube"]="youtube" + ["Books"]="books" + ["Podcasts"]="podcasts" + ["Audiobooks"]="audiobooks" +) + +DIRS=( + "Anime" + "Movies" + "Manga" + "TV" + "YouTube" + "Books" + "Podcasts" + "Audiobooks" +) + +# Select top-level category +CHOICE=$(rofi_select_list "Select a category" DIRS) || exit 1 + +# Get the actual directory name +ACTUAL_DIR="${DIR_MAP[$CHOICE]}" +TARGET_DIR="$BASE_DIR/$ACTUAL_DIR" + +if [[ ! -d "$TARGET_DIR" ]]; then + notify-send -u critical "Jellyfin Browser" "Directory not found: $TARGET_DIR" + exit 1 +fi + +# Build rofi entries with folder.jpg icons +build_icon_menu() { + local dir="$1" + local entries="" + + while IFS= read -r -d '' subdir; do + local name + name="$(basename "$subdir")" + local icon="$subdir/folder.jpg" + + # Check for folder.jpg, fallback to folder.png, then no icon + if [[ -f "$icon" ]]; then + entries+="${name}\0icon\x1f${icon}\n" + elif [[ -f "$subdir/folder.png" ]]; then + entries+="${name}\0icon\x1f${subdir}/folder.png\n" + else + entries+="${name}\n" + fi + done < <(find "$dir" -mindepth 1 -maxdepth 1 -type d -print0 | sort -z) + + printf "%b" "$entries" +} + +# Show subdirectories with icons +SELECTION=$(build_icon_menu "$TARGET_DIR" | rofi -dmenu -i -no-custom \ + -theme "$ICON_THEME" \ + -theme-str "$ICON_THEME_STR" \ + -p "Select from $CHOICE") || exit 1 + +# Full path to selected item +SELECTED_PATH="$TARGET_DIR/$SELECTION" + +if [[ -d "$SELECTED_PATH" ]]; then + # Open in file manager or do something with it + # You can customize this action as needed + $ACTION "$SELECTED_PATH" &>/dev/null & +else + notify-send -u critical "Jellyfin Browser" "Path not found: $SELECTED_PATH" + exit 1 +fi From da97c974b937b1614ae6e9cf49e4e3696c4a7f64 Mon Sep 17 00:00:00 2001 From: sudacode Date: Sun, 21 Dec 2025 00:14:58 -0800 Subject: [PATCH 11/17] update --- .config/ghostty/config##Default | 6 +- .config/ghostty/config##os.Darwin | 3 + .config/hypr/hyprland.conf | 2 +- .config/mimeapps.list | 154 ++++++++++++++++++++++++++++++ 4 files changed, 163 insertions(+), 2 deletions(-) create mode 100644 .config/mimeapps.list diff --git a/.config/ghostty/config##Default b/.config/ghostty/config##Default index 453bfee..b927875 100644 --- a/.config/ghostty/config##Default +++ b/.config/ghostty/config##Default @@ -4,7 +4,7 @@ font-size = 12 font-feature = +calt font-feature = +liga font-feature = +dlig -theme = catppuccin-macchiato +theme = Catppuccin Macchiato cursor-style = block window-padding-x = 10 window-padding-y = 10 @@ -14,6 +14,10 @@ window-width = 180 confirm-close-surface = false copy-on-select = clipboard app-notifications = no-clipboard-copy +shell-integration = zsh +shell-integration-features = title,sudo,ssh-env,ssh-terminfo +desktop-notifications = true +term=ghostty keybind = all:ctrl+enter=unbind keybind = all:ctrl+shift+j=next_tab keybind = all:ctrl+shift+k=last_tab diff --git a/.config/ghostty/config##os.Darwin b/.config/ghostty/config##os.Darwin index b54ed74..6ea1e59 100644 --- a/.config/ghostty/config##os.Darwin +++ b/.config/ghostty/config##os.Darwin @@ -17,3 +17,6 @@ app-notifications = no-clipboard-copy keybind = all:ctrl+enter=unbind keybind = all:ctrl+grave_accent=toggle_quick_terminal shell-integration = zsh +shell-integration-features = title,sudo,ssh-env,ssh-terminfo +desktop-notifications = true +term=ghostty diff --git a/.config/hypr/hyprland.conf b/.config/hypr/hyprland.conf index cd8fc87..3896428 100644 --- a/.config/hypr/hyprland.conf +++ b/.config/hypr/hyprland.conf @@ -54,7 +54,7 @@ xwayland { # See https://wiki.hyprland.org/Configuring/Keywords/ # Set programs that you use -$terminal = uwsm app -- ghostty +$terminal = uwsm app -- ghostty +new-window $fileManager = uwsm app -- thunar $menu = rofi -show drun -run-command "uwsm app -- {cmd}" # $notification_daemon = dunst diff --git a/.config/mimeapps.list b/.config/mimeapps.list new file mode 100644 index 0000000..3ac1677 --- /dev/null +++ b/.config/mimeapps.list @@ -0,0 +1,154 @@ +[Added Associations] +application/epub+zip=calibre-ebook-viewer.desktop;calibre-ebook-edit.desktop;opencomic.desktop; +application/json=notepadqq.desktop; +application/octet-stream=nvim.desktop;vim.desktop;emacsclient.desktop; +application/pdf=okularApplication_pdf.desktop;google-chrome.desktop;microsoft-edge-beta.desktop;org.inkscape.Inkscape.desktop;chromium.desktop; +application/rss+xml=fluent-reader.desktop; +application/sql=notepadqq.desktop;nvim.desktop;gvim.desktop; +application/vnd.openxmlformats-officedocument.spreadsheetml.sheet=libreoffice-calc.desktop;ms-office-online.desktop; +application/x-desktop=nvim.desktop; +application/x-extension-htm=google-chrome.desktop; +application/x-extension-html=google-chrome.desktop; +application/x-extension-shtml=google-chrome.desktop; +application/x-extension-xht=google-chrome.desktop; +application/x-extension-xhtml=google-chrome.desktop; +application/x-ms-dos-executable=wine.desktop; +application/x-ms-shortcut=wine.desktop; +application/x-yaml=notepadqq.desktop;nvim.desktop; +application/xhtml+xml=google-chrome.desktop;microsoft-edge-beta.desktop;qutebrowser.desktop; +application/zip=org.gnome.FileRoller.desktop; +audio/aac=mpv.desktop; +audio/mp4=mpv.desktop; +audio/mpeg=mpv.desktop; +audio/mpegurl=mpv.desktop; +audio/ogg=mpv.desktop; +audio/vnd.rn-realaudio=mpv.desktop; +audio/vorbis=mpv.desktop; +audio/x-flac=mpv.desktop; +audio/x-mp3=mpv.desktop; +audio/x-mpegurl=mpv.desktop; +audio/x-ms-wma=mpv.desktop; +audio/x-musepack=mpv.desktop; +audio/x-oggflac=mpv.desktop; +audio/x-pn-realaudio=mpv.desktop; +audio/x-scpls=mpv.desktop; +audio/x-vorbis=mpv.desktop; +audio/x-vorbis+ogg=mpv.desktop; +audio/x-wav=mpv.desktop; +image/avif=okularApplication_kimgio.desktop; +image/bmp=okularApplication_kimgio.desktop; +image/gif=org.gnome.gThumb.desktop;google-chrome.desktop;gimp.desktop;org.kde.gwenview.desktop;okularApplication_kimgio.desktop; +image/heif=okularApplication_kimgio.desktop; +image/jpeg=okularApplication_kimgio.desktop; +image/png=okularApplication_kimgio.desktop;org.gnome.gThumb.desktop;feh.desktop;gimp.desktop;org.kde.gwenview.desktop; +image/webp=okularApplication_kimgio.desktop; +inode/directory=thunar.desktop; +text/csv=libreoffice-calc.desktop; +text/html=google-chrome.desktop; +text/javascript=notepadqq.desktop; +text/plain=notepadqq.desktop;nvim.desktop;vim.desktop;okularApplication_txt.desktop;xed.desktop; +text/vnd.trolltech.linguist=mpv.desktop; +text/x-log=notepadqq.desktop; +video/mp4=mpv.desktop;vlc.desktop;io.github.celluloid_player.Celluloid.desktop; +video/webm=mpv.desktop;vlc.desktop;io.github.celluloid_player.Celluloid.desktop; +video/x-matroska=mpv.desktop;vlc.desktop; +x-scheme-handler/betterdiscord=discord.desktop; +x-scheme-handler/bitwarden=bitwarden.desktop;Bitwarden.desktop; +x-scheme-handler/chrome=google-chrome.desktop; +x-scheme-handler/exodus=Exodus.desktop; +x-scheme-handler/geo=google-maps-geo-handler.desktop; +x-scheme-handler/http=zen.desktop;firefox.desktop;microsoft-edge-beta.desktop;google-chrome.desktop; +x-scheme-handler/https=zen.desktop;firefox.desktop;microsoft-edge-beta.desktop;google-chrome.desktop; +x-scheme-handler/mailspring=Mailspring.desktop; +x-scheme-handler/mailto=org.mozilla.Thunderbird.desktop;Mailspring.desktop;userapp-Thunderbird-6JYZ12.desktop; +x-scheme-handler/mid=userapp-Thunderbird-6JYZ12.desktop; +x-scheme-handler/postman=Postman.desktop; +x-scheme-handler/ror2mm=r2modman.desktop; +x-scheme-handler/ssh=kitty-open.desktop;Termius.desktop; +x-scheme-handler/termius=Termius.desktop; +x-scheme-handler/tg=org.telegram.desktop.desktop;org.telegram.desktop._f79d601e26a782fd149b3ffb098aae9f.desktop;userapp-Kotatogram Desktop-IP6312.desktop; +x-scheme-handler/tonsite=org.telegram.desktop.desktop; +x-scheme-handler/tradingview=tradingview.desktop;TradingView.desktop; +application/x-wine-extension-ini=nvim.desktop; + +[Default Applications] +application/x-extension-htm=google-chrome.desktop +application/x-extension-html=google-chrome.desktop +application/x-extension-shtml=google-chrome.desktop +application/x-extension-xht=google-chrome.desktop +application/x-extension-xhtml=google-chrome.desktop +audio/aac=mpv.desktop; +audio/mp4=mpv.desktop; +audio/mpeg=mpv.desktop; +audio/mpegurl=mpv.desktop; +audio/ogg=mpv.desktop; +audio/vnd.rn-realaudio=mpv.desktop; +audio/vorbis=mpv.desktop; +audio/x-flac=mpv.desktop; +audio/x-mp3=mpv.desktop; +audio/x-mpegurl=mpv.desktop; +audio/x-ms-wma=mpv.desktop; +audio/x-musepack=mpv.desktop; +audio/x-oggflac=mpv.desktop; +audio/x-pn-realaudio=mpv.desktop; +audio/x-scpls=mpv.desktop; +audio/x-vorbis=mpv.desktop; +audio/x-vorbis+ogg=mpv.desktop; +audio/x-wav=mpv.desktop; +image/avif=okularApplication_kimgio.desktop; +image/bmp=okularApplication_kimgio.desktop; +image/heif=okularApplication_kimgio.desktop; +image/jpeg=okularApplication_kimgio.desktop; +image/png=okularApplication_kimgio.desktop; +image/webp=okularApplication_kimgio.desktop; +inode/directory=Thunar.desktop +message/rfc822=userapp-Thunderbird-6JYZ12.desktop +text/plain=nvim.desktop; +x-scheme-handler/betterdiscord=discord.desktop +x-scheme-handler/bitwarden=bitwarden.desktop +x-scheme-handler/chrome=chromium.desktop +x-scheme-handler/discord-455712169795780630=discord-455712169795780630.desktop +x-scheme-handler/discord-712465656758665259=discord-712465656758665259.desktop +x-scheme-handler/eclipse+command=_usr_lib_dbeaver_.desktop +x-scheme-handler/exodus=Exodus.desktop +x-scheme-handler/geo=google-maps-geo-handler.desktop; +x-scheme-handler/http=zen.desktop; +x-scheme-handler/https=zen.desktop; +x-scheme-handler/mailspring=Mailspring.desktop +x-scheme-handler/mailto=Mailspring.desktop +x-scheme-handler/mid=userapp-Thunderbird-6JYZ12.desktop +x-scheme-handler/msteams=teams.desktop +x-scheme-handler/postman=Postman.desktop +x-scheme-handler/ror2mm=r2modman.desktop +x-scheme-handler/ssh=kitty-open.desktop +x-scheme-handler/termius=Termius.desktop +x-scheme-handler/tg=org.telegram.desktop.desktop +x-scheme-handler/tonsite=org.telegram.desktop.desktop +x-scheme-handler/tradingview=tradingview.desktop +x-scheme-handler/webcal=google-chrome.desktop +video/webm=mpv.desktop +video/x-matroska=mpv.desktop +video/x-ms-wmv=mpv.desktop +video/quicktime=mpv.desktop +video/x-flv=mpv.desktop +video/dv=mpv.desktop +video/vnd.avi=mpv.desktop +video/x-ogm+ogg=mpv.desktop +video/ogg=mpv.desktop +video/vnd.rn-realvideo=mpv.desktop +video/mp4=mpv.desktop +video/mp2t=mpv.desktop +video/x-flic=mpv.desktop +video/3gpp2=mpv.desktop +video/x-theora+ogg=mpv.desktop +video/mpeg=mpv.desktop +video/vnd.mpegurl=mpv.desktop +video/3gpp=mpv.desktop +application/json=zen.desktop +application/xhtml+xml=zen.desktop +application/x-xpinstall=zen.desktop +application/xml=zen.desktop +application/pdf=zen.desktop +text/html=zen.desktop +text/vnd.trolltech.linguist=zen.desktop +x-scheme-handler/nxm=com.nexusmods.app.desktop From b435e0d92c6543a6ca5cd2b608294b0d7f20d590 Mon Sep 17 00:00:00 2001 From: sudacode Date: Mon, 29 Dec 2025 22:53:58 -0800 Subject: [PATCH 12/17] add steam texthooker launcher --- .config/rofi/scripts/rofi-launch-texthooker-steam.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100755 .config/rofi/scripts/rofi-launch-texthooker-steam.sh diff --git a/.config/rofi/scripts/rofi-launch-texthooker-steam.sh b/.config/rofi/scripts/rofi-launch-texthooker-steam.sh new file mode 100755 index 0000000..497069e --- /dev/null +++ b/.config/rofi/scripts/rofi-launch-texthooker-steam.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +PROGRAM="$HOME/S/lutris/wineprefix/drive_c/users/steamuser/luna-translator/LunaTranslator.exe" +SELECTION="$(protontricks -l | tail -n +2 | rofi -dmenu -theme ~/.config/rofi/launchers/type-2/style-2.rasi -theme-str 'listview {lines: 12; columns: 1;}' -i -p "Select game" | awk '{print $NF}' | tr -d '()')" + +if [[ -z "$SELECTION" ]]; then + printf "%s\n" "No game selected" + exit 1 +fi + +printf "%s\n" "Launching $PROGRAM for game ID: $SELECTION" +protontricks-launch --appid "$SELECTION" "$PROGRAM" &>/dev/null & From 63e6fad21630e91b279cf58e40cf1115647b2554 Mon Sep 17 00:00:00 2001 From: sudacode Date: Mon, 29 Dec 2025 22:54:17 -0800 Subject: [PATCH 13/17] update --- .config/mimeapps.list | 3 ++- .config/rofi/scripts/rofi-anki-script.sh | 5 ++--- .zsh/.zshrc##default | 1 + projects/scripts/screenshot-anki.sh | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.config/mimeapps.list b/.config/mimeapps.list index 3ac1677..b2b9eb9 100644 --- a/.config/mimeapps.list +++ b/.config/mimeapps.list @@ -151,4 +151,5 @@ application/xml=zen.desktop application/pdf=zen.desktop text/html=zen.desktop text/vnd.trolltech.linguist=zen.desktop -x-scheme-handler/nxm=com.nexusmods.app.desktop +x-scheme-handler/nxm=modorganizer2-nxm-handler.desktop +x-scheme-handler/discord-1361252452329848892=discord-1361252452329848892.desktop diff --git a/.config/rofi/scripts/rofi-anki-script.sh b/.config/rofi/scripts/rofi-anki-script.sh index 1c8767f..b387446 100755 --- a/.config/rofi/scripts/rofi-anki-script.sh +++ b/.config/rofi/scripts/rofi-anki-script.sh @@ -1,14 +1,14 @@ #!/usr/bin/env bash CHOICES=( - "1. Screenshot (Lapis)" + "1. Screenshot (Kiku)" "2. Screenshot (Luna)" "3. Record Audio" ) CHOICE=$(printf "%s\n" "${CHOICES[@]}" | rofi -dmenu -i -theme "$HOME/.config/rofi/launchers/type-2/style-2.rasi" -theme-str 'window {width: 25%;} listview {columns: 1; lines: 5;}' -p "Select an option") case "$CHOICE" in -"1. Screenshot (Lapis)") +"1. Screenshot (Kiku)") PICTURE_FIELD=Picture "$HOME/projects/scripts/screenshot-anki.sh" ;; "2. Screenshot (Luna)") @@ -21,4 +21,3 @@ case "$CHOICE" in exit 1 ;; esac - diff --git a/.zsh/.zshrc##default b/.zsh/.zshrc##default index 28cc42e..3410acb 100644 --- a/.zsh/.zshrc##default +++ b/.zsh/.zshrc##default @@ -28,6 +28,7 @@ setopt HIST_IGNORE_SPACE # don’t record commands that start with a space FPATH="$HOME/.docker/completions:$FPATH" [[ "$TERM_PROGRAM" == "vscode" ]] && . "$(code --locate-shell-integration-path zsh)" +eval "$(fnm env --use-on-cd --shell zsh)" bindkey -v # zstyle ':completion:*' matcher-list 'm:{a-zA-Z}={A-Za-z}' diff --git a/projects/scripts/screenshot-anki.sh b/projects/scripts/screenshot-anki.sh index 2766e9a..72bd318 100755 --- a/projects/scripts/screenshot-anki.sh +++ b/projects/scripts/screenshot-anki.sh @@ -31,7 +31,7 @@ parse_opts() { *) echo "Usage: $0 [-c] [-n DECK_NAME]" >&2 echo " -c: Capture current window" >&2 - echo " -n: Specify note name (e.g., Lapis)" >&2 + echo " -n: Specify note name (e.g., Kiku)" >&2 exit 1 ;; esac From 6a852bb48ca5eb5d7657524c9f2c6fea3a1a9f67 Mon Sep 17 00:00:00 2001 From: sudacode Date: Wed, 31 Dec 2025 01:38:43 -0800 Subject: [PATCH 14/17] update --- .config/hypr/keybindings.conf | 6 +- projects/scripts/popup-ai-translator.py | 248 ++++++++++++++++++++++++ 2 files changed, 252 insertions(+), 2 deletions(-) create mode 100755 projects/scripts/popup-ai-translator.py diff --git a/.config/hypr/keybindings.conf b/.config/hypr/keybindings.conf index 94016f7..194a87e 100644 --- a/.config/hypr/keybindings.conf +++ b/.config/hypr/keybindings.conf @@ -93,9 +93,11 @@ bindl = , XF86AudioPrev, exec, mpc prev # rofi bind = $mainMod SHIFT, v, exec, uwsm app -sb -- rofi-rbw bind = $mainMod, w, exec, rofi -show window -theme $HOME/.config/rofi/launchers/type-2/style-2.rasi -dpi 96 -theme-str 'window {width: 35%;}' -bind = $mainMod SHIFT, w, exec, $HOME/.config/rofi/scripts/rofi-wallpaper.sh -bind = $mainMod SHIFT, d, exec, $HOME/.config/rofi/scripts/rofi-docs.sh +bind = $mainMod SHIFT, w, exec, "$HOME/.config/rofi/scripts/rofi-wallpaper.sh" +bind = $mainMod SHIFT, d, exec, "$HOME/.config/rofi/scripts/rofi-docs.sh" bind = SUPER, j, exec, "$HOME/.config/rofi/scripts/rofi-jellyfin-dir.sh" +bind = $mainMod SHIFT, t, exec, "$HOME/.config/rofi/scripts/rofi-launch-texthooker-steam.sh" +bind = $mainMod, t, exec, "$HOME/projects/scripts/popup-ai-translator.py" # ncmcppp bind = $mainMod, n, exec, uwsm app -sb -- ghostty --command=/usr/bin/ncmpcpp diff --git a/projects/scripts/popup-ai-translator.py b/projects/scripts/popup-ai-translator.py new file mode 100755 index 0000000..3b27ea1 --- /dev/null +++ b/projects/scripts/popup-ai-translator.py @@ -0,0 +1,248 @@ +#!/usr/bin/env python3 +""" +Japanese Learning Assistant using OpenRouter API +Uses Google Gemini Flash 2.0 for AJATT-aligned Japanese analysis +""" + +import os +import subprocess +import sys + +import requests + +# Configuration +OPENROUTER_API_KEY = os.environ.get("OPENROUTER_API_KEY", "") +MODEL = os.environ.get("OPENROUTER_MODEL", "google/gemini-2.0-flash-001") +API_URL = "https://openrouter.ai/api/v1/chat/completions" + +# Try to load API key from file if not in environment +if not OPENROUTER_API_KEY: + key_file = os.path.expanduser("~/.openrouterapikey") + if os.path.isfile(key_file): + with open(key_file, "r") as f: + OPENROUTER_API_KEY = f.read().strip() + +SYSTEM_PROMPT = """You are my Japanese-learning assistant. Help me acquire Japanese through deep, AJATT-aligned analysis. + +For every input, output exactly using clean plain text formatting: + +═══════════════════════════════════════ +1. JAPANESE INPUT +═══════════════════════════════════════ + +Repeat the original text exactly. Correct only critical OCR/punctuation errors. + +═══════════════════════════════════════ +2. NATURAL ENGLISH TRANSLATION +═══════════════════════════════════════ + +Accurate and natural. Preserve tone, formality, and nuance. Avoid literalism. + +═══════════════════════════════════════ +3. BREAKDOWN +═══════════════════════════════════════ + +For each token, provide a breakdown of the vocabulary, grammar, and nuance. + + ▸ Vocabulary: Part of speech + concise definition + ▸ Grammar: Particles, conjugations, constructions (contextual usage) + ▸ Nuance: Implied meaning, connotation, emotional tone, differences from similar expressions + +Core Principles: + + • Preserve native phrasing—never oversimplify + • Highlight subtle grammar, register shifts, and pragmatic implications + • Encourage pattern recognition; provide contrastive examples (e.g., ~のに vs ~けど) + • Focus on real Japanese usage + +Rules: + + • English explanations only (no romaji) + • Use the section dividers shown above (═══) for major sections + • Use ▸ for sub-items and • for bullet points + • Put Japanese terms in 「brackets」 + • No filler text + +Optional Additions (only when valuable): + + • Synonyms, formality/register notes, cultural insights, common mistakes, extra native examples + +Goal: Deep comprehension, natural grammar internalization, nuanced vocabulary, progress toward Japanese-only understanding.""" + + +def show_error(message: str) -> None: + """Display an error dialog using zenity.""" + subprocess.run( + ["zenity", "--error", "--text", message, "--title", "Error"], + stderr=subprocess.DEVNULL, + ) + + +def get_clipboard() -> str: + """Get clipboard contents using wl-paste or xclip.""" + # Try wl-paste first (Wayland) + result = subprocess.run( + ["wl-paste", "--no-newline"], + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + text=True, + ) + if result.returncode == 0: + return result.stdout.strip() + + # Fall back to xclip (X11) + result = subprocess.run( + ["xclip", "-selection", "clipboard", "-o"], + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + text=True, + ) + if result.returncode == 0: + return result.stdout.strip() + + return "" + + +def get_input() -> str | None: + """Get input from user via zenity entry dialog, pre-populated with clipboard.""" + clipboard = get_clipboard() + + cmd = [ + "zenity", + "--entry", + "--title", + "Japanese Assistant", + "--text", + "Enter Japanese text to analyze (press Enter to send):", + "--width", + "600", + ] + + if clipboard: + cmd.extend(["--entry-text", clipboard]) + + result = subprocess.run( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + text=True, + ) + if result.returncode != 0: + return None + return result.stdout.strip() + + +def show_notification(message: str, body: str) -> subprocess.Popen: + """Show a notification using notify-send.""" + return subprocess.Popen( + ["notify-send", "-t", "0", "-a", "Japanese Assistant", message, body], + stderr=subprocess.DEVNULL, + ) + + +def close_notification() -> None: + """Close the processing notification.""" + subprocess.run( + ["pkill", "-f", "notify-send.*Processing.*Analyzing"], + stderr=subprocess.DEVNULL, + ) + + +def display_result(content: str) -> None: + """Display the result in a zenity text-info dialog.""" + subprocess.run( + [ + "zenity", + "--text-info", + "--title", + "Japanese Analysis", + "--width", + "900", + "--height", + "700", + "--font", + "monospace 11", + ], + input=content, + text=True, + stderr=subprocess.DEVNULL, + ) + + +def make_api_request(user_input: str) -> dict: + """Make the API request to OpenRouter.""" + headers = { + "Content-Type": "application/json", + "Authorization": f"Bearer {OPENROUTER_API_KEY}", + "HTTP-Referer": "https://github.com/sudacode/scripts", + "X-Title": "Japanese Learning Assistant", + } + + payload = { + "model": MODEL, + "messages": [ + {"role": "system", "content": SYSTEM_PROMPT}, + {"role": "user", "content": user_input}, + ], + "temperature": 0.7, + } + + response = requests.post(API_URL, headers=headers, json=payload, timeout=60) + return response.json() + + +def main() -> int: + # Check for API key + if not OPENROUTER_API_KEY: + show_error("OPENROUTER_API_KEY environment variable is not set.") + return 1 + + # Get input from user + user_input = get_input() + if not user_input: + return 0 + + # Show loading notification + show_notification("Processing...", f"Analyzing: {user_input[:50]}...") + + try: + # Make API request + response = make_api_request(user_input) + + # Close loading notification + close_notification() + + # Check for errors in response + if "error" in response: + error_msg = response["error"].get("message", "Unknown error") + show_error(error_msg) + return 1 + + # Extract content from response + try: + content = response["choices"][0]["message"]["content"] + except (KeyError, IndexError): + show_error("Failed to parse API response") + return 1 + + if not content: + show_error("Empty response from API") + return 1 + + # Display result + display_result(content) + + except requests.RequestException as e: + close_notification() + show_error(f"API request failed: {e}") + return 1 + except Exception as e: + close_notification() + show_error(f"Error: {e}") + return 1 + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) From 5efefaf6dec7e6a84af02186a5d53e8e3a1f2482 Mon Sep 17 00:00:00 2001 From: sudacode Date: Thu, 8 Jan 2026 01:01:38 -0800 Subject: [PATCH 15/17] update --- .config/ghostty/config##Default | 1 + .config/hypr/keybindings.conf | 5 ++- .config/mpv/script-opts/subs2srs.conf | 1 + projects/scripts/popup-ai-translator.py | 60 +++++++++++++++++++++++-- 4 files changed, 61 insertions(+), 6 deletions(-) diff --git a/.config/ghostty/config##Default b/.config/ghostty/config##Default index b927875..a45f478 100644 --- a/.config/ghostty/config##Default +++ b/.config/ghostty/config##Default @@ -22,3 +22,4 @@ keybind = all:ctrl+enter=unbind keybind = all:ctrl+shift+j=next_tab keybind = all:ctrl+shift+k=last_tab keybind = all:ctrl+grave_accent=toggle_quick_terminal +keybind = shift+enter=text:\x1b\r diff --git a/.config/hypr/keybindings.conf b/.config/hypr/keybindings.conf index 194a87e..ed6e6e7 100644 --- a/.config/hypr/keybindings.conf +++ b/.config/hypr/keybindings.conf @@ -96,8 +96,9 @@ bind = $mainMod, w, exec, rofi -show window -theme $HOME/.config/rofi/launchers/ bind = $mainMod SHIFT, w, exec, "$HOME/.config/rofi/scripts/rofi-wallpaper.sh" bind = $mainMod SHIFT, d, exec, "$HOME/.config/rofi/scripts/rofi-docs.sh" bind = SUPER, j, exec, "$HOME/.config/rofi/scripts/rofi-jellyfin-dir.sh" -bind = $mainMod SHIFT, t, exec, "$HOME/.config/rofi/scripts/rofi-launch-texthooker-steam.sh" -bind = $mainMod, t, exec, "$HOME/projects/scripts/popup-ai-translator.py" +bind = SUPER, t, exec, "$HOME/.config/rofi/scripts/rofi-launch-texthooker-steam.sh" +bind = $mainMod SHIFT, t, exec, "$HOME/projects/scripts/popup-ai-translator.py" +bind = SUPER SHIFT, g, exec, "$HOME/.config/rofi/scripts/rofi-vn-helper.sh" # ncmcppp bind = $mainMod, n, exec, uwsm app -sb -- ghostty --command=/usr/bin/ncmpcpp diff --git a/.config/mpv/script-opts/subs2srs.conf b/.config/mpv/script-opts/subs2srs.conf index db01b5a..ce529e4 100644 --- a/.config/mpv/script-opts/subs2srs.conf +++ b/.config/mpv/script-opts/subs2srs.conf @@ -14,6 +14,7 @@ deck_name=Minecraft # If you don't have a model for Japanese, get it from # https://tatsumoto.neocities.org/blog/setting-up-anki.html#import-an-example-mining-deck model_name=Lapis +# model_name=Kiku # Field names as they appear in the selected note type. # If you set `audio_field` or `image_field` empty, diff --git a/projects/scripts/popup-ai-translator.py b/projects/scripts/popup-ai-translator.py index 3b27ea1..2870323 100755 --- a/projects/scripts/popup-ai-translator.py +++ b/projects/scripts/popup-ai-translator.py @@ -22,7 +22,7 @@ if not OPENROUTER_API_KEY: with open(key_file, "r") as f: OPENROUTER_API_KEY = f.read().strip() -SYSTEM_PROMPT = """You are my Japanese-learning assistant. Help me acquire Japanese through deep, AJATT-aligned analysis. +TRANSLATION_PROMPT = """You are my Japanese-learning assistant. Help me acquire Japanese through deep, AJATT-aligned analysis. For every input, output exactly using clean plain text formatting: @@ -69,6 +69,24 @@ Optional Additions (only when valuable): Goal: Deep comprehension, natural grammar internalization, nuanced vocabulary, progress toward Japanese-only understanding.""" +GRAMMAR_PROMPT = """ + You are a Japanese grammar analysis assistant. + + The user will provide Japanese sentences. Your task is to explain the **grammar and structure in English**, prioritizing how the sentence is constructed rather than translating word-for-word. + + Rules: + + * Always show the original Japanese first. + * Provide a short, natural English gloss only if helpful. + * Explain grammar patterns, verb forms, particles, and omissions. + * Emphasize nuance, implication, and speaker intent. + * Avoid unnecessary vocabulary definitions unless they affect the grammar. + * Assume the user is actively studying Japanese and wants deep understanding. + + Your explanations should help the user internalize patterns, not memorize translations. + +""" + def show_error(message: str) -> None: """Display an error dialog using zenity.""" @@ -132,6 +150,29 @@ def get_input() -> str | None: return result.stdout.strip() +def get_mode() -> str | None: + """Ask user to choose between translation and grammar assistant.""" + result = subprocess.run( + [ + "zenity", + "--list", + "--title", + "Japanese Assistant", + "--text", + "Choose analysis mode:", + "--column=Mode", + "Translation (full analysis)", + "Grammar (structure focus)", + ], + stdout=subprocess.PIPE, + stderr=subprocess.DEVNULL, + text=True, + ) + if result.returncode != 0: + return None + return result.stdout.strip() + + def show_notification(message: str, body: str) -> subprocess.Popen: """Show a notification using notify-send.""" return subprocess.Popen( @@ -169,7 +210,7 @@ def display_result(content: str) -> None: ) -def make_api_request(user_input: str) -> dict: +def make_api_request(user_input: str, system_prompt: str) -> dict: """Make the API request to OpenRouter.""" headers = { "Content-Type": "application/json", @@ -181,7 +222,7 @@ def make_api_request(user_input: str) -> dict: payload = { "model": MODEL, "messages": [ - {"role": "system", "content": SYSTEM_PROMPT}, + {"role": "system", "content": system_prompt}, {"role": "user", "content": user_input}, ], "temperature": 0.7, @@ -197,6 +238,17 @@ def main() -> int: show_error("OPENROUTER_API_KEY environment variable is not set.") return 1 + # Ask user for mode + mode = get_mode() + if not mode: + return 0 + + # Select appropriate prompt + if "Grammar" in mode: + system_prompt = GRAMMAR_PROMPT + else: + system_prompt = TRANSLATION_PROMPT + # Get input from user user_input = get_input() if not user_input: @@ -207,7 +259,7 @@ def main() -> int: try: # Make API request - response = make_api_request(user_input) + response = make_api_request(user_input, system_prompt) # Close loading notification close_notification() From aa1696b3f35143dd6eae8b4b58365cf5e964704b Mon Sep 17 00:00:00 2001 From: kyasuda Date: Wed, 14 Jan 2026 13:19:08 -0800 Subject: [PATCH 16/17] update opencode --- .config/opencode/oh-my-opencode.jsonc | 44 +++++ .config/opencode/opencode.jsonc | 224 ++++++++++++++++++++++++++ 2 files changed, 268 insertions(+) create mode 100644 .config/opencode/oh-my-opencode.jsonc create mode 100644 .config/opencode/opencode.jsonc diff --git a/.config/opencode/oh-my-opencode.jsonc b/.config/opencode/oh-my-opencode.jsonc new file mode 100644 index 0000000..c11d884 --- /dev/null +++ b/.config/opencode/oh-my-opencode.jsonc @@ -0,0 +1,44 @@ +{ + "$schema": "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/master/assets/oh-my-opencode.schema.json", + "google_auth": true, + "agents": { + "Sisyphus": { + "model": "opencode/glm-4.7-free" + }, + "librarian": { + "model": "opencode/glm-4.7-free" + }, + "explore": { + "model": "google/antigravity-gemini-3-flash" + }, + "frontend-ui-ux-engineer": { + "model": "google/antigravity-gemini-3-pro-high" + }, + "document-writer": { + "model": "google/antigravity-gemini-3-flash" + }, + "multimodal-looker": { + "model": "google/antigravity-gemini-3-flash" + } + }, + "lsp": { + "typescript-language-server": { + "command": ["typescript-language-server", "--stdio"], + "extensions": [".ts", ".tsx"], + "priority": 10 + }, + "pylsp": { + "disabled": true + }, + "pyright": { + "command": ["basedpyright-languageserver", "--stdio"], + "extensions": [".py"], + "priority": 10 + }, + "bash-language-server": { + "command": ["bash-language-server", "start"], + "extensions": [".sh", ".bash"], + "priority": 10 + } + } +} diff --git a/.config/opencode/opencode.jsonc b/.config/opencode/opencode.jsonc new file mode 100644 index 0000000..62eaaa4 --- /dev/null +++ b/.config/opencode/opencode.jsonc @@ -0,0 +1,224 @@ +{ + "$schema": "https://opencode.ai/config.json", + "plugin": [ + "opencode-openai-codex-auth", + "opencode-antigravity-auth@beta", + "oh-my-opencode" + ], + "provider": { + "openai": { + "name": "OpenAI", + "options": { + "reasoningEffort": "medium", + "reasoningSummary": "auto", + "textVerbosity": "medium", + "include": [ + "reasoning.encrypted_content" + ], + "store": false + }, + "models": { + "gpt-5.2": { + "name": "GPT 5.2 (OAuth)", + "limit": { + "context": 272000, + "output": 128000 + }, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "variants": { + "none": { + "reasoningEffort": "none", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "low": { + "reasoningEffort": "low", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "medium": { + "reasoningEffort": "medium", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "high": { + "reasoningEffort": "high", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + }, + "xhigh": { + "reasoningEffort": "xhigh", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + } + } + }, + "gpt-5.2-codex": { + "name": "GPT 5.2 Codex (OAuth)", + "limit": { + "context": 272000, + "output": 128000 + }, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "variants": { + "low": { + "reasoningEffort": "low", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "medium": { + "reasoningEffort": "medium", + "reasoningSummary": "auto", + "textVerbosity": "medium" + }, + "high": { + "reasoningEffort": "high", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + }, + "xhigh": { + "reasoningEffort": "xhigh", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + } + } + }, + "gpt-5.1-codex-max": { + "name": "GPT 5.1 Codex Max (OAuth)", + "limit": { + "context": 272000, + "output": 128000 + }, + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "variants": { + "low": { + "reasoningEffort": "low", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + }, + "medium": { + "reasoningEffort": "medium", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + }, + "high": { + "reasoningEffort": "high", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + }, + "xhigh": { + "reasoningEffort": "xhigh", + "reasoningSummary": "detailed", + "textVerbosity": "medium" + } + } + } + } + }, + "google": { + "name": "Google", + "models": { + "antigravity-gemini-3-pro-high": { + "name": "Gemini 3 Pro High (Antigravity)", + "thinking": true, + "attachment": true, + "limit": { + "context": 1048576, + "output": 65535 + }, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + } + }, + "antigravity-gemini-3-pro-low": { + "name": "Gemini 3 Pro Low (Antigravity)", + "thinking": true, + "attachment": true, + "limit": { + "context": 1048576, + "output": 65535 + }, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + } + }, + "antigravity-gemini-3-flash": { + "name": "Gemini 3 Flash (Antigravity)", + "attachment": true, + "limit": { + "context": 1048576, + "output": 65536 + }, + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + } + } + } + } + }, + "theme": "catppuccin-macchiato", + "share": "manual", + "formatter": { + "prettier": { + "disabled": false + }, + "ruff": { + "disabled": false + } + }, + "instructions": [ + "AGENTS.md", + "CONTRIBUTING.md", + "docs/guidelines.md", + ".cursor/rules/*.md" + ], + "permission": { + "edit": "ask", + "bash": "ask" + } +} From ec94c68671b9366131adfde10f5a9355d8a65171 Mon Sep 17 00:00:00 2001 From: kyasuda Date: Wed, 14 Jan 2026 15:25:03 -0800 Subject: [PATCH 17/17] update opencode --- .config/opencode/opencode-notifier.json | 24 ++++++++++++ .config/opencode/opencode.json | 49 ------------------------- .config/opencode/opencode.jsonc | 1 + 3 files changed, 25 insertions(+), 49 deletions(-) create mode 100644 .config/opencode/opencode-notifier.json delete mode 100644 .config/opencode/opencode.json diff --git a/.config/opencode/opencode-notifier.json b/.config/opencode/opencode-notifier.json new file mode 100644 index 0000000..3917473 --- /dev/null +++ b/.config/opencode/opencode-notifier.json @@ -0,0 +1,24 @@ +{ + "sound": false, + "notification": true, + "timeout": 5, + "showProjectName": true, + "events": { + "permission": { "sound": false, "notification": true }, + "complete": { "sound": false, "notification": true }, + "error": { "sound": false, "notification": true }, + "question": { "sound": false, "notification": true } + }, + "messages": { + "permission": "Session needs permission", + "complete": "Session has finished", + "error": "Session encountered an error", + "question": "Session has a question" + }, + "sounds": { + "permission": "/path/to/custom/sound.wav", + "complete": "/path/to/custom/sound.wav", + "error": "/path/to/custom/sound.wav", + "question": "/path/to/custom/sound.wav" + } +} diff --git a/.config/opencode/opencode.json b/.config/opencode/opencode.json deleted file mode 100644 index 432ff89..0000000 --- a/.config/opencode/opencode.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "$schema": "https://opencode.ai/config.json", - "theme": "catppuccin", - "model": "github-copilot/gpt-5.1", - "provider": { - "openai": { - "models": { - "gpt-5": { - "options": { - "reasoningEffort": "high", - "textVerbosity": "low", - "reasoningSummary": "auto", - "include": ["reasoning.encrypted_content"], - }, - }, - }, - }, - }, - "agent": { - "build": { - "mode": "primary", - "model": "github-copilot/gpt-5.1", - "tools": { - "write": true, - "edit": true, - "bash": true - } - }, - "plan": { - "mode": "primary", - "model": "github-copilot/gpt-5.1-codex", - "tools": { - "write": false, - "edit": false, - "bash": false - } - }, - "code-reviewer": { - "description": "Reviews code for best practices and potential issues", - "mode": "subagent", - "model": "github-copilot/gpt-5.1", - "prompt": "You are a code reviewer. Focus on security, performance, and maintainability.", - "tools": { - "write": false, - "edit": false - } - } - } -} diff --git a/.config/opencode/opencode.jsonc b/.config/opencode/opencode.jsonc index 62eaaa4..21ab9f7 100644 --- a/.config/opencode/opencode.jsonc +++ b/.config/opencode/opencode.jsonc @@ -3,6 +3,7 @@ "plugin": [ "opencode-openai-codex-auth", "opencode-antigravity-auth@beta", + "@mohak34/opencode-notifier@latest", "oh-my-opencode" ], "provider": {