mirror of
https://github.com/ksyasuda/dotfiles.git
synced 2026-04-11 04:19:23 -07:00
Compare commits
5 Commits
872fc61ba5
...
7fe60df077
| Author | SHA1 | Date | |
|---|---|---|---|
|
7fe60df077
|
|||
|
748994320a
|
|||
|
15d31864db
|
|||
|
ac41c90066
|
|||
|
b20e439015
|
@@ -10,7 +10,14 @@
|
|||||||
"Read(~/.zshrc)",
|
"Read(~/.zshrc)",
|
||||||
"Bash(git * main)",
|
"Bash(git * main)",
|
||||||
"Bash(ls *)",
|
"Bash(ls *)",
|
||||||
"Bash(pnpm build *)"
|
"Bash(pnpm build *)",
|
||||||
|
"Bash(subminer stats:*)",
|
||||||
|
"Bash(identify /home/sudacode/projects/japanese/SubMiner/docs-site/public/screenshots/stats-trends.png)",
|
||||||
|
"mcp__plugin_playwright_playwright__browser_resize",
|
||||||
|
"mcp__plugin_playwright_playwright__browser_click",
|
||||||
|
"mcp__plugin_playwright_playwright__browser_take_screenshot",
|
||||||
|
"mcp__plugin_playwright_playwright__browser_close",
|
||||||
|
"Bash(bun run:*)"
|
||||||
],
|
],
|
||||||
"deny": [
|
"deny": [
|
||||||
"Bash(curl *)",
|
"Bash(curl *)",
|
||||||
|
|||||||
@@ -123,6 +123,15 @@ trust_level = "trusted"
|
|||||||
[projects."/home/sudacode/.config/git"]
|
[projects."/home/sudacode/.config/git"]
|
||||||
trust_level = "trusted"
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/sudacode/.config/rofi"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/sudacode/.config/swaync"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
|
[projects."/home/sudacode/.config/ranger"]
|
||||||
|
trust_level = "trusted"
|
||||||
|
|
||||||
[notice.model_migrations]
|
[notice.model_migrations]
|
||||||
"gpt-5.3-codex" = "gpt-5.4"
|
"gpt-5.3-codex" = "gpt-5.4"
|
||||||
|
|
||||||
|
|||||||
@@ -93,12 +93,15 @@
|
|||||||
"overwriteImage": true,
|
"overwriteImage": true,
|
||||||
"mediaInsertMode": "append",
|
"mediaInsertMode": "append",
|
||||||
"highlightWord": true,
|
"highlightWord": true,
|
||||||
"notificationType": "system",
|
"notificationType": "both",
|
||||||
"showNotificationOnUpdate": true,
|
"showNotificationOnUpdate": true,
|
||||||
"autoUpdateNewCards": true,
|
"autoUpdateNewCards": true,
|
||||||
},
|
},
|
||||||
"knownWords": {
|
"knownWords": {
|
||||||
"decks": ["Minecraft", "Kaishi 1.5k"],
|
"decks": {
|
||||||
|
"Minecraft": ["Expression", "Reading"],
|
||||||
|
"Kaishi 1.5k": ["Word", "Word Reading"]
|
||||||
|
},
|
||||||
"highlightEnabled": true,
|
"highlightEnabled": true,
|
||||||
"refreshMinutes": 60,
|
"refreshMinutes": 60,
|
||||||
"matchMode": "headword",
|
"matchMode": "headword",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
#* Name of a btop++/bpytop/bashtop formatted ".theme" file, "Default" and "TTY" for builtin themes.
|
#* Name of a btop++/bpytop/bashtop formatted ".theme" file, "Default" and "TTY" for builtin themes.
|
||||||
#* Themes should be placed in "../share/btop/themes" relative to binary or "$HOME/.config/btop/themes"
|
#* Themes should be placed in "../share/btop/themes" relative to binary or "$HOME/.config/btop/themes"
|
||||||
color_theme = "/Users/sudacode/.config/btop/themes/catppuccin_macchiato.theme"
|
color_theme = "/home/sudacode/.config/btop/themes/catppuccin_macchiato.theme"
|
||||||
|
|
||||||
#* If the theme set background should be shown, set to False if you want terminal background transparency.
|
#* If the theme set background should be shown, set to False if you want terminal background transparency.
|
||||||
theme_background = true
|
theme_background = true
|
||||||
@@ -40,6 +40,9 @@ graph_symbol = "braille"
|
|||||||
# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
|
# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
|
||||||
graph_symbol_cpu = "default"
|
graph_symbol_cpu = "default"
|
||||||
|
|
||||||
|
# Graph symbol to use for graphs in gpu box, "default", "braille", "block" or "tty".
|
||||||
|
graph_symbol_gpu = "default"
|
||||||
|
|
||||||
# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
|
# Graph symbol to use for graphs in cpu box, "default", "braille", "block" or "tty".
|
||||||
graph_symbol_mem = "default"
|
graph_symbol_mem = "default"
|
||||||
|
|
||||||
@@ -103,6 +106,9 @@ cpu_graph_upper = "Auto"
|
|||||||
#* Select from a list of detected attributes from the options menu.
|
#* Select from a list of detected attributes from the options menu.
|
||||||
cpu_graph_lower = "Auto"
|
cpu_graph_lower = "Auto"
|
||||||
|
|
||||||
|
#* If gpu info should be shown in the cpu box. Available values = "Auto", "On" and "Off".
|
||||||
|
show_gpu_info = "Auto"
|
||||||
|
|
||||||
#* Toggles if the lower CPU graph should be inverted.
|
#* Toggles if the lower CPU graph should be inverted.
|
||||||
cpu_invert_lower = true
|
cpu_invert_lower = true
|
||||||
|
|
||||||
@@ -142,6 +148,9 @@ base_10_sizes = false
|
|||||||
#* Show CPU frequency.
|
#* Show CPU frequency.
|
||||||
show_cpu_freq = true
|
show_cpu_freq = true
|
||||||
|
|
||||||
|
#* How to calculate CPU frequency, available values: "first", "range", "lowest", "highest" and "average".
|
||||||
|
freq_mode = "first"
|
||||||
|
|
||||||
#* Draw a clock at top of screen, formatting according to strftime, empty string to disable.
|
#* Draw a clock at top of screen, formatting according to strftime, empty string to disable.
|
||||||
#* Special formatting: /host = hostname | /user = username | /uptime = system uptime
|
#* Special formatting: /host = hostname | /user = username | /uptime = system uptime
|
||||||
clock_format = "%X"
|
clock_format = "%X"
|
||||||
@@ -231,3 +240,33 @@ log_level = "WARNING"
|
|||||||
|
|
||||||
#* Automatically save current settings to config file on exit.
|
#* Automatically save current settings to config file on exit.
|
||||||
save_config_on_exit = true
|
save_config_on_exit = true
|
||||||
|
|
||||||
|
#* Measure PCIe throughput on NVIDIA cards, may impact performance on certain cards.
|
||||||
|
nvml_measure_pcie_speeds = true
|
||||||
|
|
||||||
|
#* Measure PCIe throughput on AMD cards, may impact performance on certain cards.
|
||||||
|
rsmi_measure_pcie_speeds = true
|
||||||
|
|
||||||
|
#* Horizontally mirror the GPU graph.
|
||||||
|
gpu_mirror_graph = true
|
||||||
|
|
||||||
|
#* Set which GPU vendors to show. Available values are "nvidia amd intel"
|
||||||
|
shown_gpus = "nvidia amd intel"
|
||||||
|
|
||||||
|
#* Custom gpu0 model name, empty string to disable.
|
||||||
|
custom_gpu_name0 = ""
|
||||||
|
|
||||||
|
#* Custom gpu1 model name, empty string to disable.
|
||||||
|
custom_gpu_name1 = ""
|
||||||
|
|
||||||
|
#* Custom gpu2 model name, empty string to disable.
|
||||||
|
custom_gpu_name2 = ""
|
||||||
|
|
||||||
|
#* Custom gpu3 model name, empty string to disable.
|
||||||
|
custom_gpu_name3 = ""
|
||||||
|
|
||||||
|
#* Custom gpu4 model name, empty string to disable.
|
||||||
|
custom_gpu_name4 = ""
|
||||||
|
|
||||||
|
#* Custom gpu5 model name, empty string to disable.
|
||||||
|
custom_gpu_name5 = ""
|
||||||
|
|||||||
@@ -25,17 +25,17 @@ monitorv2 {
|
|||||||
position = 0x0
|
position = 0x0
|
||||||
scale = 1
|
scale = 1
|
||||||
vrr = 2
|
vrr = 2
|
||||||
cm = srgb
|
# cm = srgb
|
||||||
# Optional HDR settings
|
# Optional HDR settings
|
||||||
# cm = hdr
|
cm = hdr
|
||||||
# bitdepth = 10
|
bitdepth = 10
|
||||||
# sdr_min_luminance = 0.005
|
sdr_min_luminance = 0.005
|
||||||
# sdr_max_luminance = 200
|
sdr_max_luminance = 200
|
||||||
# min_luminance = 0
|
min_luminance = 0
|
||||||
# max_luminance = 1000
|
max_luminance = 1000
|
||||||
# max_avg_luminance = 200
|
max_avg_luminance = 200
|
||||||
# sdrbrightness = 1.2
|
sdrbrightness = 1.2
|
||||||
# sdrsaturation = 0.98
|
sdrsaturation = 0.98
|
||||||
}
|
}
|
||||||
|
|
||||||
source = ~/.config/hypr/keybindings.conf
|
source = ~/.config/hypr/keybindings.conf
|
||||||
@@ -252,7 +252,7 @@ render {
|
|||||||
}
|
}
|
||||||
|
|
||||||
misc {
|
misc {
|
||||||
font_family = JetBrainsMono Nerd Font
|
font_family = Manrope ExtraLight Medium, JetBrainsMono Nerd Font, M PLUS 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# {{{ WORKSPACES - HANDLED IN WAYBAR CONFIG
|
# {{{ WORKSPACES - HANDLED IN WAYBAR CONFIG
|
||||||
|
|||||||
106
.config/hypr/hyprlock.conf
Normal file
106
.config/hypr/hyprlock.conf
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
# sample hyprlock.conf
|
||||||
|
# for more configuration options, refer https://wiki.hyprland.org/Hypr-Ecosystem/hyprlock
|
||||||
|
#
|
||||||
|
# rendered text in all widgets supports pango markup (e.g. <b> or <i> tags)
|
||||||
|
# ref. https://wiki.hyprland.org/Hypr-Ecosystem/hyprlock/#general-remarks
|
||||||
|
#
|
||||||
|
# shortcuts to clear password buffer: ESC, Ctrl+U, Ctrl+Backspace
|
||||||
|
#
|
||||||
|
# you can get started by copying this config to ~/.config/hypr/hyprlock.conf
|
||||||
|
#
|
||||||
|
|
||||||
|
$font = Manrope ExtraLight
|
||||||
|
|
||||||
|
general {
|
||||||
|
hide_cursor = false
|
||||||
|
}
|
||||||
|
|
||||||
|
# uncomment to enable fingerprint authentication
|
||||||
|
auth {
|
||||||
|
fingerprint {
|
||||||
|
enabled = true
|
||||||
|
ready_message = Scan fingerprint to unlock
|
||||||
|
present_message = Scanning...
|
||||||
|
retry_delay = 250 # in milliseconds
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
animations {
|
||||||
|
enabled = true
|
||||||
|
bezier = linear, 1, 1, 0, 0
|
||||||
|
animation = fadeIn, 1, 5, linear
|
||||||
|
animation = fadeOut, 1, 5, linear
|
||||||
|
animation = inputFieldDots, 1, 2, linear
|
||||||
|
}
|
||||||
|
|
||||||
|
background {
|
||||||
|
monitor =
|
||||||
|
path = screenshot
|
||||||
|
blur_passes = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
input-field {
|
||||||
|
monitor =
|
||||||
|
size = 20%, 5%
|
||||||
|
outline_thickness = 3
|
||||||
|
inner_color = rgba(0, 0, 0, 0.0) # no fill
|
||||||
|
|
||||||
|
outer_color = rgba(33ccffee) rgba(00ff99ee) 45deg
|
||||||
|
check_color = rgba(00ff99ee) rgba(ff6633ee) 120deg
|
||||||
|
fail_color = rgba(ff6633ee) rgba(ff0066ee) 40deg
|
||||||
|
|
||||||
|
font_color = rgb(143, 143, 143)
|
||||||
|
fade_on_empty = false
|
||||||
|
rounding = 15
|
||||||
|
|
||||||
|
font_family = $font
|
||||||
|
placeholder_text = Input password...
|
||||||
|
fail_text = $PAMFAIL
|
||||||
|
|
||||||
|
# uncomment to use a letter instead of a dot to indicate the typed password
|
||||||
|
# dots_text_format = *
|
||||||
|
# dots_size = 0.4
|
||||||
|
dots_spacing = 0.3
|
||||||
|
|
||||||
|
# uncomment to use an input indicator that does not show the password length (similar to swaylock's input indicator)
|
||||||
|
# hide_input = true
|
||||||
|
|
||||||
|
position = 0, -20
|
||||||
|
halign = center
|
||||||
|
valign = center
|
||||||
|
}
|
||||||
|
|
||||||
|
# TIME
|
||||||
|
label {
|
||||||
|
monitor =
|
||||||
|
text = $TIME # ref. https://wiki.hyprland.org/Hypr-Ecosystem/hyprlock/#variable-substitution
|
||||||
|
font_size = 90
|
||||||
|
font_family = $font
|
||||||
|
|
||||||
|
position = -30, 0
|
||||||
|
halign = right
|
||||||
|
valign = top
|
||||||
|
}
|
||||||
|
|
||||||
|
# DATE
|
||||||
|
label {
|
||||||
|
monitor =
|
||||||
|
text = cmd[update:60000] date +"%A, %d %B %Y" # update every 60 seconds
|
||||||
|
font_size = 25
|
||||||
|
font_family = $font
|
||||||
|
|
||||||
|
position = -30, -150
|
||||||
|
halign = right
|
||||||
|
valign = top
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
monitor =
|
||||||
|
text = $LAYOUT[en,ru]
|
||||||
|
font_size = 24
|
||||||
|
onclick = hyprctl switchxkblayout all next
|
||||||
|
|
||||||
|
position = 250, -20
|
||||||
|
halign = center
|
||||||
|
valign = center
|
||||||
|
}
|
||||||
10
.config/hypr/hyprqt6engine.conf
Normal file
10
.config/hypr/hyprqt6engine.conf
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
theme = {
|
||||||
|
color_schema = "/usr/share/themes/Colloid-Purple-Dark-Catppuccin"
|
||||||
|
icon_theme = "Colloid-Purple-Catppuccin-Dark"
|
||||||
|
style = "kvantum-dark"
|
||||||
|
font_fixed = "JetBrainsMono Nerd Font"
|
||||||
|
font_fixed_size = 12
|
||||||
|
font = "Manrope ExtraLight"
|
||||||
|
font_size = 12
|
||||||
|
}
|
||||||
|
|
||||||
@@ -112,7 +112,7 @@ bind = SUPER, m, exec, ~/.local/bin/mpv-add.sh
|
|||||||
|
|
||||||
bind = SUPER SHIFT, s, exec, slurp | grim -g - - | wl-copy
|
bind = SUPER SHIFT, s, exec, slurp | grim -g - - | wl-copy
|
||||||
bind = ,code:107, exec, ~/.local/bin/screenshot
|
bind = ,code:107, exec, ~/.local/bin/screenshot
|
||||||
bind = SHIFT ,code:107, exec, grim -g "$(hyprctl activewindow -j | jq -r '.at[0],.at[1],.size[0],.size[1]' | tr '\n' ' ' | awk '{print $1","$2" "$3"x"$4}')" - | wl-copy
|
bind = SHIFT ,code:107, exec, ~/.local/bin/screenshot-active-window.sh
|
||||||
bind = SUPER,code:107, exec, flameshot screen
|
bind = SUPER,code:107, exec, flameshot screen
|
||||||
bind = SUPER, o, exec, ~/.local/bin/ocr.sh
|
bind = SUPER, o, exec, ~/.local/bin/ocr.sh
|
||||||
bind = $mainMod, o, exec, ~/.local/bin/rofi-open tab
|
bind = $mainMod, o, exec, ~/.local/bin/rofi-open tab
|
||||||
|
|||||||
87
.config/hypr/windowrules.conf
Normal file
87
.config/hypr/windowrules.conf
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
# See https://wiki.hyprland.org/Configuring/Workspace-Rules/ for workspace rules
|
||||||
|
|
||||||
|
# {{{ Floating windows
|
||||||
|
windowrule = float on, match:class discord
|
||||||
|
windowrule = float on, match:class mpv
|
||||||
|
windowrule = float on, match:class anki
|
||||||
|
windowrule = float on, match:class steam
|
||||||
|
windowrule = float on, match:class python, match:title Import
|
||||||
|
windowrule = float on, match:class zenity, match:title Japanese Analysis
|
||||||
|
windowrule = float on, match:class zenity, match:title Japanese Assistant
|
||||||
|
windowrule = min_size 1600 600, match:class anki
|
||||||
|
windowrule = max_size 2222 1234, match:class anki
|
||||||
|
windowrule = no_focus on, no_follow_mouse 1, float on, no_shadow on, no_anim on, match:class ueberzugpp.*
|
||||||
|
|
||||||
|
windowrule = size 1920 1080, match:class anki
|
||||||
|
# windowrule = min_size 1600 600, match:class anki
|
||||||
|
# windowrule = max_size 2222 1234, match:class anki
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# {{{ Workspace assignments
|
||||||
|
windowrule = workspace 3 silent, match:class Cursor
|
||||||
|
windowrule = workspace 5 silent, match:class GameSentenceMiner
|
||||||
|
windowrule = workspace 5 silent, match:class com.obsproject.Studio
|
||||||
|
windowrule = workspace 6 silent, match:class gamescope
|
||||||
|
windowrule = workspace 8 silent, match:class anki
|
||||||
|
windowrule = workspace 9 silent, match:class steam
|
||||||
|
windowrule = workspace 10 silent, match:class discord
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# {{{ Center floating windows
|
||||||
|
windowrule = match:float 1, match:class discord, center on
|
||||||
|
windowrule = match:float 1, match:class anki, center on
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# {{{ Opacity rules
|
||||||
|
# windowrule = opacity 0.88, match:class .* fullscreen:0
|
||||||
|
windowrule = opacity 1.0 override, match:class mpv
|
||||||
|
windowrule = opacity 1.0 override, match:class ^(remote-viewer)$
|
||||||
|
windowrule = opacity 1.0 override, match:class com.obsproject.Studio
|
||||||
|
windowrule = opacity 1.0 override, match:title (.*)(- YouTube(.*))
|
||||||
|
windowrule = opacity 1.0 override, match:class zen, match:title (.*)YouTube TV(.*)
|
||||||
|
windowrule = opacity 1.0 override, match:class anki
|
||||||
|
windowrule = opacity 1.0 override, match:title (.*)asbplayer
|
||||||
|
windowrule = opacity 1.0 override, match:class dolphin-emu
|
||||||
|
windowrule = opacity 1.0 override, match:class steam_app_default
|
||||||
|
windowrule = opacity 1.0 override, match:class steam_app.*
|
||||||
|
windowrule = opacity 1.0 override, match:class Rustdesk
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# {{{ Misc
|
||||||
|
windowrule = tile on, match:title (.*)asbplayer
|
||||||
|
# windowrule = size 2118 1182, match:class anki
|
||||||
|
# Ignore maximize requests from apps. You'll probably like this.
|
||||||
|
windowrule = suppress_event maximize, match:class .*
|
||||||
|
# Fix some dragging issues with XWayland
|
||||||
|
windowrule = no_focus on,match:class ^$,match:title ^$,match:xwayland 1,match:float 1,match:fullscreen 0,match:pin 0
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# {{{ Screen sharing workaround: https://wiki.hyprland.org/Useful-Utilities/Screen-Sharing/#xwayland
|
||||||
|
windowrule = opacity 0.0 override, match:class ^(xwaylandvideobridge)$
|
||||||
|
windowrule = no_anim on, match:class ^(xwaylandvideobridge)$
|
||||||
|
windowrule = no_initial_focus on, match:class ^(xwaylandvideobridge)$
|
||||||
|
windowrule = max_size 1 1, match:class ^(xwaylandvideobridge)$
|
||||||
|
windowrule = no_blur on, match:class ^(xwaylandvideobridge)$
|
||||||
|
windowrule = no_focus on, match:class ^(xwaylandvideobridge)$
|
||||||
|
# }}}
|
||||||
|
|
||||||
|
# {{{ GSM Overlay and LunaTranslator tweaks
|
||||||
|
windowrule = float on, match:class gsm_overlay
|
||||||
|
windowrule = border_size 0, match:class gsm_overlay
|
||||||
|
windowrule = xray off, match:class gsm_overlay
|
||||||
|
windowrule = no_shadow on, match:class gsm_overlay
|
||||||
|
windowrule = no_blur on, match:class gsm_overlay
|
||||||
|
windowrule = opacity 1.0 override, match:title LunaTranslator
|
||||||
|
windowrule = border_size 0, match:title LunaTranslator
|
||||||
|
windowrule = stay_focused on, match:class gsm_overlay
|
||||||
|
# windowrule = fullscreen_state 2, match:class gsm_overlay
|
||||||
|
|
||||||
|
windowrule = float on, match:class SubMiner
|
||||||
|
windowrule = border_size 0, match:class SubMiner
|
||||||
|
windowrule = xray off override, match:class SubMiner
|
||||||
|
windowrule = no_shadow on, match:class SubMiner
|
||||||
|
windowrule = no_blur on, match:class SubMiner
|
||||||
|
windowrule = allows_input offf, match:class SubMiner
|
||||||
|
windowrule = border_size 0, match:class steam_app_1277940
|
||||||
|
# {{{ GSM Overlay and LunaTranslator tweaks
|
||||||
|
|
||||||
Submodule .config/mpv-modules/ModernZ updated: d917f6e253...74885f5dec
Submodule .config/mpv-modules/mpv-anilist-updater updated: ace1f74822...2177ee4711
@@ -130,13 +130,23 @@ scale=ewa_lanczos
|
|||||||
target-peak=800
|
target-peak=800
|
||||||
|
|
||||||
[hdr]
|
[hdr]
|
||||||
target-colorspace-hint=yes
|
profile-desc=HDR content
|
||||||
|
profile-cond=get("video-params/gamma", "") == "pq" or get("video-params/gamma", "") == "hlg"
|
||||||
|
profile-restore=copy
|
||||||
tone-mapping-param=0.5
|
tone-mapping-param=0.5
|
||||||
tone-mapping-max-boost=2.0
|
tone-mapping-max-boost=2.0
|
||||||
allow-delayed-peak-detect=yes
|
allow-delayed-peak-detect=yes
|
||||||
# For SDR content on HDR display (or vice versa)
|
# For SDR content on HDR display (or vice versa)
|
||||||
icc-profile-auto=yes
|
icc-profile-auto=yes
|
||||||
|
|
||||||
|
[sdr]
|
||||||
|
profile-desc=SDR content
|
||||||
|
profile-cond=get("video-params/gamma", "") ~= "pq" and get("video-params/gamma", "") ~= "hlg"
|
||||||
|
profile-restore=copy
|
||||||
|
# put SDR-only tweaks here
|
||||||
|
deband=yes
|
||||||
|
target-colorspace-hint=auto
|
||||||
|
|
||||||
[svp]
|
[svp]
|
||||||
interpolation=no
|
interpolation=no
|
||||||
input-ipc-server=/tmp/mpvsocket
|
input-ipc-server=/tmp/mpvsocket
|
||||||
|
|||||||
@@ -157,14 +157,25 @@ scale=ewa_lanczos
|
|||||||
target-peak=800
|
target-peak=800
|
||||||
# hdr-tone-mapping=bt.2390
|
# hdr-tone-mapping=bt.2390
|
||||||
|
|
||||||
# HDR profile
|
|
||||||
[hdr]
|
[hdr]
|
||||||
target-colorspace-hint=yes
|
profile-desc=HDR content
|
||||||
|
profile-cond=get("video-params/gamma", "") == "pq" or get("video-params/gamma", "") == "hlg"
|
||||||
|
profile-restore=copy
|
||||||
tone-mapping-param=0.5
|
tone-mapping-param=0.5
|
||||||
tone-mapping-max-boost=2.0
|
tone-mapping-max-boost=2.0
|
||||||
allow-delayed-peak-detect=yes
|
allow-delayed-peak-detect=yes
|
||||||
|
# For SDR content on HDR display (or vice versa)
|
||||||
icc-profile-auto=yes
|
icc-profile-auto=yes
|
||||||
|
|
||||||
|
[sdr]
|
||||||
|
profile-desc=SDR content
|
||||||
|
profile-cond=get("video-params/gamma", "") ~= "pq" and get("video-params/gamma", "") ~= "hlg"
|
||||||
|
profile-restore=copy
|
||||||
|
# put SDR-only tweaks here
|
||||||
|
deband=yes
|
||||||
|
target-colorspace-hint=auto
|
||||||
|
|
||||||
|
|
||||||
# SVP compatibility profile
|
# SVP compatibility profile
|
||||||
[svp]
|
[svp]
|
||||||
interpolation=no
|
interpolation=no
|
||||||
|
|||||||
@@ -493,6 +493,17 @@ slider {
|
|||||||
padding-bottom: 1rem;
|
padding-bottom: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.image {
|
.notification-default-action .notification-content .image,
|
||||||
padding-right: 0.5rem;
|
.notification .notification-content .image {
|
||||||
|
-gtk-icon-size: 64px;
|
||||||
|
min-width: 64px;
|
||||||
|
min-height: 64px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-default-action .notification-content .app-icon,
|
||||||
|
.notification .notification-content .app-icon {
|
||||||
|
-gtk-icon-size: 24px;
|
||||||
|
min-width: 24px;
|
||||||
|
min-height: 24px;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,12 +16,12 @@
|
|||||||
"custom/firefox-scroll",
|
"custom/firefox-scroll",
|
||||||
"hyprland/submap",
|
"hyprland/submap",
|
||||||
],
|
],
|
||||||
// "modules-center": ["hyprland/window"],
|
"modules-center": ["hyprland/window"],
|
||||||
"modules-center": ["custom/notification"],
|
// "modules-center": ["custom/notification"],
|
||||||
"modules-right": [
|
"modules-right": [
|
||||||
// "hyprland/scratchpad",
|
// "hyprland/scratchpad",
|
||||||
// "idle_inhibitor",
|
// "idle_inhibitor",
|
||||||
// "custom/notification",
|
"custom/notification",
|
||||||
"custom/updates",
|
"custom/updates",
|
||||||
"custom/kernel",
|
"custom/kernel",
|
||||||
// "disk#ssd",
|
// "disk#ssd",
|
||||||
@@ -170,8 +170,8 @@
|
|||||||
"pulseaudio": {
|
"pulseaudio": {
|
||||||
"scroll-step": 2,
|
"scroll-step": 2,
|
||||||
"format": "{volume}% {icon} {format_source}",
|
"format": "{volume}% {icon} {format_source}",
|
||||||
"format-bluetooth": "{volume}% {icon} {format_source}",
|
"format-bluetooth": "{volume}% {icon} {format_source}",
|
||||||
"format-bluetooth-muted": " {icon} {format_source}",
|
"format-bluetooth-muted": " {icon} {format_source}",
|
||||||
"format-muted": " {format_source}",
|
"format-muted": " {format_source}",
|
||||||
"format-source": "{volume}% ",
|
"format-source": "{volume}% ",
|
||||||
"format-source-muted": "",
|
"format-source-muted": "",
|
||||||
@@ -192,8 +192,8 @@
|
|||||||
"on-click-right": "qpwgraph",
|
"on-click-right": "qpwgraph",
|
||||||
"scroll-step": 2,
|
"scroll-step": 2,
|
||||||
"format": "{volume}% {icon} {format_source}",
|
"format": "{volume}% {icon} {format_source}",
|
||||||
"format-bluetooth": "{volume}% {icon} {format_source}",
|
"format-bluetooth": "{volume}% {icon} {format_source}",
|
||||||
"format-bluetooth-muted": " {icon} {format_source}",
|
"format-bluetooth-muted": " {icon} {format_source}",
|
||||||
"format-muted": " {format_source}",
|
"format-muted": " {format_source}",
|
||||||
"format-source": "{volume}% ",
|
"format-source": "{volume}% ",
|
||||||
"format-source-muted": "",
|
"format-source-muted": "",
|
||||||
@@ -241,8 +241,8 @@
|
|||||||
"hyprland/window": {
|
"hyprland/window": {
|
||||||
"max-length": 88,
|
"max-length": 88,
|
||||||
"rewrite": {
|
"rewrite": {
|
||||||
"(.*) - YouTube(.*)": "",
|
// "(.*) - YouTube(.*)": "$1",
|
||||||
"(.*) - mpv(.*)": "",
|
// "(.*) - mpv(.*)": "$1",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"custom/updates": {
|
"custom/updates": {
|
||||||
@@ -273,7 +273,7 @@
|
|||||||
"format": "{}",
|
"format": "{}",
|
||||||
"max-length": 35,
|
"max-length": 35,
|
||||||
"on-click": "mpc toggle",
|
"on-click": "mpc toggle",
|
||||||
"on-click-right": "ghostty --initial-command=ncmpcpp",
|
"on-click-right": "ghostty --initial-command=rmpc",
|
||||||
"on-scroll-down": "mpc volume -5",
|
"on-scroll-down": "mpc volume -5",
|
||||||
"on-scroll-up": "mpc volume +5",
|
"on-scroll-up": "mpc volume +5",
|
||||||
"hide-empty-text": true,
|
"hide-empty-text": true,
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
|
|
||||||
* {
|
* {
|
||||||
border: none;
|
border: none;
|
||||||
font-family: "JetBrainsMono Nerd Font", "Font Awesome", "Noto Sans CJK JP";
|
font-family: "Manrope ExtraLight Medium","JetBrainsMono Nerd Font", "Font Awesome", "M PLUS 1 Medium";
|
||||||
font-size: 13px;
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
border-radius: 0.69em;
|
border-radius: 0.69em;
|
||||||
}
|
}
|
||||||
@@ -19,9 +20,12 @@ window#waybar.hidden {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#window {
|
#window {
|
||||||
background-color: inherit;
|
background-color: transparent;
|
||||||
padding: 0 10px;
|
color: @subtext1;
|
||||||
|
padding: 0 16px;
|
||||||
margin: 5px 1px;
|
margin: 5px 1px;
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
tooltip {
|
tooltip {
|
||||||
@@ -126,7 +130,7 @@ button:hover {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.modules-right {
|
.modules-right {
|
||||||
padding-left: 1px;
|
padding-left: 5px;
|
||||||
}
|
}
|
||||||
.modules-left {
|
.modules-left {
|
||||||
padding-right: 4px;
|
padding-right: 4px;
|
||||||
@@ -192,6 +196,7 @@ label:focus {
|
|||||||
background-color: @maroon;
|
background-color: @maroon;
|
||||||
color: @mantle;
|
color: @mantle;
|
||||||
min-width: 40px;
|
min-width: 40px;
|
||||||
|
padding: 0 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#pulseaudio {
|
#pulseaudio {
|
||||||
@@ -322,9 +327,10 @@ label:focus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#custom-notification {
|
#custom-notification {
|
||||||
font-family: "JetBrainsMono Nerd Font";
|
font-family: inherit;
|
||||||
margin: 0px 0.88em;
|
padding: 0 10px;
|
||||||
|
margin: 5px 1px;
|
||||||
min-width: 1em;
|
min-width: 1em;
|
||||||
background-color: transparent;
|
background-color: @maroon;
|
||||||
color: @maroon;
|
color: @mantle;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
../../../projects/scripts/waybar/wttr.sh
|
|
||||||
167
.config/waybar/scripts/wttr.sh
Executable file
167
.config/waybar/scripts/wttr.sh
Executable file
@@ -0,0 +1,167 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
location_input="${1:-Los_Angeles}"
|
||||||
|
location_query="${location_input//_/ }"
|
||||||
|
cache_dir="${XDG_CACHE_HOME:-$HOME/.cache}/waybar"
|
||||||
|
cache_key="${location_input//[^[:alnum:]_.-]/_}"
|
||||||
|
geo_cache_file="$cache_dir/weather-geo-${cache_key}.json"
|
||||||
|
weather_cache_file="$cache_dir/weather-${cache_key}.json"
|
||||||
|
|
||||||
|
json_escape() {
|
||||||
|
jq -Rsa .
|
||||||
|
}
|
||||||
|
|
||||||
|
emit_json() {
|
||||||
|
local text="$1"
|
||||||
|
local tooltip="$2"
|
||||||
|
|
||||||
|
printf '{"text":%s,"tooltip":%s}\n' \
|
||||||
|
"$(printf '%s' "$text" | json_escape)" \
|
||||||
|
"$(printf '%s' "$tooltip" | json_escape)"
|
||||||
|
}
|
||||||
|
|
||||||
|
emit_cached_or_error() {
|
||||||
|
if [[ -f "$weather_cache_file" ]]; then
|
||||||
|
cat "$weather_cache_file"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
emit_json "weather unavailable" "weather service unavailable"
|
||||||
|
}
|
||||||
|
|
||||||
|
url_encode() {
|
||||||
|
jq -rn --arg value "$1" '$value | @uri'
|
||||||
|
}
|
||||||
|
|
||||||
|
weather_icon() {
|
||||||
|
local code="$1"
|
||||||
|
local is_day="$2"
|
||||||
|
|
||||||
|
case "$code" in
|
||||||
|
0) [[ "$is_day" == "1" ]] && printf '☀' || printf '☾' ;;
|
||||||
|
1 | 2) [[ "$is_day" == "1" ]] && printf '⛅' || printf '☁' ;;
|
||||||
|
3) printf '☁' ;;
|
||||||
|
45 | 48) printf '🌫' ;;
|
||||||
|
51 | 53 | 55 | 56 | 57) printf '🌦' ;;
|
||||||
|
61 | 63 | 65 | 66 | 67 | 80 | 81 | 82) printf '🌧' ;;
|
||||||
|
71 | 73 | 75 | 77 | 85 | 86) printf '🌨' ;;
|
||||||
|
95 | 96 | 99) printf '⛈' ;;
|
||||||
|
*) printf '☁' ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
weather_description() {
|
||||||
|
case "$1" in
|
||||||
|
0) printf 'Clear sky' ;;
|
||||||
|
1) printf 'Mainly clear' ;;
|
||||||
|
2) printf 'Partly cloudy' ;;
|
||||||
|
3) printf 'Overcast' ;;
|
||||||
|
45 | 48) printf 'Fog' ;;
|
||||||
|
51) printf 'Light drizzle' ;;
|
||||||
|
53) printf 'Drizzle' ;;
|
||||||
|
55) printf 'Dense drizzle' ;;
|
||||||
|
56 | 57) printf 'Freezing drizzle' ;;
|
||||||
|
61) printf 'Slight rain' ;;
|
||||||
|
63) printf 'Rain' ;;
|
||||||
|
65) printf 'Heavy rain' ;;
|
||||||
|
66 | 67) printf 'Freezing rain' ;;
|
||||||
|
71) printf 'Slight snow' ;;
|
||||||
|
73) printf 'Snow' ;;
|
||||||
|
75) printf 'Heavy snow' ;;
|
||||||
|
77) printf 'Snow grains' ;;
|
||||||
|
80) printf 'Rain showers' ;;
|
||||||
|
81) printf 'Rain showers' ;;
|
||||||
|
82) printf 'Heavy rain showers' ;;
|
||||||
|
85 | 86) printf 'Snow showers' ;;
|
||||||
|
95) printf 'Thunderstorm' ;;
|
||||||
|
96 | 99) printf 'Thunderstorm with hail' ;;
|
||||||
|
*) printf 'Weather unavailable' ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
load_geocode() {
|
||||||
|
if [[ -f "$geo_cache_file" ]]; then
|
||||||
|
cat "$geo_cache_file"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local encoded_query response
|
||||||
|
encoded_query="$(url_encode "$location_query")"
|
||||||
|
response="$(
|
||||||
|
curl \
|
||||||
|
--silent \
|
||||||
|
--show-error \
|
||||||
|
--fail \
|
||||||
|
--max-time 10 \
|
||||||
|
"https://geocoding-api.open-meteo.com/v1/search?name=${encoded_query}&count=1&language=en&format=json" 2>/dev/null || true
|
||||||
|
)"
|
||||||
|
|
||||||
|
if jq -e '.results[0] | .name and .latitude and .longitude and .timezone' >/dev/null <<<"$response"; then
|
||||||
|
jq -c '.results[0] | {name, admin1, country, latitude, longitude, timezone}' <<<"$response" | tee "$geo_cache_file"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch_weather() {
|
||||||
|
local latitude="$1"
|
||||||
|
local longitude="$2"
|
||||||
|
local timezone="$3"
|
||||||
|
|
||||||
|
curl \
|
||||||
|
--silent \
|
||||||
|
--show-error \
|
||||||
|
--fail \
|
||||||
|
--max-time 10 \
|
||||||
|
"https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}¤t=temperature_2m,relative_humidity_2m,apparent_temperature,weather_code,wind_speed_10m,is_day&timezone=$(url_encode "$timezone")" 2>/dev/null || true
|
||||||
|
}
|
||||||
|
|
||||||
|
format_weather() {
|
||||||
|
local place_json="$1"
|
||||||
|
local weather_json="$2"
|
||||||
|
local name region place code is_day icon description temperature feels_like humidity wind text tooltip
|
||||||
|
|
||||||
|
name="$(jq -r '.name' <<<"$place_json")"
|
||||||
|
region="$(jq -r '.admin1 // empty' <<<"$place_json")"
|
||||||
|
place="$name"
|
||||||
|
if [[ -n "$region" ]]; then
|
||||||
|
place+=", $region"
|
||||||
|
fi
|
||||||
|
|
||||||
|
code="$(jq -r '.current.weather_code' <<<"$weather_json")"
|
||||||
|
is_day="$(jq -r '.current.is_day' <<<"$weather_json")"
|
||||||
|
icon="$(weather_icon "$code" "$is_day")"
|
||||||
|
description="$(weather_description "$code")"
|
||||||
|
temperature="$(jq -r '.current.temperature_2m | round | "\(.)"' <<<"$weather_json")"
|
||||||
|
feels_like="$(jq -r '.current.apparent_temperature | round | "\(.)"' <<<"$weather_json")"
|
||||||
|
humidity="$(jq -r '.current.relative_humidity_2m | round | "\(.)"' <<<"$weather_json")"
|
||||||
|
wind="$(jq -r '.current.wind_speed_10m | round | "\(.)"' <<<"$weather_json")"
|
||||||
|
|
||||||
|
text="${icon} ${temperature}°C"
|
||||||
|
tooltip="${place}: ${description}. Feels like ${feels_like}°C, humidity ${humidity}%, wind ${wind} km/h"
|
||||||
|
|
||||||
|
emit_json "$text" "$tooltip"
|
||||||
|
}
|
||||||
|
|
||||||
|
mkdir -p "$cache_dir"
|
||||||
|
|
||||||
|
if ! place_json="$(load_geocode)"; then
|
||||||
|
emit_cached_or_error
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
latitude="$(jq -r '.latitude' <<<"$place_json")"
|
||||||
|
longitude="$(jq -r '.longitude' <<<"$place_json")"
|
||||||
|
timezone="$(jq -r '.timezone' <<<"$place_json")"
|
||||||
|
|
||||||
|
weather_json="$(fetch_weather "$latitude" "$longitude" "$timezone")"
|
||||||
|
|
||||||
|
if jq -e '.current | .temperature_2m and .relative_humidity_2m and .apparent_temperature and .weather_code and .wind_speed_10m and .is_day' >/dev/null <<<"$weather_json"; then
|
||||||
|
format_weather "$place_json" "$weather_json" | tee "$weather_cache_file"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
emit_cached_or_error
|
||||||
17
projects/scripts/favorite-wallpaper.sh
Executable file
17
projects/scripts/favorite-wallpaper.sh
Executable file
@@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
HOME=/home/$USER
|
||||||
|
|
||||||
|
CURRENT="$(cat ~/.wallpaper)"
|
||||||
|
CURRENT="${CURRENT/\/\///}"
|
||||||
|
OUTPUT_DIR="/truenas/sudacode/pictures/wallpapers/"
|
||||||
|
|
||||||
|
cp "$CURRENT" "$HOME/Pictures/wallpapers/favorites/"
|
||||||
|
|
||||||
|
if cp "$CURRENT" "$OUTPUT_DIR"; then
|
||||||
|
notify-send "favorite-wallpaper" "Wallpaper saved to $OUTPUT_DIR"
|
||||||
|
else
|
||||||
|
notify-send "favorite-wallpaper" "Failed to saved wallpaper to $OUTPUT_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ft: sh
|
||||||
22
projects/scripts/hyprland-pin.sh
Executable file
22
projects/scripts/hyprland-pin.sh
Executable file
@@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
window_info=$(hyprctl activewindow -j)
|
||||||
|
read -r is_pinned window_class window_title <<< "$(echo "$window_info" | jq -r '[.pinned, .class, .title] | @tsv')"
|
||||||
|
|
||||||
|
hyprctl dispatch pin active
|
||||||
|
|
||||||
|
read -r window_x window_y window_w window_h <<< "$(echo "$window_info" | jq -r '[.at[0], .at[1], .size[0], .size[1]] | @tsv')"
|
||||||
|
|
||||||
|
screenshot=$(mktemp --suffix=.png)
|
||||||
|
grim -g "${window_x},${window_y} ${window_w}x${window_h}" "$screenshot"
|
||||||
|
|
||||||
|
if [ "$is_pinned" = "true" ]; then
|
||||||
|
status="Unpinned"
|
||||||
|
else
|
||||||
|
status="Pinned"
|
||||||
|
fi
|
||||||
|
|
||||||
|
notify-send -u low -i "$screenshot" "$status: $window_class" "$window_title"
|
||||||
|
rm -f "$screenshot"
|
||||||
|
|
||||||
|
# vim: set ft=sh
|
||||||
214
projects/scripts/popup-ai-chat.py
Executable file
214
projects/scripts/popup-ai-chat.py
Executable file
@@ -0,0 +1,214 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Popup AI chat assistant using rofi for input and OpenRouter for responses.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
|
API_URL = "https://openrouter.ai/api/v1/chat/completions"
|
||||||
|
MODEL = os.environ.get("OPENROUTER_MODEL", "openai/gpt-oss-120b:free")
|
||||||
|
APP_NAME = "Popup AI Chat"
|
||||||
|
SYSTEM_PROMPT = (
|
||||||
|
"You are a helpful AI assistant. Give direct, accurate answers. "
|
||||||
|
"Use concise formatting unless the user asks for depth."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def load_api_key() -> str:
|
||||||
|
"""Load OpenRouter API key from env or fallback file."""
|
||||||
|
api_key = os.environ.get("OPENROUTER_API_KEY", "").strip()
|
||||||
|
if api_key:
|
||||||
|
return api_key
|
||||||
|
|
||||||
|
key_file = os.path.expanduser("~/.openrouterapikey")
|
||||||
|
if os.path.isfile(key_file):
|
||||||
|
with open(key_file, "r", encoding="utf-8") as handle:
|
||||||
|
return handle.read().strip()
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
def show_error(message: str) -> None:
|
||||||
|
"""Display an error message via zenity."""
|
||||||
|
subprocess.run(
|
||||||
|
["zenity", "--error", "--title", "Error", "--text", message],
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def check_dependencies() -> bool:
|
||||||
|
"""Validate required desktop tools are available."""
|
||||||
|
missing = [cmd for cmd in ("rofi", "zenity") if shutil.which(cmd) is None]
|
||||||
|
if not missing:
|
||||||
|
return True
|
||||||
|
|
||||||
|
message = f"Missing required command(s): {', '.join(missing)}"
|
||||||
|
if shutil.which("zenity") is not None:
|
||||||
|
show_error(message)
|
||||||
|
else:
|
||||||
|
print(f"Error: {message}", file=sys.stderr)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def get_rofi_input() -> Optional[str]:
|
||||||
|
"""Ask for user input through rofi."""
|
||||||
|
result = subprocess.run(
|
||||||
|
["rofi", "-dmenu", "-i", "-p", "Ask AI"],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
prompt = result.stdout.strip()
|
||||||
|
return prompt or None
|
||||||
|
|
||||||
|
|
||||||
|
def show_notification(body: str) -> None:
|
||||||
|
"""Show processing notification when notify-send exists."""
|
||||||
|
if shutil.which("notify-send") is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
subprocess.Popen(
|
||||||
|
["notify-send", "-t", "0", "-a", APP_NAME, "Processing...", body],
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def close_notification() -> None:
|
||||||
|
"""Close the processing notification if one was sent."""
|
||||||
|
if shutil.which("pkill") is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
subprocess.run(
|
||||||
|
["pkill", "-f", "notify-send.*Processing..."],
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def make_api_request(api_key: str, messages: list[dict[str, str]]) -> dict:
|
||||||
|
"""Send chat request to OpenRouter and return JSON payload."""
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": f"Bearer {api_key}",
|
||||||
|
"HTTP-Referer": "https://github.com/sudacode/scripts",
|
||||||
|
"X-Title": APP_NAME,
|
||||||
|
}
|
||||||
|
payload = {
|
||||||
|
"model": MODEL,
|
||||||
|
"messages": messages,
|
||||||
|
"temperature": 0.7,
|
||||||
|
}
|
||||||
|
response = requests.post(API_URL, headers=headers, json=payload, timeout=90)
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
|
||||||
|
def display_result(content: str) -> None:
|
||||||
|
"""Display model output in a text window."""
|
||||||
|
subprocess.run(
|
||||||
|
[
|
||||||
|
"zenity",
|
||||||
|
"--text-info",
|
||||||
|
"--title",
|
||||||
|
"AI Response",
|
||||||
|
"--width",
|
||||||
|
"900",
|
||||||
|
"--height",
|
||||||
|
"700",
|
||||||
|
"--font",
|
||||||
|
"monospace 11",
|
||||||
|
],
|
||||||
|
input=content,
|
||||||
|
text=True,
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def ask_follow_up() -> bool:
|
||||||
|
"""Ask if the user wants to continue the conversation."""
|
||||||
|
result = subprocess.run(
|
||||||
|
[
|
||||||
|
"zenity",
|
||||||
|
"--question",
|
||||||
|
"--title",
|
||||||
|
APP_NAME,
|
||||||
|
"--text",
|
||||||
|
"Ask a follow-up question?",
|
||||||
|
"--ok-label",
|
||||||
|
"Ask Follow-up",
|
||||||
|
"--cancel-label",
|
||||||
|
"Close",
|
||||||
|
],
|
||||||
|
stderr=subprocess.DEVNULL,
|
||||||
|
)
|
||||||
|
return result.returncode == 0
|
||||||
|
|
||||||
|
|
||||||
|
def extract_content(response: dict) -> str:
|
||||||
|
"""Extract assistant response from OpenRouter payload."""
|
||||||
|
if "error" in response:
|
||||||
|
message = response["error"].get("message", "Unknown API error")
|
||||||
|
raise ValueError(message)
|
||||||
|
|
||||||
|
try:
|
||||||
|
content = response["choices"][0]["message"]["content"]
|
||||||
|
except (KeyError, IndexError, TypeError) as exc:
|
||||||
|
raise ValueError("Failed to parse API response") from exc
|
||||||
|
|
||||||
|
if not content:
|
||||||
|
raise ValueError("Empty response from API")
|
||||||
|
|
||||||
|
return content
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
if not check_dependencies():
|
||||||
|
return 1
|
||||||
|
|
||||||
|
api_key = load_api_key()
|
||||||
|
if not api_key:
|
||||||
|
show_error("OPENROUTER_API_KEY environment variable is not set.")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
history: list[dict[str, str]] = [{"role": "system", "content": SYSTEM_PROMPT}]
|
||||||
|
|
||||||
|
while True:
|
||||||
|
user_input = get_rofi_input()
|
||||||
|
if not user_input:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
request_messages = history + [{"role": "user", "content": user_input}]
|
||||||
|
show_notification(f"Thinking: {user_input[:60]}...")
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = make_api_request(api_key, request_messages)
|
||||||
|
content = extract_content(response)
|
||||||
|
except requests.RequestException as exc:
|
||||||
|
show_error(f"API request failed: {exc}")
|
||||||
|
return 1
|
||||||
|
except ValueError as exc:
|
||||||
|
show_error(str(exc))
|
||||||
|
return 1
|
||||||
|
except Exception as exc: # pragma: no cover
|
||||||
|
show_error(f"Unexpected error: {exc}")
|
||||||
|
return 1
|
||||||
|
finally:
|
||||||
|
close_notification()
|
||||||
|
|
||||||
|
history.append({"role": "user", "content": user_input})
|
||||||
|
history.append({"role": "assistant", "content": content})
|
||||||
|
|
||||||
|
display_result(content)
|
||||||
|
if not ask_follow_up():
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
181
projects/scripts/popup-ai-translator.sh
Executable file
181
projects/scripts/popup-ai-translator.sh
Executable file
@@ -0,0 +1,181 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Japanese Learning Assistant using OpenRouter API
|
||||||
|
# Uses Google Gemini Flash 2.0 for AJATT-aligned Japanese analysis
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
OPENROUTER_API_KEY="${OPENROUTER_API_KEY:-}"
|
||||||
|
MODEL="${OPENROUTER_MODEL:-google/gemini-2.0-flash-001}"
|
||||||
|
API_URL="https://openrouter.ai/api/v1/chat/completions"
|
||||||
|
|
||||||
|
if [[ -z $OPENROUTER_API_KEY && -f "$HOME/.openrouterapikey" ]]; then
|
||||||
|
OPENROUTER_API_KEY="$(<"$HOME/.openrouterapikey")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# System prompt for Japanese learning
|
||||||
|
SYSTEM_PROMPT='You are my Japanese-learning assistant. Help me acquire Japanese through deep, AJATT-aligned analysis.
|
||||||
|
|
||||||
|
For every input, output exactly:
|
||||||
|
|
||||||
|
1. Japanese Input (Verbatim)
|
||||||
|
|
||||||
|
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. Word-by-Word Breakdown
|
||||||
|
|
||||||
|
For each unit:
|
||||||
|
|
||||||
|
- 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)
|
||||||
|
- Clean, structured formatting; calm, precise tone
|
||||||
|
- 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.'
|
||||||
|
|
||||||
|
# Check for API key
|
||||||
|
if [[ -z "$OPENROUTER_API_KEY" ]]; then
|
||||||
|
zenity --error --text="OPENROUTER_API_KEY environment variable is not set." --title="Error" 2>/dev/null
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get input from zenity
|
||||||
|
input=$(zenity --entry \
|
||||||
|
--title="Japanese Assistant" \
|
||||||
|
--text="Enter Japanese text to analyze:" \
|
||||||
|
--width=500 \
|
||||||
|
2>/dev/null)
|
||||||
|
|
||||||
|
# Exit if no input
|
||||||
|
if [[ -z "$input" ]]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Show loading notification
|
||||||
|
notify-send -t 0 -a "Japanese Assistant" "Processing..." "Analyzing: ${input:0:50}..." &
|
||||||
|
notif_pid=$!
|
||||||
|
|
||||||
|
# Escape special characters for JSON
|
||||||
|
escape_json() {
|
||||||
|
local str="$1"
|
||||||
|
str="${str//\\/\\\\}"
|
||||||
|
str="${str//\"/\\\"}"
|
||||||
|
str="${str//$'\n'/\\n}"
|
||||||
|
str="${str//$'\r'/\\r}"
|
||||||
|
str="${str//$'\t'/\\t}"
|
||||||
|
printf '%s' "$str"
|
||||||
|
}
|
||||||
|
|
||||||
|
escaped_input=$(escape_json "$input")
|
||||||
|
escaped_system=$(escape_json "$SYSTEM_PROMPT")
|
||||||
|
|
||||||
|
# Build JSON payload
|
||||||
|
json_payload=$(
|
||||||
|
cat <<EOF
|
||||||
|
{
|
||||||
|
"model": "$MODEL",
|
||||||
|
"messages": [
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "$escaped_system"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": "$escaped_input"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"temperature": 0.7
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
# Make API request
|
||||||
|
response=$(curl -s -X POST "$API_URL" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer $OPENROUTER_API_KEY" \
|
||||||
|
-H "HTTP-Referer: https://github.com/sudacode/scripts" \
|
||||||
|
-H "X-Title: Japanese Learning Assistant" \
|
||||||
|
-d "$json_payload")
|
||||||
|
|
||||||
|
# Close loading notification
|
||||||
|
pkill -f "notify-send.*Processing.*Analyzing" 2>/dev/null
|
||||||
|
|
||||||
|
# Check for errors
|
||||||
|
if [[ -z "$response" ]]; then
|
||||||
|
zenity --error --text="No response from API" --title="Error" 2>/dev/null
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Parse response and extract content using Python (handles Unicode properly)
|
||||||
|
result=$(echo "$response" | python3 -c "
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = json.load(sys.stdin)
|
||||||
|
|
||||||
|
if 'error' in data:
|
||||||
|
err = data['error'].get('message', 'Unknown error')
|
||||||
|
print(f'ERROR:{err}', end='')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
content = data.get('choices', [{}])[0].get('message', {}).get('content', '')
|
||||||
|
if not content:
|
||||||
|
print('ERROR:Failed to parse API response', end='')
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Decode any unicode escape sequences in the content
|
||||||
|
try:
|
||||||
|
content = content.encode('utf-8').decode('unicode_escape').encode('latin-1').decode('utf-8')
|
||||||
|
except:
|
||||||
|
pass # Keep original if decoding fails
|
||||||
|
|
||||||
|
print(content, end='')
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
print(f'ERROR:Invalid JSON response: {e}', end='')
|
||||||
|
sys.exit(1)
|
||||||
|
except Exception as e:
|
||||||
|
print(f'ERROR:{e}', end='')
|
||||||
|
sys.exit(1)
|
||||||
|
")
|
||||||
|
|
||||||
|
# Check for errors from Python parsing
|
||||||
|
if [[ "$result" == ERROR:* ]]; then
|
||||||
|
error_msg="${result#ERROR:}"
|
||||||
|
zenity --error --text="$error_msg" --title="Error" 2>/dev/null
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
content="$result"
|
||||||
|
|
||||||
|
if [[ -z "$content" ]]; then
|
||||||
|
zenity --error --text="Empty response from API" --title="Error" 2>/dev/null
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Display result in zenity
|
||||||
|
zenity --text-info \
|
||||||
|
--title="Japanese Analysis" \
|
||||||
|
--width=800 \
|
||||||
|
--height=600 \
|
||||||
|
--font="monospace" \
|
||||||
|
<<<"$content" 2>/dev/null
|
||||||
7
projects/scripts/screenshot-active-window.sh
Executable file
7
projects/scripts/screenshot-active-window.sh
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
tmpfile=$(mktemp /tmp/screenshot-XXXXXX.png)
|
||||||
|
grim -g "$(hyprctl activewindow -j | jq -r '.at[0],.at[1],.size[0],.size[1]' | tr '\n' ' ' | awk '{print $1","$2" "$3"x"$4}')" "$tmpfile"
|
||||||
|
wl-copy < "$tmpfile"
|
||||||
|
notify-send -i "$tmpfile" "Screenshot of active window copied to clipboard"
|
||||||
|
rm -f "$tmpfile"
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
music_dir="/jellyfin/music"
|
music_dir="/truenas/jellyfin/music"
|
||||||
previewdir="$XDG_CONFIG_HOME/ncmpcpp/previews"
|
previewdir="${XDG_CONFIG_HOME:-$HOME/.config}/ncmpcpp/previews"
|
||||||
filename="$(mpc --format "$music_dir"/%file% current)"
|
filename="$(mpc --format "$music_dir"/%file% current)"
|
||||||
previewname="$previewdir/$(mpc --format %album% current | base64).png"
|
previewname="$previewdir/$(mpc --format %album% current | base64).png"
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/bin/sh
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
if pgrep -af "waybar -c /home/sudacode/.config/waybar/catppuccin-macchiato/config-battery.jsonc -s /home/sudacode/.config/waybar/catppuccin-macchiato/style.css" ||
|
if pgrep -af "waybar -c /home/sudacode/.config/waybar/catppuccin-macchiato/config-battery.jsonc -s /home/sudacode/.config/waybar/catppuccin-macchiato/style.css" ||
|
||||||
pgrep -af "waybar -c /home/sudacode/.config/waybar/catppuccin-macchiato/config.jsonc -s /home/sudacode/.config/waybar/catppuccin-macchiato/style.css"; then
|
pgrep -af "waybar -c /home/sudacode/.config/waybar/catppuccin-macchiato/config.jsonc -s /home/sudacode/.config/waybar/catppuccin-macchiato/style.css"; then
|
||||||
@@ -9,7 +9,7 @@ fi
|
|||||||
BASE_DIR="$HOME/.config/waybar/catppuccin-macchiato"
|
BASE_DIR="$HOME/.config/waybar/catppuccin-macchiato"
|
||||||
NODE_NAME="$(hyprctl systeminfo | grep -i "node name" | sed 's/Node name: //')"
|
NODE_NAME="$(hyprctl systeminfo | grep -i "node name" | sed 's/Node name: //')"
|
||||||
|
|
||||||
if [[ "$NODE_NAME" = "sc-arch" ]]; then
|
if [[ "$NODE_NAME" = "sc-arch" || "$NODE_NAME" = "cachypc" ]]; then
|
||||||
CONFIG="$BASE_DIR/config.jsonc"
|
CONFIG="$BASE_DIR/config.jsonc"
|
||||||
else
|
else
|
||||||
CONFIG="$BASE_DIR/config-laptop.jsonc"
|
CONFIG="$BASE_DIR/config-laptop.jsonc"
|
||||||
|
|||||||
Reference in New Issue
Block a user