diff --git a/.dotter/global.toml b/.dotter/global.toml index 307169ad..f8a4688e 100644 --- a/.dotter/global.toml +++ b/.dotter/global.toml @@ -66,6 +66,7 @@ depends = [ "torrent", "zathura" ] "config/fastfetch/" = "~/.config/fastfetch/" "config/flameshot.ini" = "~/.config/flameshot/flameshot.ini" "config/gitui/" = "~/.config/gitui/" +"config/git/" = "~/.config/git/" "config/mpv/" = "~/.config/mpv/" "config/lf/" = "~/.config/lf/" "config/paru.conf" = "~/.config/paru/paru.conf" @@ -128,6 +129,8 @@ depends = [ "zsh", "tmux" ] "config/alacritty/" = "~/.config/alacritty/" "config/alacritty/alacritty.toml" = {target = "~/.config/alacritty/alacritty.toml", type = "template"} "config/starship.toml" = "~/.config/starship/starship.toml" +"config/ghostty/" = "~/.config/ghostty/" +"config/ghostty/config" = {target = "~/.config/ghostty/config", type = "template"} [tmux.files] "config/tmux/" = "~/.config/tmux/" diff --git a/config/HybridBar/scripts/change-active-workspace b/config/HybridBar/scripts/change-active-workspace old mode 100755 new mode 100644 diff --git a/config/HybridBar/scripts/get-active-workspace b/config/HybridBar/scripts/get-active-workspace old mode 100755 new mode 100644 diff --git a/config/HybridBar/scripts/get-window-title b/config/HybridBar/scripts/get-window-title old mode 100755 new mode 100644 diff --git a/config/HybridBar/scripts/get-workspaces b/config/HybridBar/scripts/get-workspaces old mode 100755 new mode 100644 diff --git a/config/awesome/rc.lua b/config/awesome/rc.lua index 179f34a6..e748c589 100644 --- a/config/awesome/rc.lua +++ b/config/awesome/rc.lua @@ -435,7 +435,7 @@ local globalkeys = gears.table.join( -- awful.key({ modkey, "Control" }, "q", awesome.quit, { description = "quit awesome", group = "awesome" }), awful.key({ modkey }, "b", function() - awful.spawn("floorp") + awful.spawn("zen-browser") end, { description = "open browser", group = "launcher" }), awful.key({ modkey }, "n", function() awful.spawn("alacritty -e yazi") diff --git a/config/btop/btop.conf b/config/btop/btop.conf index 6a728033..82a46b55 100644 --- a/config/btop/btop.conf +++ b/config/btop/btop.conf @@ -202,7 +202,7 @@ net_download = 100 net_upload = 100 #* Use network graphs auto rescaling mode, ignores any values set above and rescales down to 10 Kibibytes at the lowest. -net_auto = False +net_auto = True #* Sync the auto scaling for download and upload to whichever currently has the highest scale. net_sync = False diff --git a/config/dunst/dunstrc.d/50-theme.conf b/config/dunst/dunstrc.d/50-theme.conf index 08f14c76..d66a95d5 100644 --- a/config/dunst/dunstrc.d/50-theme.conf +++ b/config/dunst/dunstrc.d/50-theme.conf @@ -20,21 +20,21 @@ foreground = "#e0def4" [urgency_low] - background = "#26273d" - highlight = "#31748f" - frame_color = "#31748f" + background = "#26233a" + highlight = "#9ccfd8" + frame_color = "#9ccfd8" default_icon = "dialog-information" - format = "%s\n%b" + format = "%s\n%b" [urgency_normal] - background = "#362e3c" - highlight = "#f6c177" - frame_color = "#f6c177" + background = "#26233a" + highlight = "#ebbcba" + frame_color = "#ebbcba" default_icon = "dialog-warning" - format = "%s\n%b" + format = "%s\n%b" [urgency_critical] - background = "#35263d" + background = "#26233a" highlight = "#eb6f92" frame_color = "#eb6f92" default_icon = "dialog-error" diff --git a/config/eww.bak/eww.yuck b/config/eww.bak/eww.yuck index 55001ef3..8debe384 100644 --- a/config/eww.bak/eww.yuck +++ b/config/eww.bak/eww.yuck @@ -224,7 +224,7 @@ :height "10px" :anchor "top center") :reserve (struts :side "top" :distance "4%") -(bar)) + (bar)) (defwidget bright [] (eventbox :onhover "eww update br_reveal=true" :onhoverlost "eww update br_reveal=false" diff --git a/config/eww.bak/scripts/change-active-workspace b/config/eww.bak/scripts/change-active-workspace deleted file mode 100755 index 3a43646e..00000000 --- a/config/eww.bak/scripts/change-active-workspace +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bash -function clamp { - min=$1 - max=$2 - val=$3 - python -c "print(max($min, min($val, $max)))" -} - -direction=$1 -current=$2 -if test "$direction" = "down" -then - target=$(clamp 1 10 $(($current+1))) - echo "jumping to $target" - hyprctl dispatch workspace $target -elif test "$direction" = "up" -then - target=$(clamp 1 10 $(($current-1))) - echo "jumping to $target" - hyprctl dispatch workspace $target -fi diff --git a/config/eww.bak/scripts/get-active-workspace b/config/eww.bak/scripts/get-active-workspace deleted file mode 100755 index c80f7f00..00000000 --- a/config/eww.bak/scripts/get-active-workspace +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -hyprctl monitors -j | jq '.[] | select(.focused) | .activeWorkspace.id' - -socat -u UNIX-CONNECT:$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock - | - stdbuf -o0 awk -F '>>|,' -e '/^workspace>>/ {print $2}' -e '/^focusedmon>>/ {print $3}' diff --git a/config/eww.bak/scripts/get-music b/config/eww.bak/scripts/get-music deleted file mode 100755 index 2a57d4c6..00000000 --- a/config/eww.bak/scripts/get-music +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -title="$(sp metadata | rg title| cut -d'|' -f2)" -artist="$(sp metadata | rg artist | cut -d'|' -f2)" -echo "$artist" - "$title" diff --git a/config/eww.bak/scripts/get-network b/config/eww.bak/scripts/get-network deleted file mode 100755 index 8d9ece1a..00000000 --- a/config/eww.bak/scripts/get-network +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -line=$(nmcli connection show | rg wlan0) -echo $line - diff --git a/config/eww.bak/scripts/get-window-title b/config/eww.bak/scripts/get-window-title deleted file mode 100755 index f246c712..00000000 --- a/config/eww.bak/scripts/get-window-title +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh -hyprctl activewindow -j | jq --raw-output .title -socat -u UNIX-CONNECT:$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock - | stdbuf -o0 awk -F '>>|,' '/^activewindow>>/{print $3}' diff --git a/config/eww.bak/scripts/get-workspaces b/config/eww.bak/scripts/get-workspaces deleted file mode 100755 index 14911c95..00000000 --- a/config/eww.bak/scripts/get-workspaces +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash - -spaces (){ - WORKSPACE_WINDOWS=$(hyprctl workspaces -j | jq 'map({key: .id | tostring, value: .windows}) | from_entries') - seq 1 10 | jq --argjson windows "${WORKSPACE_WINDOWS}" --slurp -Mc 'map(tostring) | map({id: ., windows: ($windows[.]//0)})' -} - -spaces -socat -u UNIX-CONNECT:$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock - | while read -r line; do - spaces -done diff --git a/config/eww.bak/scripts/getvol b/config/eww.bak/scripts/getvol deleted file mode 100755 index 4e4275d8..00000000 --- a/config/eww.bak/scripts/getvol +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/sh - -if command -v pamixer &>/dev/null; then - if [ true == $(pamixer --get-mute) ]; then - echo 0 - exit - else - pamixer --get-volume - fi -else - amixer -D pulse sget Master | awk -F '[^0-9]+' '/Left:/{print $3}' -fi diff --git a/config/eww.bak/scripts/github b/config/eww.bak/scripts/github deleted file mode 100755 index 17682bf0..00000000 --- a/config/eww.bak/scripts/github +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -notifications="$(gh api notifications 2> /dev/null | jq '. | length')" -[ -z notifications ] && echo "" || echo "$notifications" diff --git a/config/eww/eww.scss b/config/eww/eww.scss index 36f18843..ca8fc2e4 100644 --- a/config/eww/eww.scss +++ b/config/eww/eww.scss @@ -18,7 +18,7 @@ $bg: rgba(25, 23, 37, 0.7); * { all: unset; font-family: "JetBrainsMono NF"; - font-size: 14; + font-size: 20; } /** General **/ @@ -128,6 +128,10 @@ clock_time { &__Charging { color: $gold; } + + &__low { + color: $love; + } } .iconbat, @@ -275,6 +279,10 @@ calendar:indeterminate { .sys_icon_mem { font-size: 30; margin: 30px; + + &__low { + color: $love; + } } .sys_win { diff --git a/config/eww/eww.yuck b/config/eww/eww.yuck index 9f2d3d74..ec58833c 100644 --- a/config/eww/eww.yuck +++ b/config/eww/eww.yuck @@ -1,5 +1,5 @@ ;; Variables -(defpoll clock_hour :interval "5m" "date +%H") +(defpoll clock_hour :interval "1m" "date +%H") (defpoll clock_minute :interval "2s" "date +%M") (defpoll clock_second :interval "1s" "date +%S") (defpoll clock_date :interval "10h" "date +%d.%m.%Y") @@ -7,34 +7,32 @@ (defpoll volume_percent :interval "3s" "amixer -D pulse sget Master | grep 'Left:' | awk -F'[][]' '{ print $2 }' | tr -d '%'") (defpoll mic_percent :interval "3s" "amixer -D pulse sget Capture | grep 'Left:' | awk -F'[][]' '{ print $2 }' | tr -d '%'") (defpoll brightness_percent :interval "5s" "brightnessctl -m -d intel_backlight | awk -F, '{print substr($4, 0, length($4)-1)}' | tr -d '%'") -(defpoll battery :interval "15s" "./scripts/battery --bat") -(defpoll battery_status :interval "1m" "./scripts/battery --bat-st") -(defpoll memory :interval "15s" "scripts/memory") -(defpoll memory_used_mb :interval "2m" "scripts/mem-ad used") -(defpoll memory_total_mb :interval "2m" "scripts/mem-ad total") -(defpoll memory_free_mb :interval "2m" "scripts/mem-ad free") +(defpoll memory :interval "10s" "scripts/memory") +(defpoll memory_used_mb :interval "1m" "scripts/mem-ad used") +(defpoll memory_total_mb :interval "1m" "scripts/mem-ad total") +(defpoll memory_free_mb :interval "1m" "scripts/mem-ad free") (defvar vol_reveal false) (defvar br_reveal false) (defvar music_reveal false) (defvar wifi_rev false) (defvar time_rev false) -(deflisten workspaces :initial "[]" "bash ~/.config/eww/scripts/get-workspaces") +(deflisten workspaces :initial "[]" "bash ~/.config/eww/scripts/workspace") (deflisten current_workspace :initial "1" "bash ~/.config/eww/scripts/get-active-workspace") (deflisten window :initial "..." "bash ~/.config/eww/scripts/get-window-title") (defvar eww "eww") -(defpoll COL_WLAN :interval "1m" "~/.config/eww/bar/scripts/wifi --COL") -(defpoll ESSID_WLAN :interval "1m" "~/.config/eww/bar/scripts/wifi --ESSID") -(defpoll WLAN_ICON :interval "1m" "~/.config/eww/bar/scripts/wifi --ICON") +(defpoll COL_WLAN :interval "1m" "~/.config/eww/scripts/wifi --COL") +(defpoll ESSID_WLAN :interval "1m" "~/.config/eww/scripts/wifi --ESSID") +(defpoll WLAN_ICON :interval "1m" "~/.config/eww/scripts/wifi --ICON") -(defpoll song :interval "2s" "~/.config/eww/bar/scripts/music_info --song") -(defpoll song_artist :interval "2s" "~/.config/eww/bar/scripts/music_info --artist") -(defpoll current_status :interval "1s" "~/.config/eww/bar/scripts/music_info --time") -(defpoll song_status :interval "2s" "~/.config/eww/bar/scripts/music_info --status") -(defpoll cover_art :interval "2s" "~/.config/eww/bar/scripts/music_info --cover") +(defpoll song :interval "2s" "~/.config/eww/scripts/music_info --song") +(defpoll song_artist :interval "2s" "~/.config/eww/scripts/music_info --artist") +(defpoll current_status :interval "1s" "~/.config/eww/scripts/music_info --time") +(defpoll song_status :interval "2s" "~/.config/eww/scripts/music_info --status") +(defpoll cover_art :interval "2s" "~/.config/eww/scripts/music_info --cover") (defpoll calendar_day :interval "20h" "date '+%d'") (defpoll calendar_year :interval "20h" "date '+%Y'") @@ -42,16 +40,21 @@ ;; widgets (defwidget wifi [] - (eventbox :onhover "${eww} update wifi_rev=true" + (eventbox + :onhover "${eww} update wifi_rev=true" :onhoverlost "${eww} update wifi_rev=false" - (box :vexpand "false" :hexpand "false" :space-evenly "false" + (box + :vexpand "false" + :hexpand "false" + :space-evenly "false" (button - :class "module-wif" + :class "module-wifi" :onclick "networkmanager_dmenu" :wrap "false" :limit-width 12 :style "color: ${COL_WLAN};" WLAN_ICON) - (revealer :transition "slideright" + (revealer + :transition "slideright" :reveal wifi_rev :duration "350ms" (label @@ -62,7 +65,9 @@ (defwidget workspaces [] - (eventbox :onscroll "bash ~/.config/eww/scripts/change-active-workspace {} ${current_workspace}" :class "workspaces" + (eventbox + :onscroll "bash ~/.config/eww/scripts/change-active-workspace {} ${current_workspace}" + :class "workspaces" (box :space-evenly true :halign "start" :space-evenly true @@ -74,25 +79,25 @@ (eventbox :onclick "hyprctl dispatch workspace ${workspace.id}" (box :class "workspace-entry ${workspace.windows > 0 ? "occupied" : "empty"}" (label :text "${workspace.id}" :class "workspace-entry ${workspace.id == current_workspace ? "current" : ""}" ) - ) - ) - ) - ) - ) - ) + )))))) (defwidget bat [] - (box :class "bat_module" :vexpand "false" :hexpand "false" - (circular-progress :value battery + (button + :class "bat_module" + :vexpand "false" + :hexpand "false" + :onclick "bash ~/.config/eww/scripts/pop system" + (circular-progress + :value "${EWW_BATTERY.BAT0.capacity}" :class "batbar" :thickness 4 - (button - :class "iconbat iconbat__${battery_status}" + (box + :class "iconbat iconbat__${EWW_BATTERY.BAT0.status} ${EWW_BATTERY.BAT0.capacity < 20 ? "iconbat__low" : ""}" + :interval "5m" :limit-width 2 - :tooltip "battery on ${battery}%" + :tooltip "Battery on ${EWW_BATTERY.BAT0.capacity}%" :show_truncated false - :onclick "$HOME/.config/eww/bar/scripts/pop system" :wrap false "${EWW_BATTERY.BAT0.status == "Charging" ? "" : EWW_BATTERY.BAT0.capacity > 90 ? "" : @@ -104,15 +109,19 @@ (defwidget mem [] - (box :class "mem_module" :vexpand "false" :hexpand "false" - (circular-progress :value memory + (button + :class "mem_module" + :vexpand "false" + :hexpand "false" + :onclick "bash ~/.config/eww/scripts/pop system" + (circular-progress + :value memory :class "membar" :thickness 4 - (button + (box :class "iconmem" :limit-width 2 - :tooltip "using ${memory}% ram" - :onclick "$HOME/.config/eww/bar/scripts/pop system" + :tooltip "Using ${memory}% ram" :show_truncated false :wrap false "")))) @@ -132,7 +141,7 @@ :reveal time_rev :duration "350ms" (button :class "clock_date" - :onclick "$HOME/.config/eww/bar/scripts/pop calendar" "${clock_date}, ${clock_day}" + :onclick "bash ~/.config/eww/scripts/pop calendar" "${clock_date}, ${clock_day}" ) )))) @@ -144,7 +153,8 @@ (revealer :transition "slideleft" :reveal vol_reveal :duration "350ms" - (scale :class "volbar" + (scale + :class "volbar" :value volume_percent :orientation "h" :tooltip "${volume_percent}%" @@ -153,13 +163,24 @@ :onchange "amixer -D pulse sset Master {}%" ))))) (defwidget bright [] - (eventbox :onhover "${eww} update br_reveal=true" :onhoverlost "${eww} update br_reveal=false" - (box :class "module-2" :space-evenly "false" :orientation "h" :spacing "3" - (label :text "☼" :class "bright_icon" :tooltip "brightness") - (revealer :transition "slideleft" + (eventbox + :onhover "${eww} update br_reveal=true" + :onhoverlost "${eww} update br_reveal=false" + (box + :class "module-2" + :space-evenly "false" + :orientation "h" + :spacing "3" + (label + :text "☼" + :class "bright_icon" + :tooltip "brightness") + (revealer + :transition "slideleft" :reveal br_reveal :duration "350ms" - (scale :class "brightbar" + (scale + :class "brightbar" :value brightness_percent :orientation "h" :tooltip "${brightness_percent}%" @@ -171,18 +192,35 @@ ;; Music (defwidget music [] - (eventbox :onhover "${eww} update music_reveal=true" + (eventbox + :onhover "${eww} update music_reveal=true" :onhoverlost "${eww} update music_reveal=false" - (box :class "module-2" :orientation "h" :space-evenly "false" :vexpand "false" :hexpand "false" - (box :class "song_cover_art" :vexpand "false" :hexpand "false" :style "background-image: url('${cover_art}');") - (button :class "song" :wrap "true" :onclick "~/.config/eww/bar/scripts/pop music" song) - (revealer :transition "slideright" + (box + :class "module-2" + :orientation "h" + :space-evenly "false" + :vexpand "false" + :hexpand "false" + (box + :class "song_cover_art" + :vexpand "false" + :hexpand "false" + :style "background-image: url('${cover_art}');") + (button + :class "song" + :wrap "true" + :onclick "~/.config/eww/scripts/pop music" song) + (revealer + :transition "slideright" :reveal music_reveal :duration "350ms" - (box :vexpand "false" :hexpand "false" :oreintation "h" - (button :class "song_btn_prev" :onclick "~/.config/eww/bar/scripts/music_info --prev" "") - (button :class "song_btn_play" :onclick "~/.config/eww/bar/scripts/music_info --toggle" song_status) - (button :class "song_btn_next" :onclick "~/.config/eww/bar/scripts/music_info --next" "")))))) + (box + :vexpand "false" + :hexpand "false" + :oreintation "h" + (button :class "song_btn_prev" :onclick "~/.config/eww/scripts/music_info --prev" "") + (button :class "song_btn_play" :onclick "~/.config/eww/scripts/music_info --toggle" song_status) + (button :class "song_btn_next" :onclick "~/.config/eww/scripts/music_info --next" "")))))) @@ -203,7 +241,8 @@ (defwidget right [] - (box :orientation "h" + (box + :orientation "h" :space-evenly false :halign "start" :class "right_modules" @@ -211,14 +250,16 @@ (defwidget center [] - (box :orientation "h" + (box + :orientation "h" :space-evenly false :halign "center" :class "center_modules" (window))) (defwidget bar_1 [] - (box :class "bar" + (box + :class "bar" :orientation "h" (right) (center) @@ -239,61 +280,117 @@ :windowtype "dock" (bar_1)) +(defwindow bar-1 + :monitor 1 + :exclusive true + :geometry (geometry + :x "0%" + :y "0%" + :width "100%" + :height "10px" + :anchor "top center") + :reserve (struts :side "top" :distance "4%") + :stacking "fg" + :windowtype "dock" + (bar_1)) + (defwidget system [] - (box :class "sys_win" :orientation "v" :space-evenly "false" :hexpand "false" :vexpand "false" :spacing 0 - (box :class "sys_bat_box" :orientation "h" :space-evenly "false" - (circular-progress :value battery + (box + :class "sys_win" + :orientation "v" + :space-evenly "false" + :hexpand "false" + :vexpand "false" + :spacing 0 + (box + :class "sys_bat_box" + :orientation "h" + :space-evenly "false" + (circular-progress + :value "${EWW_BATTERY.BAT0.capacity}" :class "sys_bat" :thickness 9 - (label :text "" - :class "sys_icon_bat" + (label + :text "${EWW_BATTERY.BAT0.status == "Charging" ? "" : + EWW_BATTERY.BAT0.capacity > 90 ? "" : + EWW_BATTERY.BAT0.capacity > 70 ? "" : + EWW_BATTERY.BAT0.capacity > 40 ? "" : + EWW_BATTERY.BAT0.capacity > 20 ? "" : + ""}" + :interval "5m" + :class "sys_icon_bat ${EWW_BATTERY.BAT0.capacity} syst_icon_bat__${EWW_BATTERY.BAT0.status} ${EWW_BATTERY.BAT0.capacity < 20 ? "sys_icon_bat__low" : ""}" :limit-width 2 :show_truncated false :wrap false)) - (box :orientation "v" :space-evenly "false" :spacing 0 :hexpand "false" :vexpand "false" - (label :text "battery" + (box + :orientation "v" + :space-evenly "false" + :spacing 0 + :hexpand "false" + :vexpand "false" + (label + :text "Battery" :halign "start" :class "sys_text_bat" :limit-width 9 :show_truncated false :wrap false) - (label :text "${battery}%" + (label + :text "${EWW_BATTERY.BAT0.capacity}%" :halign "start" :class "sys_text_bat_sub" :limit-width 22 :show_truncated false :wrap false) - (label :text "${battery_status}" + (label + :text "${EWW_BATTERY.BAT0.status}" :halign "start" :class "sys_text_bat_sub" :limit-width 22 :show_truncated false :wrap false))) - (label :text "" :class "sys_sep" :halign "center") - (box :class "sys_mem_box" :orientation "h" :space-evenly "false" :halign "start" - (circular-progress :value memory + (label + :text "" + :class "sys_sep" + :halign "center") + (box + :class "sys_mem_box" + :orientation "h" + :space-evenly "false" + :halign "start" + (circular-progress + :value memory :class "sys_mem" :thickness 9 - (label :text "" + (label + :text "" :class "sys_icon_mem" :limit-width 2 :show_truncated false :wrap false :angle 0.0)) - (box :orientation "v" :space-evenly "false" :spacing 0 :hexpand "false" :vexpand "false" - (label :text "memory" + (box + :orientation "v" + :space-evenly "false" + :spacing 0 + :hexpand "false" + :vexpand "false" + (label + :text "Memory" :halign "start" :class "sys_text_mem" :limit-width 9 :show_truncated false :wrap false) - (label :text "${memory_used_mb} | ${memory_total_mb}mb " + (label + :text "${memory_used_mb}/${memory_total_mb}mb" :halign "start" :class "sys_text_mem_sub" :limit-width 22 :show_truncated false :wrap false) - (label :text "${memory_free_mb}mb free" + (label + :text "${memory_free_mb}mb free" :halign "start" :class "sys_text_mem_sub" :limit-width 22 @@ -301,16 +398,21 @@ :wrap false))))) (defwidget cal [] - (box :class "cal" :orientation "v" - (box :class "cal-in" - (calendar :class "cal" + (box + :class "cal" + :orientation "v" + (box + :class "cal-in" + (calendar + :class "cal" :day calendar_day :year calendar_year)))) (defwindow calendar :monitor 0 - :geometry (geometry :x "-20px" - :y "7%" + :geometry (geometry + :x "0px" + :y "10px" :anchor "top right" :width "270px" :height "60px") @@ -332,13 +434,35 @@ :tooltip "volume on ${volume_percent}%" :max 100 :min 0)))) - (label :text "" :class "audio_sep" :halign "center") - (box :halign "v" :space-evenly "false" :hexpand "false" :vexpand "false" - (box :class "mic_icon" :orientation "v") - (box :orientation "v" :halign "center" :vexpand "false" :hexpand "false" - (label :class "mic_text" :text "mic" :valign "center" :halign "left" ) - (box :class "mic_bar" :halign "center" :vexpand "false" :hexpand "false" - (scale :value mic_percent + (label + :text "" + :class "audio_sep" + :halign "center") + (box + :halign "v" + :space-evenly "false" + :hexpand "false" + :vexpand "false" + (box + :class "mic_icon" + :orientation "v") + (box + :orientation "v" + :halign "center" + :vexpand "false" + :hexpand "false" + (label + :class "mic_text" + :text "mic" + :valign "center" + :halign "left" ) + (box + :class "mic_bar" + :halign "center" + :vexpand "false" + :hexpand "false" + (scale + :value mic_percent :space-evenly "false" :orientation "h" :tooltip "mic on ${mic_percent}%" @@ -348,8 +472,9 @@ (defwindow audio_ctl :monitor 0 - :geometry (geometry :x "-20px" - :y "7%" + :geometry (geometry + :x "10px" + :y "10px" :anchor "top right" :width "280px" :height "60px") @@ -359,8 +484,8 @@ (defwindow system :monitor 0 :geometry (geometry - :x "-20px" - :y "5%" + :x "10px" + :y "10px" :anchor "top right" :width "290px" :height "120px") @@ -370,26 +495,71 @@ ;; Music (defwidget music_pop [] - (box :class "music_pop" :orientation "h" :space-evenly "false" :vexpand "false" :hexpand "false" - (box :class "music_cover_art" :vexpand "false" :hexpand "false" :style "background-image: url('${cover_art}');") - (box :orientation "v" :spacing 20 :space-evenly "false" :vexpand "false" :hexpand "false" - (label :halign "center" :class "music" :wrap "true" :limit-width 13 :text song) - (label :halign "center" :class "music_artist" :wrap "true" :limit-width 15 :text song_artist) - (box :orientation "h" :spacing 15 :halign "center" :space-evenly "false" :vexpand "false" :hexpand "false" - (button :class "music_btn_prev" :onclick "~/.config/eww/bar/scripts/music_info --prev" "") - (button :class "music_btn_play" :onclick "~/.config/eww/bar/scripts/music_info --toggle" song_status) - (button :class "music_btn_next" :onclick "~/.config/eww/bar/scripts/music_info --next" "")) - (box :class "music_bar" :halign "center" :vexpand "false" :hexpand "false" :space-evenly "false" - (scale :onscroll "mpc -q seek {}" :min 0 :active "true" :max 100 :value current_status))))) + (box + :class "music_pop" + :orientation "h" + :space-evenly "false" + :vexpand "false" + :hexpand "false" + (box + :class "music_cover_art" + :vexpand "false" + :hexpand "false" + :style "background-image: url('${cover_art}');") + (box + :orientation "v" + :spacing 20 + :space-evenly "false" + :vexpand "false" + :hexpand "false" + (label + :halign "center" + :class "music" + :wrap "true" + :limit-width 13 + :text song) + (label + :halign "center" + :class "music_artist" + :wrap "true" + :limit-width 15 + :text song_artist) + (box + :orientation "h" + :spacing 15 + :halign "center" + :space-evenly "false" + :vexpand "false" + :hexpand "false" + (button :class "music_btn_prev" :onclick "~/.config/eww/scripts/music_info --prev" "") + (button :class "music_btn_play" :onclick "~/.config/eww/scripts/music_info --toggle" song_status) + (button :class "music_btn_next" :onclick "~/.config/eww/scripts/music_info --next" "")) + (box + :class "music_bar" + :halign "center" + :vexpand "false" + :hexpand "false" + :space-evenly "false" + (scale + :onscroll "mpc -q seek {}" + :min 0 + :active "true" + :max 100 + :value current_status))))) ;; music (defwindow music_win - ;; :monitor 0 + :monitor 0 :stacking "fg" :focusable "false" :screen 1 - :geometry (geometry :x "0" :y "7%" :width 428 :height 104 :anchor "top center") + :geometry (geometry + :x "10px" + :y "10px" + :width 428 + :height 104 + :anchor "top center") (music_pop)) (defwidget window [] diff --git a/config/eww/scripts/battery b/config/eww/scripts/battery deleted file mode 100755 index 0540a1b1..00000000 --- a/config/eww/scripts/battery +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -battery() { - BAT=`ls /sys/class/power_supply | grep BAT | head -n 1` - cat /sys/class/power_supply/${BAT}/capacity -} -battery_stat() { - BAT=`ls /sys/class/power_supply | grep BAT | head -n 1` - cat /sys/class/power_supply/${BAT}/status -} - -if [[ "$1" == "--bat" ]]; then - battery -elif [[ "$1" == "--bat-st" ]]; then - battery_stat -fi - diff --git a/config/eww/scripts/change-active-workspace b/config/eww/scripts/change-active-workspace old mode 100755 new mode 100644 diff --git a/config/eww/scripts/get-active-workspace b/config/eww/scripts/get-active-workspace old mode 100755 new mode 100644 diff --git a/config/eww/scripts/get-window-title b/config/eww/scripts/get-window-title old mode 100755 new mode 100644 diff --git a/config/eww/scripts/get-workspaces b/config/eww/scripts/get-workspaces old mode 100755 new mode 100644 diff --git a/config/eww/scripts/mem-ad b/config/eww/scripts/mem-ad old mode 100755 new mode 100644 index 1e4cf89f..aba1cf1b --- a/config/eww/scripts/mem-ad +++ b/config/eww/scripts/mem-ad @@ -1,5 +1,4 @@ -#!/bin/sh - +#!/usr/bin/env bash total="$(free -m | grep Mem: | awk '{ print $2 }')" used="$(free -m | grep Mem: | awk '{ print $3 }')" diff --git a/config/eww/scripts/memory b/config/eww/scripts/memory old mode 100755 new mode 100644 index 217c8344..4cd200f7 --- a/config/eww/scripts/memory +++ b/config/eww/scripts/memory @@ -1,3 +1,3 @@ -#!/bin/sh +#!/usr/bin/env bash -printf "%.0f\n" $(free -m | grep Mem | awk '{print ($3/$2)*100}') +awk '/Mem:/ {printf "%.0f\n", ($3/$2)*100}' <(free -m) diff --git a/config/eww/scripts/music_info b/config/eww/scripts/music_info old mode 100755 new mode 100644 diff --git a/config/eww/scripts/music_info.bak b/config/eww/scripts/music_info.bak old mode 100755 new mode 100644 diff --git a/config/eww/scripts/pop b/config/eww/scripts/pop old mode 100755 new mode 100644 diff --git a/config/eww/scripts/wifi b/config/eww/scripts/wifi old mode 100755 new mode 100644 diff --git a/config/eww/scripts/workspace b/config/eww/scripts/workspace old mode 100755 new mode 100644 diff --git a/config/ghostty/config b/config/ghostty/config new file mode 100644 index 00000000..e3258598 --- /dev/null +++ b/config/ghostty/config @@ -0,0 +1,2253 @@ +# The font families to use. +# +# You can generate the list of valid values using the CLI: +# +# ghostty +list-fonts +# +# This configuration can be repeated multiple times to specify preferred +# fallback fonts when the requested codepoint is not available in the primary +# font. This is particularly useful for multiple languages, symbolic fonts, +# etc. +# +# Notes on emoji specifically: On macOS, Ghostty by default will always use +# Apple Color Emoji and on Linux will always use Noto Emoji. You can +# override this behavior by specifying a font family here that contains +# emoji glyphs. +# +# The specific styles (bold, italic, bold italic) do not need to be +# explicitly set. If a style is not set, then the regular style (font-family) +# will be searched for stylistic variants. If a stylistic variant is not +# found, Ghostty will use the regular style. This prevents falling back to a +# different font family just to get a style such as bold. This also applies +# if you explicitly specify a font family for a style. For example, if you +# set `font-family-bold = FooBar` and "FooBar" cannot be found, Ghostty will +# use whatever font is set for `font-family` for the bold style. +# +# Finally, some styles may be synthesized if they are not supported. +# For example, if a font does not have an italic style and no alternative +# italic font is specified, Ghostty will synthesize an italic style by +# applying a slant to the regular style. If you want to disable these +# synthesized styles then you can use the `font-style` configurations +# as documented below. +# +# You can disable styles completely by using the `font-style` set of +# configurations. See the documentation for `font-style` for more information. +# +# If you want to overwrite a previous set value rather than append a fallback, +# specify the value as `""` (empty string) to reset the list and then set the +# new values. For example: +# +# font-family = "" +# font-family = "My Favorite Font" +# +# Setting any of these as CLI arguments will automatically clear the +# values set in configuration files so you don't need to specify +# `--font-family=""` before setting a new value. You only need to specify +# this within config files if you want to clear previously set values in +# configuration files or on the CLI if you want to clear values set on the +# CLI. +# +# Changing this configuration at runtime will only affect new terminals, i.e. +# new windows, tabs, etc. +font-family = "JetBrainsMono NF" + +# font-family-bold = "JetBrainsMono NF Bold" +# font-family-italic = "JetBrainsMono NF Italic" +# font-family-bold-italic = "JetBrainsMono NF Bold Italic" +# The named font style to use for each of the requested terminal font styles. +# This looks up the style based on the font style string advertised by the +# font itself. For example, "Iosevka Heavy" has a style of "Heavy". +# +# You can also use these fields to completely disable a font style. If you set +# the value of the configuration below to literal `false` then that font style +# will be disabled. If the running program in the terminal requests a disabled +# font style, the regular font style will be used instead. +# +# These are only valid if its corresponding font-family is also specified. If +# no font-family is specified, then the font-style is ignored unless you're +# disabling the font style. +font-style = default + +font-style-bold = default +font-style-italic = default +font-style-bold-italic = default +# Control whether Ghostty should synthesize a style if the requested style is +# not available in the specified font-family. +# +# Ghostty can synthesize bold, italic, and bold italic styles if the font +# does not have a specific style. For bold, this is done by drawing an +# outline around the glyph of varying thickness. For italic, this is done by +# applying a slant to the glyph. For bold italic, both of these are applied. +# +# Synthetic styles are not perfect and will generally not look as good +# as a font that has the style natively. However, they are useful to +# provide styled text when the font does not have the style. +# +# Set this to "false" or "true" to disable or enable synthetic styles +# completely. You can disable specific styles using "no-bold", "no-italic", +# and "no-bold-italic". You can disable multiple styles by separating them +# with a comma. For example, "no-bold,no-italic". +# +# Available style keys are: `bold`, `italic`, `bold-italic`. +# +# If synthetic styles are disabled, then the regular style will be used +# instead if the requested style is not available. If the font has the +# requested style, then the font will be used as-is since the style is +# not synthetic. +# +# Warning: An easy mistake is to disable `bold` or `italic` but not +# `bold-italic`. Disabling only `bold` or `italic` will NOT disable either +# in the `bold-italic` style. If you want to disable `bold-italic`, you must +# explicitly disable it. You cannot partially disable `bold-italic`. +# +# By default, synthetic styles are enabled. +font-synthetic-style = bold,italic,bold-italic + +# Apply a font feature. To enable multiple font features you can repeat +# this multiple times or use a comma-separated list of feature settings. +# +# The syntax for feature settings is as follows, where `feat` is a feature: +# +# * Enable features with e.g. `feat`, `+feat`, `feat on`, `feat=1`. +# * Disabled features with e.g. `-feat`, `feat off`, `feat=0`. +# * Set a feature value with e.g. `feat=2`, `feat = 3`, `feat 4`. +# * Feature names may be wrapped in quotes, meaning this config should be +# syntactically compatible with the `font-feature-settings` CSS property. +# +# The syntax is fairly loose, but invalid settings will be silently ignored. +# +# The font feature will apply to all fonts rendered by Ghostty. A future +# enhancement will allow targeting specific faces. +# +# To disable programming ligatures, use `-calt` since this is the typical +# feature name for programming ligatures. To look into what font features +# your font has and what they do, use a font inspection tool such as +# [fontdrop.info](https://fontdrop.info). +# +# To generally disable most ligatures, use `-calt, -liga, -dlig`. +font-feature = "-calt, -liga, -dlig" + +# Font size in points. This value can be a non-integer and the nearest integer +# pixel size will be selected. If you have a high dpi display where 1pt = 2px +# then you can get an odd numbered pixel size by specifying a half point. +# +# For example, 13.5pt @ 2px/pt = 27px +# +# Changing this configuration at runtime will only affect new terminals, +# i.e. new windows, tabs, etc. Note that you may still not see the change +# depending on your `window-inherit-font-size` setting. If that setting is +# true, only the first window will be affected by this change since all +# subsequent windows will inherit the font size of the previous window. +# +# On Linux with GTK, font size is scaled according to both display-wide and +# text-specific scaling factors, which are often managed by your desktop +# environment (e.g. the GNOME display scale and large text settings). +font-size = {{font_size}} + +# A repeatable configuration to set one or more font variations values for +# a variable font. A variable font is a single font, usually with a filename +# ending in `-VF.ttf` or `-VF.otf` that contains one or more configurable axes +# for things such as weight, slant, etc. Not all fonts support variations; +# only fonts that explicitly state they are variable fonts will work. +# +# The format of this is `id=value` where `id` is the axis identifier. An axis +# identifier is always a 4 character string, such as `wght`. To get the list +# of supported axes, look at your font documentation or use a font inspection +# tool. +# +# Invalid ids and values are usually ignored. For example, if a font only +# supports weights from 100 to 700, setting `wght=800` will do nothing (it +# will not be clamped to 700). You must consult your font's documentation to +# see what values are supported. +# +# Common axes are: `wght` (weight), `slnt` (slant), `ital` (italic), `opsz` +# (optical size), `wdth` (width), `GRAD` (gradient), etc. +font-variation = + +font-variation-bold = +font-variation-italic = +font-variation-bold-italic = +# Force one or a range of Unicode codepoints to map to a specific named font. +# This is useful if you want to support special symbols or if you want to use +# specific glyphs that render better for your specific font. +# +# The syntax is `codepoint=fontname` where `codepoint` is either a single +# codepoint or a range. Codepoints must be specified as full Unicode +# hex values, such as `U+ABCD`. Codepoints ranges are specified as +# `U+ABCD-U+DEFG`. You can specify multiple ranges for the same font separated +# by commas, such as `U+ABCD-U+DEFG,U+1234-U+5678=fontname`. The font name is +# the same value as you would use for `font-family`. +# +# This configuration can be repeated multiple times to specify multiple +# codepoint mappings. +# +# Changing this configuration at runtime will only affect new terminals, +# i.e. new windows, tabs, etc. +font-codepoint-map = + +# Draw fonts with a thicker stroke, if supported. +# This is currently only supported on macOS. +font-thicken = false + +# Strength of thickening when `font-thicken` is enabled. +# +# Valid values are integers between `0` and `255`. `0` does not correspond to +# *no* thickening, rather it corresponds to the lightest available thickening. +# +# Has no effect when `font-thicken` is set to `false`. +# +# This is currently only supported on macOS. +font-thicken-strength = 255 + +# What color space to use when performing alpha blending. +# +# This affects the appearance of text and of any images with transparency. +# Additionally, custom shaders will receive colors in the configured space. +# +# Valid values: +# +# * `native` - Perform alpha blending in the native color space for the OS. +# On macOS this corresponds to Display P3, and on Linux it's sRGB. +# +# * `linear` - Perform alpha blending in linear space. This will eliminate +# the darkening artifacts around the edges of text that are very visible +# when certain color combinations are used (e.g. red / green), but makes +# dark text look much thinner than normal and light text much thicker. +# This is also sometimes known as "gamma correction". +# (Currently only supported on macOS. Has no effect on Linux.) +# +# * `linear-corrected` - Same as `linear`, but with a correction step applied +# for text that makes it look nearly or completely identical to `native`, +# but without any of the darkening artifacts. +alpha-blending = native + +# All of the configurations behavior adjust various metrics determined by the +# font. The values can be integers (1, -1, etc.) or a percentage (20%, -15%, +# etc.). In each case, the values represent the amount to change the original +# value. +# +# For example, a value of `1` increases the value by 1; it does not set it to +# literally 1. A value of `20%` increases the value by 20%. And so on. +# +# There is little to no validation on these values so the wrong values (e.g. +# `-100%`) can cause the terminal to be unusable. Use with caution and reason. +# +# Some values are clamped to minimum or maximum values. This can make it +# appear that certain values are ignored. For example, many `*-thickness` +# adjustments cannot go below 1px. +# +# `adjust-cell-height` has some additional behaviors to describe: +# +# * The font will be centered vertically in the cell. +# +# * The cursor will remain the same size as the font, but may be +# adjusted separately with `adjust-cursor-height`. +# +# * Powerline glyphs will be adjusted along with the cell height so +# that things like status lines continue to look aligned. +adjust-cell-width = + +adjust-cell-height = +# Distance in pixels or percentage adjustment from the bottom of the cell to the text baseline. +# Increase to move baseline UP, decrease to move baseline DOWN. +# See the notes about adjustments in `adjust-cell-width`. +adjust-font-baseline = + +# Distance in pixels or percentage adjustment from the top of the cell to the top of the underline. +# Increase to move underline DOWN, decrease to move underline UP. +# See the notes about adjustments in `adjust-cell-width`. +adjust-underline-position = + +# Thickness in pixels of the underline. +# See the notes about adjustments in `adjust-cell-width`. +adjust-underline-thickness = + +# Distance in pixels or percentage adjustment from the top of the cell to the top of the strikethrough. +# Increase to move strikethrough DOWN, decrease to move underline UP. +# See the notes about adjustments in `adjust-cell-width`. +adjust-strikethrough-position = + +# Thickness in pixels or percentage adjustment of the strikethrough. +# See the notes about adjustments in `adjust-cell-width`. +adjust-strikethrough-thickness = + +# Distance in pixels or percentage adjustment from the top of the cell to the top of the overline. +# Increase to move overline DOWN, decrease to move underline UP. +# See the notes about adjustments in `adjust-cell-width`. +adjust-overline-position = + +# Thickness in pixels or percentage adjustment of the overline. +# See the notes about adjustments in `adjust-cell-width`. +adjust-overline-thickness = + +# Thickness in pixels or percentage adjustment of the bar cursor and outlined rect cursor. +# See the notes about adjustments in `adjust-cell-width`. +adjust-cursor-thickness = + +# Height in pixels or percentage adjustment of the cursor. Currently applies to all cursor types: +# bar, rect, and outlined rect. +# See the notes about adjustments in `adjust-cell-width`. +adjust-cursor-height = + +# Thickness in pixels or percentage adjustment of box drawing characters. +# See the notes about adjustments in `adjust-cell-width`. +adjust-box-thickness = + +# The method to use for calculating the cell width of a grapheme cluster. +# The default value is `unicode` which uses the Unicode standard to determine +# grapheme width. This results in correct grapheme width but may result in +# cursor-desync issues with some programs (such as shells) that may use a +# legacy method such as `wcswidth`. +# +# Valid values are: +# +# * `legacy` - Use a legacy method to determine grapheme width, such as +# wcswidth This maximizes compatibility with legacy programs but may result +# in incorrect grapheme width for certain graphemes such as skin-tone +# emoji, non-English characters, etc. +# +# This is called "legacy" and not something more specific because the +# behavior is undefined and we want to retain the ability to modify it. +# For example, we may or may not use libc `wcswidth` now or in the future. +# +# * `unicode` - Use the Unicode standard to determine grapheme width. +# +# If a running program explicitly enables terminal mode 2027, then `unicode` +# width will be forced regardless of this configuration. When mode 2027 is +# reset, this configuration will be used again. +# +# This configuration can be changed at runtime but will not affect existing +# terminals. Only new terminals will use the new configuration. +grapheme-width-method = unicode + +# FreeType load flags to enable. The format of this is a list of flags to +# enable separated by commas. If you prefix a flag with `no-` then it is +# disabled. If you omit a flag, its default value is used, so you must +# explicitly disable flags you don't want. You can also use `true` or `false` +# to turn all flags on or off. +# +# This configuration only applies to Ghostty builds that use FreeType. +# This is usually the case only for Linux builds. macOS uses CoreText +# and does not have an equivalent configuration. +# +# Available flags: +# +# * `hinting` - Enable or disable hinting, enabled by default. +# * `force-autohint` - Use the freetype auto-hinter rather than the +# font's native hinter. Enabled by default. +# * `monochrome` - Instructs renderer to use 1-bit monochrome +# rendering. This option doesn't impact the hinter. +# Enabled by default. +# * `autohint` - Use the freetype auto-hinter. Enabled by default. +# +# Example: `hinting`, `no-hinting`, `force-autohint`, `no-force-autohint` +freetype-load-flags = hinting,force-autohint,monochrome,autohint + +# A theme to use. This can be a built-in theme name, a custom theme +# name, or an absolute path to a custom theme file. Ghostty also supports +# specifying a different theme to use for light and dark mode. Each +# option is documented below. +# +# If the theme is an absolute pathname, Ghostty will attempt to load that +# file as a theme. If that file does not exist or is inaccessible, an error +# will be logged and no other directories will be searched. +# +# If the theme is not an absolute pathname, two different directories will be +# searched for a file name that matches the theme. This is case sensitive on +# systems with case-sensitive filesystems. It is an error for a theme name to +# include path separators unless it is an absolute pathname. +# +# The first directory is the `themes` subdirectory of your Ghostty +# configuration directory. This is `$XDG_CONFIG_DIR/ghostty/themes` or +# `~/.config/ghostty/themes`. +# +# The second directory is the `themes` subdirectory of the Ghostty resources +# directory. Ghostty ships with a multitude of themes that will be installed +# into this directory. On macOS, this list is in the +# `Ghostty.app/Contents/Resources/ghostty/themes` directory. On Linux, this +# list is in the `share/ghostty/themes` directory (wherever you installed the +# Ghostty "share" directory. +# +# To see a list of available themes, run `ghostty +list-themes`. +# +# A theme file is simply another Ghostty configuration file. They share +# the same syntax and same configuration options. A theme can set any valid +# configuration option so please do not use a theme file from an untrusted +# source. The built-in themes are audited to only set safe configuration +# options. +# +# Some options cannot be set within theme files. The reason these are not +# supported should be self-evident. A theme file cannot set `theme` or +# `config-file`. At the time of writing this, Ghostty will not show any +# warnings or errors if you set these options in a theme file but they will +# be silently ignored. +# +# Any additional colors specified via background, foreground, palette, etc. +# will override the colors specified in the theme. +# +# To specify a different theme for light and dark mode, use the following +# syntax: `light:theme-name,dark:theme-name`. For example: +# `light:rose-pine-dawn,dark:rose-pine`. Whitespace around all values are +# trimmed and order of light and dark does not matter. Both light and dark +# must be specified in this form. In this form, the theme used will be +# based on the current desktop environment theme. +# +# There are some known bugs with light/dark mode theming. These will +# be fixed in a future update: +# +# - macOS: titlebar tabs style is not updated when switching themes. +# +theme = rose-pine + +# Background color for the window. +# Specified as either hex (`#RRGGBB` or `RRGGBB`) or a named X11 color. +# background = #282c34 + +# Foreground color for the window. +# Specified as either hex (`#RRGGBB` or `RRGGBB`) or a named X11 color. +# foreground = #ffffff + +# The foreground and background color for selection. If this is not set, then +# the selection color is just the inverted window background and foreground +# (note: not to be confused with the cell bg/fg). +# Specified as either hex (`#RRGGBB` or `RRGGBB`) or a named X11 color. +selection-foreground = + +selection-background = +# Swap the foreground and background colors of cells for selection. This +# option overrides the `selection-foreground` and `selection-background` +# options. +# +# If you select across cells with differing foregrounds and backgrounds, the +# selection color will vary across the selection. +selection-invert-fg-bg = false + +# The minimum contrast ratio between the foreground and background colors. +# The contrast ratio is a value between 1 and 21. A value of 1 allows for no +# contrast (e.g. black on black). This value is the contrast ratio as defined +# by the [WCAG 2.0 specification](https://www.w3.org/TR/WCAG20/). +# +# If you want to avoid invisible text (same color as background), a value of +# 1.1 is a good value. If you want to avoid text that is difficult to read, a +# value of 3 or higher is a good value. The higher the value, the more likely +# that text will become black or white. +# +# This value does not apply to Emoji or images. +minimum-contrast = 1 + +# Color palette for the 256 color form that many terminal applications use. +# The syntax of this configuration is `N=COLOR` where `N` is 0 to 255 (for +# the 256 colors in the terminal color table) and `COLOR` is a typical RGB +# color code such as `#AABBCC` or `AABBCC`, or a named X11 color. +# +# The palette index can be in decimal, binary, octal, or hexadecimal. +# Decimal is assumed unless a prefix is used: `0b` for binary, `0o` for octal, +# and `0x` for hexadecimal. +# +# For definitions on the color indices and what they canonically map to, +# [see this cheat sheet](https://www.ditig.com/256-colors-cheat-sheet). + +# The color of the cursor. If this is not set, a default will be chosen. +# Specified as either hex (`#RRGGBB` or `RRGGBB`) or a named X11 color. +cursor-color = + +# Swap the foreground and background colors of the cell under the cursor. This +# option overrides the `cursor-color` and `cursor-text` options. +cursor-invert-fg-bg = false + +# The opacity level (opposite of transparency) of the cursor. A value of 1 +# is fully opaque and a value of 0 is fully transparent. A value less than 0 +# or greater than 1 will be clamped to the nearest valid value. Note that a +# sufficiently small value such as 0.3 may be effectively invisible and may +# make it difficult to find the cursor. +cursor-opacity = 1 + +# The style of the cursor. This sets the default style. A running program can +# still request an explicit cursor style using escape sequences (such as `CSI +# q`). Shell configurations will often request specific cursor styles. +# +# Note that shell integration will automatically set the cursor to a bar at +# a prompt, regardless of this configuration. You can disable that behavior +# by specifying `shell-integration-features = no-cursor` or disabling shell +# integration entirely. +# +# Valid values are: +# +# * `block` +# * `bar` +# * `underline` +# * `block_hollow` +# +cursor-style = bar + +# Sets the default blinking state of the cursor. This is just the default +# state; running programs may override the cursor style using `DECSCUSR` (`CSI +# q`). +# +# If this is not set, the cursor blinks by default. Note that this is not the +# same as a "true" value, as noted below. +# +# If this is not set at all (`null`), then Ghostty will respect DEC Mode 12 +# (AT&T cursor blink) as an alternate approach to turning blinking on/off. If +# this is set to any value other than null, DEC mode 12 will be ignored but +# `DECSCUSR` will still be respected. +# +# Valid values are: +# +# * ` ` (blank) +# * `true` +# * `false` +# +cursor-style-blink = + +# The color of the text under the cursor. If this is not set, a default will +# be chosen. +# Specified as either hex (`#RRGGBB` or `RRGGBB`) or a named X11 color. +cursor-text = + +# Enables the ability to move the cursor at prompts by using `alt+click` on +# Linux and `option+click` on macOS. +# +# This feature requires shell integration (specifically prompt marking +# via `OSC 133`) and only works in primary screen mode. Alternate screen +# applications like vim usually have their own version of this feature but +# this configuration doesn't control that. +# +# It should be noted that this feature works by translating your desired +# position into a series of synthetic arrow key movements, so some weird +# behavior around edge cases are to be expected. This is unfortunately how +# this feature is implemented across terminals because there isn't any other +# way to implement it. +cursor-click-to-move = true + +# Hide the mouse immediately when typing. The mouse becomes visible again +# when the mouse is used (button, movement, etc.). Platform-specific behavior +# may dictate other scenarios where the mouse is shown. For example on macOS, +# the mouse is shown again when a new window, tab, or split is created. +mouse-hide-while-typing = false + +# Determines whether running programs can detect the shift key pressed with a +# mouse click. Typically, the shift key is used to extend mouse selection. +# +# The default value of `false` means that the shift key is not sent with +# the mouse protocol and will extend the selection. This value can be +# conditionally overridden by the running program with the `XTSHIFTESCAPE` +# sequence. +# +# The value `true` means that the shift key is sent with the mouse protocol +# but the running program can override this behavior with `XTSHIFTESCAPE`. +# +# The value `never` is the same as `false` but the running program cannot +# override this behavior with `XTSHIFTESCAPE`. The value `always` is the +# same as `true` but the running program cannot override this behavior with +# `XTSHIFTESCAPE`. +# +# If you always want shift to extend mouse selection even if the program +# requests otherwise, set this to `never`. +# +# Valid values are: +# +# * `true` +# * `false` +# * `always` +# * `never` +# +mouse-shift-capture = false + +# Multiplier for scrolling distance with the mouse wheel. Any value less +# than 0.01 or greater than 10,000 will be clamped to the nearest valid +# value. +# +# A value of "1" (default) scrolls the default amount. A value of "2" scrolls +# double the default amount. A value of "0.5" scrolls half the default amount. +# Et cetera. +mouse-scroll-multiplier = 1 + +# The opacity level (opposite of transparency) of the background. A value of +# 1 is fully opaque and a value of 0 is fully transparent. A value less than 0 +# or greater than 1 will be clamped to the nearest valid value. +# +# On macOS, background opacity is disabled when the terminal enters native +# fullscreen. This is because the background becomes gray and it can cause +# widgets to show through which isn't generally desirable. +# +# On macOS, changing this configuration requires restarting Ghostty completely. +background-opacity = 0.95 + +# Whether to blur the background when `background-opacity` is less than 1. +# +# Valid values are: +# +# * a nonnegative integer specifying the *blur intensity* +# * `false`, equivalent to a blur intensity of 0 +# * `true`, equivalent to the default blur intensity of 20, which is +# reasonable for a good looking blur. Higher blur intensities may +# cause strange rendering and performance issues. +# +# Supported on macOS and on some Linux desktop environments, including: +# +# * KDE Plasma (Wayland and X11) +# +# Warning: the exact blur intensity is _ignored_ under KDE Plasma, and setting +# this setting to either `true` or any positive blur intensity value would +# achieve the same effect. The reason is that KWin, the window compositor +# powering Plasma, only has one global blur setting and does not allow +# applications to specify individual blur settings. +# +# To configure KWin's global blur setting, open System Settings and go to +# "Apps & Windows" > "Window Management" > "Desktop Effects" and select the +# "Blur" plugin. If disabled, enable it by ticking the checkbox to the left. +# Then click on the "Configure" button and there will be two sliders that +# allow you to set background blur and noise intensities for all apps, +# including Ghostty. +# +# All other Linux desktop environments are as of now unsupported. Users may +# need to set environment-specific settings and/or install third-party plugins +# in order to support background blur, as there isn't a unified interface for +# doing so. +background-blur = true + +# The opacity level (opposite of transparency) of an unfocused split. +# Unfocused splits by default are slightly faded out to make it easier to see +# which split is focused. To disable this feature, set this value to 1. +# +# A value of 1 is fully opaque and a value of 0 is fully transparent. Because +# "0" is not useful (it makes the window look very weird), the minimum value +# is 0.15. This value still looks weird but you can at least see what's going +# on. A value outside of the range 0.15 to 1 will be clamped to the nearest +# valid value. +unfocused-split-opacity = 0.7 + +# The color to dim the unfocused split. Unfocused splits are dimmed by +# rendering a semi-transparent rectangle over the split. This sets the color of +# that rectangle and can be used to carefully control the dimming effect. +# +# This will default to the background color. +# +# Specified as either hex (`#RRGGBB` or `RRGGBB`) or a named X11 color. +unfocused-split-fill = + +# The color of the split divider. If this is not set, a default will be chosen. +# Specified as either hex (`#RRGGBB` or `RRGGBB`) or a named X11 color. +split-divider-color = + +# The command to run, usually a shell. If this is not an absolute path, it'll +# be looked up in the `PATH`. If this is not set, a default will be looked up +# from your system. The rules for the default lookup are: +# +# * `SHELL` environment variable +# +# * `passwd` entry (user information) +# +# This can contain additional arguments to run the command with. If additional +# arguments are provided, the command will be executed using `/bin/sh -c`. +# Ghostty does not do any shell command parsing. +# +# This command will be used for all new terminal surfaces, i.e. new windows, +# tabs, etc. If you want to run a command only for the first terminal surface +# created when Ghostty starts, use the `initial-command` configuration. +# +# Ghostty supports the common `-e` flag for executing a command with +# arguments. For example, `ghostty -e fish --with --custom --args`. +# This flag sets the `initial-command` configuration, see that for more +# information. +command = + +# This is the same as "command", but only applies to the first terminal +# surface created when Ghostty starts. Subsequent terminal surfaces will use +# the `command` configuration. +# +# After the first terminal surface is created (or closed), there is no +# way to run this initial command again automatically. As such, setting +# this at runtime works but will only affect the next terminal surface +# if it is the first one ever created. +# +# If you're using the `ghostty` CLI there is also a shortcut to set this +# with arguments directly: you can use the `-e` flag. For example: `ghostty -e +# fish --with --custom --args`. The `-e` flag automatically forces some +# other behaviors as well: +# +# * `gtk-single-instance=false` - This ensures that a new instance is +# launched and the CLI args are respected. +# +# * `quit-after-last-window-closed=true` - This ensures that the Ghostty +# process will exit when the command exits. Additionally, the +# `quit-after-last-window-closed-delay` is unset. +# +# * `shell-integration=detect` (if not `none`) - This prevents forcibly +# injecting any configured shell integration into the command's +# environment. With `-e` its highly unlikely that you're executing a +# shell and forced shell integration is likely to cause problems +# (e.g. by wrapping your command in a shell, setting env vars, etc.). +# This is a safety measure to prevent unexpected behavior. If you want +# shell integration with a `-e`-executed command, you must either +# name your binary appropriately or source the shell integration script +# manually. +# +initial-command = + +# If true, keep the terminal open after the command exits. Normally, the +# terminal window closes when the running command (such as a shell) exits. +# With this true, the terminal window will stay open until any keypress is +# received. +# +# This is primarily useful for scripts or debugging. +wait-after-command = false + +# The number of milliseconds of runtime below which we consider a process exit +# to be abnormal. This is used to show an error message when the process exits +# too quickly. +# +# On Linux, this must be paired with a non-zero exit code. On macOS, we allow +# any exit code because of the way shell processes are launched via the login +# command. +abnormal-command-exit-runtime = 250 + +# The size of the scrollback buffer in bytes. This also includes the active +# screen. No matter what this is set to, enough memory will always be +# allocated for the visible screen and anything leftover is the limit for +# the scrollback. +# +# When this limit is reached, the oldest lines are removed from the +# scrollback. +# +# Scrollback currently exists completely in memory. This means that the +# larger this value, the larger potential memory usage. Scrollback is +# allocated lazily up to this limit, so if you set this to a very large +# value, it will not immediately consume a lot of memory. +# +# This size is per terminal surface, not for the entire application. +# +# It is not currently possible to set an unlimited scrollback buffer. +# This is a future planned feature. +# +# This can be changed at runtime but will only affect new terminal surfaces. +scrollback-limit = 10000000 + +# Match a regular expression against the terminal text and associate clicking +# it with an action. This can be used to match URLs, file paths, etc. Actions +# can be opening using the system opener (e.g. `open` or `xdg-open`) or +# executing any arbitrary binding action. +# +# Links that are configured earlier take precedence over links that are +# configured later. +# +# A default link that matches a URL and opens it in the system opener always +# exists. This can be disabled using `link-url`. +# +# TODO: This can't currently be set! + +# Enable URL matching. URLs are matched on hover with control (Linux) or +# super (macOS) pressed and open using the default system application for +# the linked URL. +# +# The URL matcher is always lowest priority of any configured links (see +# `link`). If you want to customize URL matching, use `link` and disable this. +link-url = true + +# Whether to start the window in a maximized state. This setting applies +# to new windows and does not apply to tabs, splits, etc. However, this setting +# will apply to all new windows, not just the first one. +maximize = false + +# Start new windows in fullscreen. This setting applies to new windows and +# does not apply to tabs, splits, etc. However, this setting will apply to all +# new windows, not just the first one. +# +# On macOS, this setting does not work if window-decoration is set to +# "false", because native fullscreen on macOS requires window decorations +# to be set. +fullscreen = false + +# The title Ghostty will use for the window. This will force the title of the +# window to be this title at all times and Ghostty will ignore any set title +# escape sequences programs (such as Neovim) may send. +# +# If you want a blank title, set this to one or more spaces by quoting +# the value. For example, `title = " "`. This effectively hides the title. +# This is necessary because setting a blank value resets the title to the +# default value of the running program. +# +# This configuration can be reloaded at runtime. If it is set, the title +# will update for all windows. If it is unset, the next title change escape +# sequence will be honored but previous changes will not retroactively +# be set. This latter case may require you to restart programs such as Neovim +# to get the new title. +title = "Ghostty" + +# The setting that will change the application class value. +# +# This controls the class field of the `WM_CLASS` X11 property (when running +# under X11), and the Wayland application ID (when running under Wayland). +# +# Note that changing this value between invocations will create new, separate +# instances, of Ghostty when running with `gtk-single-instance=true`. See that +# option for more details. +# +# The class name must follow the requirements defined [in the GTK +# documentation](https://docs.gtk.org/gio/type_func.Application.id_is_valid.html). +# +# The default is `com.mitchellh.ghostty`. +# +# This only affects GTK builds. +class = "ghostty" + +# This controls the instance name field of the `WM_CLASS` X11 property when +# running under X11. It has no effect otherwise. +# +# The default is `ghostty`. +# +# This only affects GTK builds. +x11-instance-name = + +# The directory to change to after starting the command. +# +# This setting is secondary to the `window-inherit-working-directory` +# setting. If a previous Ghostty terminal exists in the same process, +# `window-inherit-working-directory` will take precedence. Otherwise, this +# setting will be used. Typically, this setting is used only for the first +# window. +# +# The default is `inherit` except in special scenarios listed next. On macOS, +# if Ghostty can detect it is launched from launchd (double-clicked) or +# `open`, then it defaults to `home`. On Linux with GTK, if Ghostty can detect +# it was launched from a desktop launcher, then it defaults to `home`. +# +# The value of this must be an absolute value or one of the special values +# below: +# +# * `home` - The home directory of the executing user. +# +# * `inherit` - The working directory of the launching process. +working-directory = + +# Key bindings. The format is `trigger=action`. Duplicate triggers will +# overwrite previously set values. The list of actions is available in +# the documentation or using the `ghostty +list-actions` command. +# +# Trigger: `+`-separated list of keys and modifiers. Example: `ctrl+a`, +# `ctrl+shift+b`, `up`. +# +# Valid keys are currently only listed in the +# [Ghostty source code](https://github.com/ghostty-org/ghostty/blob/d6e76858164d52cff460fedc61ddf2e560912d71/src/input/key.zig#L255). +# This is a documentation limitation and we will improve this in the future. +# A common gotcha is that numeric keys are written as words: e.g. `one`, +# `two`, `three`, etc. and not `1`, `2`, `3`. This will also be improved in +# the future. +# +# Valid modifiers are `shift`, `ctrl` (alias: `control`), `alt` (alias: `opt`, +# `option`), and `super` (alias: `cmd`, `command`). You may use the modifier +# or the alias. When debugging keybinds, the non-aliased modifier will always +# be used in output. +# +# Note: The fn or "globe" key on keyboards are not supported as a +# modifier. This is a limitation of the operating systems and GUI toolkits +# that Ghostty uses. +# +# Some additional notes for triggers: +# +# * modifiers cannot repeat, `ctrl+ctrl+a` is invalid. +# +# * modifiers and keys can be in any order, `shift+a+ctrl` is *weird*, +# but valid. +# +# * only a single key input is allowed, `ctrl+a+b` is invalid. +# +# * the key input can be prefixed with `physical:` to specify a +# physical key mapping rather than a logical one. A physical key +# mapping responds to the hardware keycode and not the keycode +# translated by any system keyboard layouts. Example: "ctrl+physical:a" +# +# You may also specify multiple triggers separated by `>` to require a +# sequence of triggers to activate the action. For example, +# `ctrl+a>n=new_window` will only trigger the `new_window` action if the +# user presses `ctrl+a` followed separately by `n`. In other software, this +# is sometimes called a leader key, a key chord, a key table, etc. There +# is no hardcoded limit on the number of parts in a sequence. +# +# Warning: If you define a sequence as a CLI argument to `ghostty`, +# you probably have to quote the keybind since `>` is a special character +# in most shells. Example: ghostty --keybind='ctrl+a>n=new_window' +# +# A trigger sequence has some special handling: +# +# * Ghostty will wait an indefinite amount of time for the next key in +# the sequence. There is no way to specify a timeout. The only way to +# force the output of a prefix key is to assign another keybind to +# specifically output that key (e.g. `ctrl+a>ctrl+a=text:foo`) or +# press an unbound key which will send both keys to the program. +# +# * If a prefix in a sequence is previously bound, the sequence will +# override the previous binding. For example, if `ctrl+a` is bound to +# `new_window` and `ctrl+a>n` is bound to `new_tab`, pressing `ctrl+a` +# will do nothing. +# +# * Adding to the above, if a previously bound sequence prefix is +# used in a new, non-sequence binding, the entire previously bound +# sequence will be unbound. For example, if you bind `ctrl+a>n` and +# `ctrl+a>t`, and then bind `ctrl+a` directly, both `ctrl+a>n` and +# `ctrl+a>t` will become unbound. +# +# * Trigger sequences are not allowed for `global:` or `all:`-prefixed +# triggers. This is a limitation we could remove in the future. +# +# Action is the action to take when the trigger is satisfied. It takes the +# format `action` or `action:param`. The latter form is only valid if the +# action requires a parameter. +# +# * `ignore` - Do nothing, ignore the key input. This can be used to +# black hole certain inputs to have no effect. +# +# * `unbind` - Remove the binding. This makes it so the previous action +# is removed, and the key will be sent through to the child command +# if it is printable. Unbind will remove any matching trigger, +# including `physical:`-prefixed triggers without specifying the +# prefix. +# +# * `csi:text` - Send a CSI sequence. e.g. `csi:A` sends "cursor up". +# +# * `esc:text` - Send an escape sequence. e.g. `esc:d` deletes to the +# end of the word to the right. +# +# * `text:text` - Send a string. Uses Zig string literal syntax. +# e.g. `text:\x15` sends Ctrl-U. +# +# * All other actions can be found in the documentation or by using the +# `ghostty +list-actions` command. +# +# Some notes for the action: +# +# * The parameter is taken as-is after the `:`. Double quotes or +# other mechanisms are included and NOT parsed. If you want to +# send a string value that includes spaces, wrap the entire +# trigger/action in double quotes. Example: `--keybind="up=csi:A B"` +# +# There are some additional special values that can be specified for +# keybind: +# +# * `keybind=clear` will clear all set keybindings. Warning: this +# removes ALL keybindings up to this point, including the default +# keybindings. +# +# The keybind trigger can be prefixed with some special values to change +# the behavior of the keybind. These are: +# +# * `all:` - Make the keybind apply to all terminal surfaces. By default, +# keybinds only apply to the focused terminal surface. If this is true, +# then the keybind will be sent to all terminal surfaces. This only +# applies to actions that are surface-specific. For actions that +# are already global (e.g. `quit`), this prefix has no effect. +# +# * `global:` - Make the keybind global. By default, keybinds only work +# within Ghostty and under the right conditions (application focused, +# sometimes terminal focused, etc.). If you want a keybind to work +# globally across your system (e.g. even when Ghostty is not focused), +# specify this prefix. This prefix implies `all:`. Note: this does not +# work in all environments; see the additional notes below for more +# information. +# +# * `unconsumed:` - Do not consume the input. By default, a keybind +# will consume the input, meaning that the associated encoding (if +# any) will not be sent to the running program in the terminal. If +# you wish to send the encoded value to the program, specify the +# `unconsumed:` prefix before the entire keybind. For example: +# `unconsumed:ctrl+a=reload_config`. `global:` and `all:`-prefixed +# keybinds will always consume the input regardless of this setting. +# Since they are not associated with a specific terminal surface, +# they're never encoded. +# +# * `performable:` - Only consume the input if the action is able to be +# performed. For example, the `copy_to_clipboard` action will only +# consume the input if there is a selection to copy. If there is no +# selection, Ghostty behaves as if the keybind was not set. This has +# no effect with `global:` or `all:`-prefixed keybinds. For key +# sequences, this will reset the sequence if the action is not +# performable (acting identically to not having a keybind set at +# all). +# +# Performable keybinds will not appear as menu shortcuts in the +# application menu. This is because the menu shortcuts force the +# action to be performed regardless of the state of the terminal. +# Performable keybinds will still work, they just won't appear as +# a shortcut label in the menu. +# +# Keybind triggers are not unique per prefix combination. For example, +# `ctrl+a` and `global:ctrl+a` are not two separate keybinds. The keybind +# set later will overwrite the keybind set earlier. In this case, the +# `global:` keybind will be used. +# +# Multiple prefixes can be specified. For example, +# `global:unconsumed:ctrl+a=reload_config` will make the keybind global +# and not consume the input to reload the config. +# +# Note: `global:` is only supported on macOS. On macOS, +# this feature requires accessibility permissions to be granted to Ghostty. +# When a `global:` keybind is specified and Ghostty is launched or reloaded, +# Ghostty will attempt to request these permissions. If the permissions are +# not granted, the keybind will not work. On macOS, you can find these +# permissions in System Preferences -> Privacy & Security -> Accessibility. +keybind = alt+f4=ignore +keybind = alt+one=unbind +keybind = alt+two=unbind +keybind = alt+three=unbind +keybind = alt+four=unbind +keybind = alt+five=unbind +keybind = alt+six=unbind +keybind = alt+seven=unbind +keybind = alt+eight=unbind +keybind = alt+nine=unbind +keybind = ctrl+alt+down=ignore +keybind = ctrl+alt+left=ignore +keybind = ctrl+alt+right=ignore +keybind = ctrl+alt+shift+j=write_screen_file:open +keybind = ctrl+alt+up=ignore +keybind = ctrl+comma=open_config +keybind = ctrl+enter=ignore +keybind = ctrl+equal=increase_font_size:1 +keybind = ctrl+insert=copy_to_clipboard +keybind = ctrl+minus=decrease_font_size:1 +keybind = ctrl+page_down=ignore +keybind = ctrl+page_up=ignore +keybind = ctrl+plus=ignore +keybind = ctrl+shift+a=select_all +keybind = ctrl+shift+c=copy_to_clipboard +keybind = ctrl+shift+comma=reload_config +keybind = ctrl+shift+e=ignore +keybind = ctrl+shift+enter=ignore +keybind = ctrl+shift+i=inspector:toggle +keybind = ctrl+shift+j=write_screen_file:paste +keybind = ctrl+shift+left=ignore +keybind = ctrl+shift+n=ignore +keybind = ctrl+shift+o=ignore +keybind = ctrl+shift+page_down=ignore +keybind = ctrl+shift+page_up=jump_to_prompt:-1 +keybind = ctrl+shift+q=ignore +keybind = ctrl+shift+right=ignore +keybind = ctrl+shift+t=ignore +keybind = ctrl+shift+tab=ignore +keybind = ctrl+shift+v=paste_from_clipboard +keybind = ctrl+shift+w=ignore +keybind = ctrl+tab=ignore +keybind = ctrl+zero=reset_font_size +keybind = shift+down=adjust_selection:down +keybind = shift+end=scroll_to_bottom +keybind = shift+home=scroll_to_top +keybind = shift+insert=paste_from_selection +keybind = shift+left=adjust_selection:left +keybind = shift+page_down=scroll_page_down +keybind = shift+page_up=scroll_page_up +keybind = shift+right=adjust_selection:right +keybind = shift+up=ignore +keybind = super+ctrl+left_bracket=ignore +keybind = super+ctrl+right_bracket=ignore +keybind = super+ctrl+shift+down=ignore +keybind = super+ctrl+shift+equal=ignore +keybind = super+ctrl+shift+left=ignore +keybind = super+ctrl+shift+right=ignore +keybind = super+ctrl+shift+up=ignore + +# Horizontal window padding. This applies padding between the terminal cells +# and the left and right window borders. The value is in points, meaning that +# it will be scaled appropriately for screen DPI. +# +# If this value is set too large, the screen will render nothing, because the +# grid will be completely squished by the padding. It is up to you as the user +# to pick a reasonable value. If you pick an unreasonable value, a warning +# will appear in the logs. +# +# Changing this configuration at runtime will only affect new terminals, i.e. +# new windows, tabs, etc. +# +# To set a different left and right padding, specify two numerical values +# separated by a comma. For example, `window-padding-x = 2,4` will set the +# left padding to 2 and the right padding to 4. If you want to set both +# paddings to the same value, you can use a single value. For example, +# `window-padding-x = 2` will set both paddings to 2. +window-padding-x = 2 + +# Vertical window padding. This applies padding between the terminal cells and +# the top and bottom window borders. The value is in points, meaning that it +# will be scaled appropriately for screen DPI. +# +# If this value is set too large, the screen will render nothing, because the +# grid will be completely squished by the padding. It is up to you as the user +# to pick a reasonable value. If you pick an unreasonable value, a warning +# will appear in the logs. +# +# Changing this configuration at runtime will only affect new terminals, +# i.e. new windows, tabs, etc. +# +# To set a different top and bottom padding, specify two numerical values +# separated by a comma. For example, `window-padding-y = 2,4` will set the +# top padding to 2 and the bottom padding to 4. If you want to set both +# paddings to the same value, you can use a single value. For example, +# `window-padding-y = 2` will set both paddings to 2. +window-padding-y = 2 + +# The viewport dimensions are usually not perfectly divisible by the cell +# size. In this case, some extra padding on the end of a column and the bottom +# of the final row may exist. If this is `true`, then this extra padding +# is automatically balanced between all four edges to minimize imbalance on +# one side. If this is `false`, the top left grid cell will always hug the +# edge with zero padding other than what may be specified with the other +# `window-padding` options. +# +# If other `window-padding` fields are set and this is `true`, this will still +# apply. The other padding is applied first and may affect how many grid cells +# actually exist, and this is applied last in order to balance the padding +# given a certain viewport size and grid cell size. +window-padding-balance = false + +# The color of the padding area of the window. Valid values are: +# +# * `background` - The background color specified in `background`. +# * `extend` - Extend the background color of the nearest grid cell. +# * `extend-always` - Same as "extend" but always extends without applying +# any of the heuristics that disable extending noted below. +# +# The "extend" value will be disabled in certain scenarios. On primary +# screen applications (e.g. not something like Neovim), the color will not +# be extended vertically if any of the following are true: +# +# * The nearest row has any cells that have the default background color. +# The thinking is that in this case, the default background color looks +# fine as a padding color. +# * The nearest row is a prompt row (requires shell integration). The +# thinking here is that prompts often contain powerline glyphs that +# do not look good extended. +# * The nearest row contains a perfect fit powerline character. These +# don't look good extended. +# +window-padding-color = background + +# Synchronize rendering with the screen refresh rate. If true, this will +# minimize tearing and align redraws with the screen but may cause input +# latency. If false, this will maximize redraw frequency but may cause tearing, +# and under heavy load may use more CPU and power. +# +# This defaults to true because out-of-sync rendering on macOS can +# cause kernel panics (macOS 14.4+) and performance issues for external +# displays over some hardware such as DisplayLink. If you want to minimize +# input latency, set this to false with the known aforementioned risks. +# +# Changing this value at runtime will only affect new terminals. +# +# This setting is only supported currently on macOS. +window-vsync = true + +# If true, new windows and tabs will inherit the working directory of the +# previously focused window. If no window was previously focused, the default +# working directory will be used (the `working-directory` option). +window-inherit-working-directory = true + +# If true, new windows and tabs will inherit the font size of the previously +# focused window. If no window was previously focused, the default font size +# will be used. If this is false, the default font size specified in the +# configuration `font-size` will be used. +window-inherit-font-size = true + +# Configure a preference for window decorations. This setting specifies +# a _preference_; the actual OS, desktop environment, window manager, etc. +# may override this preference. Ghostty will do its best to respect this +# preference but it may not always be possible. +# +# Valid values: +# +# * `none` - All window decorations will be disabled. Titlebar, +# borders, etc. will not be shown. On macOS, this will also disable +# tabs (enforced by the system). +# +# * `auto` - Automatically decide to use either client-side or server-side +# decorations based on the detected preferences of the current OS and +# desktop environment. This option usually makes Ghostty look the most +# "native" for your desktop. +# +# * `client` - Prefer client-side decorations. +# +# * `server` - Prefer server-side decorations. This is only relevant +# on Linux with GTK. This currently only works on Linux with Wayland +# and the `org_kde_kwin_server_decoration` protocol available (e.g. +# KDE Plasma, but almost any non-GNOME desktop supports this protocol). +# +# If `server` is set but the environment doesn't support server-side +# decorations, client-side decorations will be used instead. +# +# The default value is `auto`. +# +# For the sake of backwards compatibility and convenience, this setting also +# accepts boolean true and false values. If set to `true`, this is equivalent +# to `auto`. If set to `false`, this is equivalent to `none`. +# This is convenient for users who live primarily on systems that don't +# differentiate between client and server-side decorations (e.g. macOS and +# Windows). +# +# The "toggle_window_decorations" keybind action can be used to create +# a keybinding to toggle this setting at runtime. This will always toggle +# back to "auto" if the current value is "none" (this is an issue +# that will be fixed in the future). +# +# Changing this configuration in your configuration and reloading will +# only affect new windows. Existing windows will not be affected. +# +# macOS: To hide the titlebar without removing the native window borders +# or rounded corners, use `macos-titlebar-style = hidden` instead. +window-decoration = auto + +# The font that will be used for the application's window and tab titles. +# +# If this setting is left unset, the system default font will be used. +# +# Note: any font available on the system may be used, this font is not +# required to be a fixed-width font. +window-title-font-family = + +# The text that will be displayed in the subtitle of the window. Valid values: +# +# * `false` - Disable the subtitle. +# * `working-directory` - Set the subtitle to the working directory of the +# surface. +# +# This feature is only supported on GTK with Adwaita enabled. +window-subtitle = false + +# The theme to use for the windows. Valid values: +# +# * `auto` - Determine the theme based on the configured terminal +# background color. This has no effect if the "theme" configuration +# has separate light and dark themes. In that case, the behavior +# of "auto" is equivalent to "system". +# * `system` - Use the system theme. +# * `light` - Use the light theme regardless of system theme. +# * `dark` - Use the dark theme regardless of system theme. +# * `ghostty` - Use the background and foreground colors specified in the +# Ghostty configuration. This is only supported on Linux builds with +# Adwaita and `gtk-adwaita` enabled. +# +# On macOS, if `macos-titlebar-style` is "tabs", the window theme will be +# automatically set based on the luminosity of the terminal background color. +# This only applies to terminal windows. This setting will still apply to +# non-terminal windows within Ghostty. +# +# This is currently only supported on macOS and Linux. +window-theme = auto + +# The color space to use when interpreting terminal colors. "Terminal colors" +# refers to colors specified in your configuration and colors produced by +# direct-color SGR sequences. +# +# Valid values: +# +# * `srgb` - Interpret colors in the sRGB color space. This is the default. +# * `display-p3` - Interpret colors in the Display P3 color space. +# +# This setting is currently only supported on macOS. +window-colorspace = srgb + +# The initial window size. This size is in terminal grid cells by default. +# Both values must be set to take effect. If only one value is set, it is +# ignored. +# +# We don't currently support specifying a size in pixels but a future change +# can enable that. If this isn't specified, the app runtime will determine +# some default size. +# +# Note that the window manager may put limits on the size or override the +# size. For example, a tiling window manager may force the window to be a +# certain size to fit within the grid. There is nothing Ghostty will do about +# this, but it will make an effort. +# +# Sizes larger than the screen size will be clamped to the screen size. +# This can be used to create a maximized-by-default window size. +# +# This will not affect new tabs, splits, or other nested terminal elements. +# This only affects the initial window size of any new window. Changing this +# value will not affect the size of the window after it has been created. This +# is only used for the initial size. +# +# BUG: On Linux with GTK, the calculated window size will not properly take +# into account window decorations. As a result, the grid dimensions will not +# exactly match this configuration. If window decorations are disabled (see +# `window-decoration`), then this will work as expected. +# +# Windows smaller than 10 wide by 4 high are not allowed. +window-height = 0 + +window-width = 0 +# The starting window position. This position is in pixels and is relative +# to the top-left corner of the primary monitor. Both values must be set to take +# effect. If only one value is set, it is ignored. +# +# Note that the window manager may put limits on the position or override +# the position. For example, a tiling window manager may force the window +# to be a certain position to fit within the grid. There is nothing Ghostty +# will do about this, but it will make an effort. +# +# Also note that negative values are also up to the operating system and +# window manager. Some window managers may not allow windows to be placed +# off-screen. +# +# Invalid positions are runtime-specific, but generally the positions are +# clamped to the nearest valid position. +# +# On macOS, the window position is relative to the top-left corner of +# the visible screen area. This means that if the menu bar is visible, the +# window will be placed below the menu bar. +# +# Note: this is only supported on macOS and Linux GLFW builds. The GTK +# runtime does not support setting the window position (this is a limitation +# of GTK 4.0). +window-position-x = + +window-position-y = +# Whether to enable saving and restoring window state. Window state includes +# their position, size, tabs, splits, etc. Some window state requires shell +# integration, such as preserving working directories. See `shell-integration` +# for more information. +# +# There are three valid values for this configuration: +# +# * `default` will use the default system behavior. On macOS, this +# will only save state if the application is forcibly terminated +# or if it is configured systemwide via Settings.app. +# +# * `never` will never save window state. +# +# * `always` will always save window state whenever Ghostty is exited. +# +# If you change this value to `never` while Ghostty is not running, the next +# Ghostty launch will NOT restore the window state. +# +# If you change this value to `default` while Ghostty is not running and the +# previous exit saved state, the next Ghostty launch will still restore the +# window state. This is because Ghostty cannot know if the previous exit was +# due to a forced save or not (macOS doesn't provide this information). +# +# If you change this value so that window state is saved while Ghostty is not +# running, the previous window state will not be restored because Ghostty only +# saves state on exit if this is enabled. +# +# The default value is `default`. +# +# This is currently only supported on macOS. This has no effect on Linux. +window-save-state = default + +# Resize the window in discrete increments of the focused surface's cell size. +# If this is disabled, surfaces are resized in pixel increments. Currently +# only supported on macOS. +window-step-resize = false + +# The position where new tabs are created. Valid values: +# +# * `current` - Insert the new tab after the currently focused tab, +# or at the end if there are no focused tabs. +# +# * `end` - Insert the new tab at the end of the tab list. +window-new-tab-position = current + +# Background color for the window titlebar. This only takes effect if +# window-theme is set to ghostty. Currently only supported in the GTK app +# runtime. +# +# Specified as either hex (`#RRGGBB` or `RRGGBB`) or a named X11 color. +window-titlebar-background = + +# Foreground color for the window titlebar. This only takes effect if +# window-theme is set to ghostty. Currently only supported in the GTK app +# runtime. +# +# Specified as either hex (`#RRGGBB` or `RRGGBB`) or a named X11 color. +window-titlebar-foreground = + +# This controls when resize overlays are shown. Resize overlays are a +# transient popup that shows the size of the terminal while the surfaces are +# being resized. The possible options are: +# +# * `always` - Always show resize overlays. +# * `never` - Never show resize overlays. +# * `after-first` - The resize overlay will not appear when the surface +# is first created, but will show up if the surface is +# subsequently resized. +# +# The default is `after-first`. +resize-overlay = after-first + +# If resize overlays are enabled, this controls the position of the overlay. +# The possible options are: +# +# * `center` +# * `top-left` +# * `top-center` +# * `top-right` +# * `bottom-left` +# * `bottom-center` +# * `bottom-right` +# +# The default is `center`. +resize-overlay-position = center + +# If resize overlays are enabled, this controls how long the overlay is +# visible on the screen before it is hidden. The default is ¾ of a second or +# 750 ms. +# +# The duration is specified as a series of numbers followed by time units. +# Whitespace is allowed between numbers and units. Each number and unit will +# be added together to form the total duration. +# +# The allowed time units are as follows: +# +# * `y` - 365 SI days, or 8760 hours, or 31536000 seconds. No adjustments +# are made for leap years or leap seconds. +# * `d` - one SI day, or 86400 seconds. +# * `h` - one hour, or 3600 seconds. +# * `m` - one minute, or 60 seconds. +# * `s` - one second. +# * `ms` - one millisecond, or 0.001 second. +# * `us` or `µs` - one microsecond, or 0.000001 second. +# * `ns` - one nanosecond, or 0.000000001 second. +# +# Examples: +# * `1h30m` +# * `45s` +# +# Units can be repeated and will be added together. This means that +# `1h1h` is equivalent to `2h`. This is confusing and should be avoided. +# A future update may disallow this. +# +# The maximum value is `584y 49w 23h 34m 33s 709ms 551µs 615ns`. Any +# value larger than this will be clamped to the maximum value. +resize-overlay-duration = 750ms + +# If true, when there are multiple split panes, the mouse selects the pane +# that is focused. This only applies to the currently focused window; e.g. +# mousing over a split in an unfocused window will not focus that split +# and bring the window to front. +# +# Default is false. +focus-follows-mouse = false + +# Whether to allow programs running in the terminal to read/write to the +# system clipboard (OSC 52, for googling). The default is to allow clipboard +# reading after prompting the user and allow writing unconditionally. +# +# Valid values are: +# +# * `ask` +# * `allow` +# * `deny` +# +clipboard-read = ask + +clipboard-write = allow +# Trims trailing whitespace on data that is copied to the clipboard. This does +# not affect data sent to the clipboard via `clipboard-write`. +clipboard-trim-trailing-spaces = true + +# Require confirmation before pasting text that appears unsafe. This helps +# prevent a "copy/paste attack" where a user may accidentally execute unsafe +# commands by pasting text with newlines. +clipboard-paste-protection = true + +# If true, bracketed pastes will be considered safe. By default, bracketed +# pastes are considered safe. "Bracketed" pastes are pastes while the running +# program has bracketed paste mode enabled (a setting set by the running +# program, not the terminal emulator). +clipboard-paste-bracketed-safe = true + +# Enables or disabled title reporting (CSI 21 t). This escape sequence +# allows the running program to query the terminal title. This is a common +# security issue and is disabled by default. +# +# Warning: This can expose sensitive information at best and enable +# arbitrary code execution at worst (with a maliciously crafted title +# and a minor amount of user interaction). +title-report = false + +# The total amount of bytes that can be used for image data (e.g. the Kitty +# image protocol) per terminal screen. The maximum value is 4,294,967,295 +# (4GiB). The default is 320MB. If this is set to zero, then all image +# protocols will be disabled. +# +# This value is separate for primary and alternate screens so the effective +# limit per surface is double. +image-storage-limit = 320000000 + +# Whether to automatically copy selected text to the clipboard. `true` +# will prefer to copy to the selection clipboard, otherwise it will copy to +# the system clipboard. +# +# The value `clipboard` will always copy text to the selection clipboard +# as well as the system clipboard. +# +# Middle-click paste will always use the selection clipboard. Middle-click +# paste is always enabled even if this is `false`. +# +# The default value is true on Linux and macOS. +copy-on-select = true + +# The time in milliseconds between clicks to consider a click a repeat +# (double, triple, etc.) or an entirely new single click. A value of zero will +# use a platform-specific default. The default on macOS is determined by the +# OS settings. On every other platform it is 500ms. +click-repeat-interval = 0 + +# Additional configuration files to read. This configuration can be repeated +# to read multiple configuration files. Configuration files themselves can +# load more configuration files. Paths are relative to the file containing the +# `config-file` directive. For command-line arguments, paths are relative to +# the current working directory. +# +# Prepend a ? character to the file path to suppress errors if the file does +# not exist. If you want to include a file that begins with a literal ? +# character, surround the file path in double quotes ("). +# +# Cycles are not allowed. If a cycle is detected, an error will be logged and +# the configuration file will be ignored. +# +# Configuration files are loaded after the configuration they're defined +# within in the order they're defined. **THIS IS A VERY SUBTLE BUT IMPORTANT +# POINT.** To put it another way: configuration files do not take effect +# until after the entire configuration is loaded. For example, in the +# configuration below: +# +# ``` +# config-file = "foo" +# a = 1 +# ``` +# +# If "foo" contains `a = 2`, the final value of `a` will be 2, because +# `foo` is loaded after the configuration file that configures the +# nested `config-file` value. +config-file = + +# When this is true, the default configuration file paths will be loaded. +# The default configuration file paths are currently only the XDG +# config path ($XDG_CONFIG_HOME/ghostty/config). +# +# If this is false, the default configuration paths will not be loaded. +# This is targeted directly at using Ghostty from the CLI in a way +# that minimizes external effects. +# +# This is a CLI-only configuration. Setting this in a configuration file +# will have no effect. It is not an error, but it will not do anything. +# This configuration can only be set via CLI arguments. +config-default-files = true + +# Confirms that a surface should be closed before closing it. +# +# This defaults to `true`. If set to `false`, surfaces will close without +# any confirmation. This can also be set to `always`, which will always +# confirm closing a surface, even if shell integration says a process isn't +# running. +confirm-close-surface = false + +# Whether or not to quit after the last surface is closed. +# +# This defaults to `false` on macOS since that is standard behavior for +# a macOS application. On Linux, this defaults to `true` since that is +# generally expected behavior. +# +# On Linux, if this is `true`, Ghostty can delay quitting fully until a +# configurable amount of time has passed after the last window is closed. +# See the documentation of `quit-after-last-window-closed-delay`. +quit-after-last-window-closed = true + +# Controls how long Ghostty will stay running after the last open surface has +# been closed. This only has an effect if `quit-after-last-window-closed` is +# also set to `true`. +# +# The minimum value for this configuration is `1s`. Any values lower than +# this will be clamped to `1s`. +# +# The duration is specified as a series of numbers followed by time units. +# Whitespace is allowed between numbers and units. Each number and unit will +# be added together to form the total duration. +# +# The allowed time units are as follows: +# +# * `y` - 365 SI days, or 8760 hours, or 31536000 seconds. No adjustments +# are made for leap years or leap seconds. +# * `d` - one SI day, or 86400 seconds. +# * `h` - one hour, or 3600 seconds. +# * `m` - one minute, or 60 seconds. +# * `s` - one second. +# * `ms` - one millisecond, or 0.001 second. +# * `us` or `µs` - one microsecond, or 0.000001 second. +# * `ns` - one nanosecond, or 0.000000001 second. +# +# Examples: +# * `1h30m` +# * `45s` +# +# Units can be repeated and will be added together. This means that +# `1h1h` is equivalent to `2h`. This is confusing and should be avoided. +# A future update may disallow this. +# +# The maximum value is `584y 49w 23h 34m 33s 709ms 551µs 615ns`. Any +# value larger than this will be clamped to the maximum value. +# +# By default `quit-after-last-window-closed-delay` is unset and +# Ghostty will quit immediately after the last window is closed if +# `quit-after-last-window-closed` is `true`. +# +# Only implemented on Linux. +quit-after-last-window-closed-delay = + +# This controls whether an initial window is created when Ghostty +# is run. Note that if `quit-after-last-window-closed` is `true` and +# `quit-after-last-window-closed-delay` is set, setting `initial-window` to +# `false` will mean that Ghostty will quit after the configured delay if no +# window is ever created. Only implemented on Linux and macOS. +initial-window = true + +# The position of the "quick" terminal window. To learn more about the +# quick terminal, see the documentation for the `toggle_quick_terminal` +# binding action. +# +# Valid values are: +# +# * `top` - Terminal appears at the top of the screen. +# * `bottom` - Terminal appears at the bottom of the screen. +# * `left` - Terminal appears at the left of the screen. +# * `right` - Terminal appears at the right of the screen. +# * `center` - Terminal appears at the center of the screen. +# +# Changing this configuration requires restarting Ghostty completely. +# +# Note: There is no default keybind for toggling the quick terminal. +# To enable this feature, bind the `toggle_quick_terminal` action to a key. +quick-terminal-position = top + +# The screen where the quick terminal should show up. +# +# Valid values are: +# +# * `main` - The screen that the operating system recommends as the main +# screen. On macOS, this is the screen that is currently receiving +# keyboard input. This screen is defined by the operating system and +# not chosen by Ghostty. +# +# * `mouse` - The screen that the mouse is currently hovered over. +# +# * `macos-menu-bar` - The screen that contains the macOS menu bar as +# set in the display settings on macOS. This is a bit confusing because +# every screen on macOS has a menu bar, but this is the screen that +# contains the primary menu bar. +# +# The default value is `main` because this is the recommended screen +# by the operating system. +quick-terminal-screen = main + +# Duration (in seconds) of the quick terminal enter and exit animation. +# Set it to 0 to disable animation completely. This can be changed at +# runtime. +quick-terminal-animation-duration = 0.2 + +# Automatically hide the quick terminal when focus shifts to another window. +# Set it to false for the quick terminal to remain open even when it loses focus. +quick-terminal-autohide = true + +# This configuration option determines the behavior of the quick terminal +# when switching between macOS spaces. macOS spaces are virtual desktops +# that can be manually created or are automatically created when an +# application is in full-screen mode. +# +# Valid values are: +# +# * `move` - When switching to another space, the quick terminal will +# also moved to the current space. +# +# * `remain` - The quick terminal will stay only in the space where it +# was originally opened and will not follow when switching to another +# space. +# +# The default value is `move`. +quick-terminal-space-behavior = move + +# Whether to enable shell integration auto-injection or not. Shell integration +# greatly enhances the terminal experience by enabling a number of features: +# +# * Working directory reporting so new tabs, splits inherit the +# previous terminal's working directory. +# +# * Prompt marking that enables the "jump_to_prompt" keybinding. +# +# * If you're sitting at a prompt, closing a terminal will not ask +# for confirmation. +# +# * Resizing the window with a complex prompt usually paints much +# better. +# +# Allowable values are: +# +# * `none` - Do not do any automatic injection. You can still manually +# configure your shell to enable the integration. +# +# * `detect` - Detect the shell based on the filename. +# +# * `bash`, `elvish`, `fish`, `zsh` - Use this specific shell injection scheme. +# +# The default value is `detect`. +shell-integration = detect + +# Shell integration features to enable. These require our shell integration +# to be loaded, either automatically via shell-integration or manually. +# +# The format of this is a list of features to enable separated by commas. If +# you prefix a feature with `no-` then it is disabled. If you omit a feature, +# its default value is used, so you must explicitly disable features you don't +# want. You can also use `true` or `false` to turn all features on or off. +# +# Available features: +# +# * `cursor` - Set the cursor to a blinking bar at the prompt. +# +# * `sudo` - Set sudo wrapper to preserve terminfo. +# +# * `title` - Set the window title via shell integration. +# +# Example: `cursor`, `no-cursor`, `sudo`, `no-sudo`, `title`, `no-title` +shell-integration-features = cursor,no-sudo,title + +# Sets the reporting format for OSC sequences that request color information. +# Ghostty currently supports OSC 10 (foreground), OSC 11 (background), and +# OSC 4 (256 color palette) queries, and by default the reported values +# are scaled-up RGB values, where each component are 16 bits. This is how +# most terminals report these values. However, some legacy applications may +# require 8-bit, unscaled, components. We also support turning off reporting +# altogether. The components are lowercase hex values. +# +# Allowable values are: +# +# * `none` - OSC 4/10/11 queries receive no reply +# +# * `8-bit` - Color components are return unscaled, e.g. `rr/gg/bb` +# +# * `16-bit` - Color components are returned scaled, e.g. `rrrr/gggg/bbbb` +# +# The default value is `16-bit`. +osc-color-report-format = 16-bit + +# If true, allows the "KAM" mode (ANSI mode 2) to be used within +# the terminal. KAM disables keyboard input at the request of the +# application. This is not a common feature and is not recommended +# to be enabled. This will not be documented further because +# if you know you need KAM, you know. If you don't know if you +# need KAM, you don't need it. +vt-kam-allowed = false + +# Custom shaders to run after the default shaders. This is a file path +# to a GLSL-syntax shader for all platforms. +# +# Warning: Invalid shaders can cause Ghostty to become unusable such as by +# causing the window to be completely black. If this happens, you can +# unset this configuration to disable the shader. +# +# On Linux, this requires OpenGL 4.2. Ghostty typically only requires +# OpenGL 3.3, but custom shaders push that requirement up to 4.2. +# +# The shader API is identical to the Shadertoy API: you specify a `mainImage` +# function and the available uniforms match Shadertoy. The iChannel0 uniform +# is a texture containing the rendered terminal screen. +# +# If the shader fails to compile, the shader will be ignored. Any errors +# related to shader compilation will not show up as configuration errors +# and only show up in the log, since shader compilation happens after +# configuration loading on the dedicated render thread. For interactive +# development, use [shadertoy.com](https://shadertoy.com). +# +# This can be repeated multiple times to load multiple shaders. The shaders +# will be run in the order they are specified. +# +# Changing this value at runtime and reloading the configuration will only +# affect new windows, tabs, and splits. +custom-shader = + +# If `true` (default), the focused terminal surface will run an animation +# loop when custom shaders are used. This uses slightly more CPU (generally +# less than 10%) but allows the shader to animate. This only runs if there +# are custom shaders and the terminal is focused. +# +# If this is set to `false`, the terminal and custom shader will only render +# when the terminal is updated. This is more efficient but the shader will +# not animate. +# +# This can also be set to `always`, which will always run the animation +# loop regardless of whether the terminal is focused or not. The animation +# loop will still only run when custom shaders are used. Note that this +# will use more CPU per terminal surface and can become quite expensive +# depending on the shader and your terminal usage. +# +# This value can be changed at runtime and will affect all currently +# open terminals. +custom-shader-animation = true + +# Control the in-app notifications that Ghostty shows. +# +# On Linux (GTK) with Adwaita, in-app notifications show up as toasts. Toasts +# appear overlaid on top of the terminal window. They are used to show +# information that is not critical but may be important. +# +# Possible notifications are: +# +# - `clipboard-copy` (default: true) - Show a notification when text is copied +# to the clipboard. +# +# To specify a notification to enable, specify the name of the notification. +# To specify a notification to disable, prefix the name with `no-`. For +# example, to disable `clipboard-copy`, set this configuration to +# `no-clipboard-copy`. To enable it, set this configuration to `clipboard-copy`. +# +# Multiple notifications can be enabled or disabled by separating them +# with a comma. +# +# A value of "false" will disable all notifications. A value of "true" will +# enable all notifications. +# +# This configuration only applies to GTK with Adwaita enabled. +app-notifications = clipboard-copy + +# If anything other than false, fullscreen mode on macOS will not use the +# native fullscreen, but make the window fullscreen without animations and +# using a new space. It's faster than the native fullscreen mode since it +# doesn't use animations. +# +# Important: tabs DO NOT WORK in this mode. Non-native fullscreen removes +# the titlebar and macOS native tabs require the titlebar. If you use tabs, +# you should not use this mode. +# +# If you fullscreen a window with tabs, the currently focused tab will +# become fullscreen while the others will remain in a separate window in +# the background. You can switch to that window using normal window-switching +# keybindings such as command+tilde. When you exit fullscreen, the window +# will return to the tabbed state it was in before. +# +# Allowable values are: +# +# * `visible-menu` - Use non-native macOS fullscreen, keep the menu bar visible +# * `true` - Use non-native macOS fullscreen, hide the menu bar +# * `false` - Use native macOS fullscreen +# +# Changing this option at runtime works, but will only apply to the next +# time the window is made fullscreen. If a window is already fullscreen, +# it will retain the previous setting until fullscreen is exited. +macos-non-native-fullscreen = false + +# The style of the macOS titlebar. Available values are: "native", +# "transparent", "tabs", and "hidden". +# +# The "native" style uses the native macOS titlebar with zero customization. +# The titlebar will match your window theme (see `window-theme`). +# +# The "transparent" style is the same as "native" but the titlebar will +# be transparent and allow your window background color to come through. +# This makes a more seamless window appearance but looks a little less +# typical for a macOS application and may not work well with all themes. +# +# The "transparent" style will also update in real-time to dynamic +# changes to the window background color, e.g. via OSC 11. To make this +# more aesthetically pleasing, this only happens if the terminal is +# a window, tab, or split that borders the top of the window. This +# avoids a disjointed appearance where the titlebar color changes +# but all the topmost terminals don't match. +# +# The "tabs" style is a completely custom titlebar that integrates the +# tab bar into the titlebar. This titlebar always matches the background +# color of the terminal. There are some limitations to this style: +# On macOS 13 and below, saved window state will not restore tabs correctly. +# macOS 14 does not have this issue and any other macOS version has not +# been tested. +# +# The "hidden" style hides the titlebar. Unlike `window-decoration = false`, +# however, it does not remove the frame from the window or cause it to have +# squared corners. Changing to or from this option at run-time may affect +# existing windows in buggy ways. +# +# When "hidden", the top titlebar area can no longer be used for dragging +# the window. To drag the window, you can use option+click on the resizable +# areas of the frame to drag the window. This is a standard macOS behavior +# and not something Ghostty enables. +# +# The default value is "transparent". This is an opinionated choice +# but its one I think is the most aesthetically pleasing and works in +# most cases. +# +# Changing this option at runtime only applies to new windows. +macos-titlebar-style = transparent + +# Whether the proxy icon in the macOS titlebar is visible. The proxy icon +# is the icon that represents the folder of the current working directory. +# You can see this very clearly in the macOS built-in Terminal.app +# titlebar. +# +# The proxy icon is only visible with the native macOS titlebar style. +# +# Valid values are: +# +# * `visible` - Show the proxy icon. +# * `hidden` - Hide the proxy icon. +# +# The default value is `visible`. +# +# This setting can be changed at runtime and will affect all currently +# open windows but only after their working directory changes again. +# Therefore, to make this work after changing the setting, you must +# usually `cd` to a different directory, open a different file in an +# editor, etc. +macos-titlebar-proxy-icon = visible + +# macOS doesn't have a distinct "alt" key and instead has the "option" +# key which behaves slightly differently. On macOS by default, the +# option key plus a character will sometimes produces a Unicode character. +# For example, on US standard layouts option-b produces "∫". This may be +# undesirable if you want to use "option" as an "alt" key for keybindings +# in terminal programs or shells. +# +# This configuration lets you change the behavior so that option is treated +# as alt. +# +# The default behavior (unset) will depend on your active keyboard +# layout. If your keyboard layout is one of the keyboard layouts listed +# below, then the default value is "true". Otherwise, the default +# value is "false". Keyboard layouts with a default value of "true" are: +# +# - U.S. Standard +# - U.S. International +# +# Note that if an *Option*-sequence doesn't produce a printable character, it +# will be treated as *Alt* regardless of this setting. (e.g. `alt+ctrl+a`). +# +# Explicit values that can be set: +# +# If `true`, the *Option* key will be treated as *Alt*. This makes terminal +# sequences expecting *Alt* to work properly, but will break Unicode input +# sequences on macOS if you use them via the *Alt* key. +# +# You may set this to `false` to restore the macOS *Alt* key unicode +# sequences but this will break terminal sequences expecting *Alt* to work. +# +# The values `left` or `right` enable this for the left or right *Option* +# key, respectively. +# +# This does not work with GLFW builds. +macos-option-as-alt = + +# Whether to enable the macOS window shadow. The default value is true. +# With some window managers and window transparency settings, you may +# find false more visually appealing. +macos-window-shadow = true + +# If true, Ghostty on macOS will automatically enable the "Secure Input" +# feature when it detects that a password prompt is being displayed. +# +# "Secure Input" is a macOS security feature that prevents applications from +# reading keyboard events. This can always be enabled manually using the +# `Ghostty > Secure Keyboard Entry` menu item. +# +# Note that automatic password prompt detection is based on heuristics +# and may not always work as expected. Specifically, it does not work +# over SSH connections, but there may be other cases where it also +# doesn't work. +# +# A reason to disable this feature is if you find that it is interfering +# with legitimate accessibility software (or software that uses the +# accessibility APIs), since secure input prevents any application from +# reading keyboard events. +macos-auto-secure-input = true + +# If true, Ghostty will show a graphical indication when secure input is +# enabled. This indication is generally recommended to know when secure input +# is enabled. +# +# Normally, secure input is only active when a password prompt is displayed +# or it is manually (and typically temporarily) enabled. However, if you +# always have secure input enabled, the indication can be distracting and +# you may want to disable it. +macos-secure-input-indication = true + +# Customize the macOS app icon. +# +# This only affects the icon that appears in the dock, application +# switcher, etc. This does not affect the icon in Finder because +# that is controlled by a hardcoded value in the signed application +# bundle and can't be changed at runtime. For more details on what +# exactly is affected, see the `NSApplication.icon` Apple documentation; +# that is the API that is being used to set the icon. +# +# Valid values: +# +# * `official` - Use the official Ghostty icon. +# * `custom-style` - Use the official Ghostty icon but with custom +# styles applied to various layers. The custom styles must be +# specified using the additional `macos-icon`-prefixed configurations. +# The `macos-icon-ghost-color` and `macos-icon-screen-color` +# configurations are required for this style. +# +# WARNING: The `custom-style` option is _experimental_. We may change +# the format of the custom styles in the future. We're still finalizing +# the exact layers and customization options that will be available. +# +# Other caveats: +# +# * The icon in the update dialog will always be the official icon. +# This is because the update dialog is managed through a +# separate framework and cannot be customized without significant +# effort. +# +macos-icon = official + +# The material to use for the frame of the macOS app icon. +# +# Valid values: +# +# * `aluminum` - A brushed aluminum frame. This is the default. +# * `beige` - A classic 90's computer beige frame. +# * `plastic` - A glossy, dark plastic frame. +# * `chrome` - A shiny chrome frame. +# +# This only has an effect when `macos-icon` is set to `custom-style`. +macos-icon-frame = aluminum + +# The color of the ghost in the macOS app icon. +# +# Note: This configuration is required when `macos-icon` is set to +# `custom-style`. +# +# This only has an effect when `macos-icon` is set to `custom-style`. +# +# Specified as either hex (`#RRGGBB` or `RRGGBB`) or a named X11 color. +macos-icon-ghost-color = + +# The color of the screen in the macOS app icon. +# +# The screen is a gradient so you can specify multiple colors that +# make up the gradient. Comma-separated colors may be specified as +# as either hex (`#RRGGBB` or `RRGGBB`) or as named X11 colors. +# +# Note: This configuration is required when `macos-icon` is set to +# `custom-style`. +# +# This only has an effect when `macos-icon` is set to `custom-style`. +macos-icon-screen-color = + +# Put every surface (tab, split, window) into a dedicated Linux cgroup. +# +# This makes it so that resource management can be done on a per-surface +# granularity. For example, if a shell program is using too much memory, +# only that shell will be killed by the oom monitor instead of the entire +# Ghostty process. Similarly, if a shell program is using too much CPU, +# only that surface will be CPU-throttled. +# +# This will cause startup times to be slower (a hundred milliseconds or so), +# so the default value is "single-instance." In single-instance mode, only +# one instance of Ghostty is running (see gtk-single-instance) so the startup +# time is a one-time cost. Additionally, single instance Ghostty is much +# more likely to have many windows, tabs, etc. so cgroup isolation is a +# big benefit. +# +# This feature requires systemd. If systemd is unavailable, cgroup +# initialization will fail. By default, this will not prevent Ghostty +# from working (see linux-cgroup-hard-fail). +# +# Valid values are: +# +# * `never` - Never use cgroups. +# * `always` - Always use cgroups. +# * `single-instance` - Enable cgroups only for Ghostty instances launched +# as single-instance applications (see gtk-single-instance). +# +linux-cgroup = single-instance + +# Memory limit for any individual terminal process (tab, split, window, +# etc.) in bytes. If this is unset then no memory limit will be set. +# +# Note that this sets the "memory.high" configuration for the memory +# controller, which is a soft limit. You should configure something like +# systemd-oom to handle killing processes that have too much memory +# pressure. +linux-cgroup-memory-limit = + +# Number of processes limit for any individual terminal process (tab, split, +# window, etc.). If this is unset then no limit will be set. +# +# Note that this sets the "pids.max" configuration for the process number +# controller, which is a hard limit. +linux-cgroup-processes-limit = + +# If this is false, then any cgroup initialization (for linux-cgroup) +# will be allowed to fail and the failure is ignored. This is useful if +# you view cgroup isolation as a "nice to have" and not a critical resource +# management feature, because Ghostty startup will not fail if cgroup APIs +# fail. +# +# If this is true, then any cgroup initialization failure will cause +# Ghostty to exit or new surfaces to not be created. +# +# Note: This currently only affects cgroup initialization. Subprocesses +# must always be able to move themselves into an isolated cgroup. +linux-cgroup-hard-fail = false + +# Enable or disable GTK's OpenGL debugging logs. The default is `true` for +# debug builds, `false` for all others. +gtk-opengl-debug = false + +# After GTK 4.14.0, we need to force the GSK renderer to OpenGL as the default +# GSK renderer is broken on some systems. If you would like to override +# that bekavior, set `gtk-gsk-renderer=default` and either use your system's +# default GSK renderer, or set the GSK_RENDERER environment variable to your +# renderer of choice before launching Ghostty. This setting has no effect when +# using versions of GTK earlier than 4.14.0. +gtk-gsk-renderer = opengl + +# If `true`, the Ghostty GTK application will run in single-instance mode: +# each new `ghostty` process launched will result in a new window if there is +# already a running process. +# +# If `false`, each new ghostty process will launch a separate application. +# +# The default value is `desktop` which will default to `true` if Ghostty +# detects that it was launched from the `.desktop` file such as an app +# launcher (like Gnome Shell) or by D-Bus activation. If Ghostty is launched +# from the command line, it will default to `false`. +# +# Note that debug builds of Ghostty have a separate single-instance ID +# so you can test single instance without conflicting with release builds. +gtk-single-instance = desktop + +# When enabled, the full GTK titlebar is displayed instead of your window +# manager's simple titlebar. The behavior of this option will vary with your +# window manager. +# +# This option does nothing when `window-decoration` is false or when running +# under macOS. +# +# Changing this value at runtime and reloading the configuration will only +# affect new windows. +gtk-titlebar = false + +# Determines the side of the screen that the GTK tab bar will stick to. +# Top, bottom, left, right, and hidden are supported. The default is top. +# +# If this option has value `left` or `right` when using Adwaita, it falls +# back to `top`. `hidden`, meaning that tabs don't exist, is not supported +# without using Adwaita, falling back to `top`. +# +# When `hidden` is set and Adwaita is enabled, a tab button displaying the +# number of tabs will appear in the title bar. It has the ability to open a +# tab overview for displaying tabs. Alternatively, you can use the +# `toggle_tab_overview` action in a keybind if your window doesn't have a +# title bar, or you can switch tabs with keybinds. +gtk-tabs-location = top + +# If this is `true`, the titlebar will be hidden when the window is maximized, +# and shown when the titlebar is unmaximized. GTK only. +gtk-titlebar-hide-when-maximized = false + +# Determines the appearance of the top and bottom bars when using the +# Adwaita tab bar. This requires `gtk-adwaita` to be enabled (it is +# by default). +# +# Valid values are: +# +# * `flat` - Top and bottom bars are flat with the terminal window. +# * `raised` - Top and bottom bars cast a shadow on the terminal area. +# * `raised-border` - Similar to `raised` but the shadow is replaced with a +# more subtle border. +# +# Changing this value at runtime will only affect new windows. +adw-toolbar-style = raised + +# If `true` (default), then the Ghostty GTK tabs will be "wide." Wide tabs +# are the new typical Gnome style where tabs fill their available space. +# If you set this to `false` then tabs will only take up space they need, +# which is the old style. +gtk-wide-tabs = true + +# If `true` (default), Ghostty will enable Adwaita theme support. This +# will make `window-theme` work properly and will also allow Ghostty to +# properly respond to system theme changes, light/dark mode changing, etc. +# This requires a GTK4 desktop with a GTK4 theme. +# +# If you are running GTK3 or have a GTK3 theme, you may have to set this +# to false to get your theme picked up properly. Having this set to true +# with GTK3 should not cause any problems, but it may not work exactly as +# expected. +# +# This configuration only has an effect if Ghostty was built with +# Adwaita support. +gtk-adwaita = true + +# Custom CSS files to be loaded. +# +# This configuration can be repeated multiple times to load multiple files. +# Prepend a ? character to the file path to suppress errors if the file does +# not exist. If you want to include a file that begins with a literal ? +# character, surround the file path in double quotes ("). +# The file size limit for a single stylesheet is 5MiB. +gtk-custom-css = + +# If `true` (default), applications running in the terminal can show desktop +# notifications using certain escape sequences such as OSC 9 or OSC 777. +desktop-notifications = true + +# If `true`, the bold text will use the bright color palette. +bold-is-bright = true + +# This will be used to set the `TERM` environment variable. +# HACK: We set this with an `xterm` prefix because vim uses that to enable key +# protocols (specifically this will enable `modifyOtherKeys`), among other +# features. An option exists in vim to modify this: `:set +# keyprotocol=ghostty:kitty`, however a bug in the implementation prevents it +# from working properly. https://github.com/vim/vim/pull/13211 fixes this. +term = xterm-ghostty + +# String to send when we receive `ENQ` (`0x05`) from the command that we are +# running. Defaults to an empty string if not set. +enquiry-response = + +# Control the auto-update functionality of Ghostty. This is only supported +# on macOS currently, since Linux builds are distributed via package +# managers that are not centrally controlled by Ghostty. +# +# Checking or downloading an update does not send any information to +# the project beyond standard network information mandated by the +# underlying protocols. To put it another way: Ghostty doesn't explicitly +# add any tracking to the update process. The update process works by +# downloading information about the latest version and comparing it +# client-side to the current version. +# +# Valid values are: +# +# * `off` - Disable auto-updates. +# * `check` - Check for updates and notify the user if an update is +# available, but do not automatically download or install the update. +# * `download` - Check for updates, automatically download the update, +# notify the user, but do not automatically install the update. +# +# If unset, we defer to Sparkle's default behavior, which respects the +# preference stored in the standard user defaults (`defaults(1)`). +# +# Changing this value at runtime works after a small delay. +auto-update = + +# The release channel to use for auto-updates. +# +# The default value of this matches the release channel of the currently +# running Ghostty version. If you download a pre-release version of Ghostty +# then this will be set to `tip` and you will receive pre-release updates. +# If you download a stable version of Ghostty then this will be set to +# `stable` and you will receive stable updates. +# +# Valid values are: +# +# * `stable` - Stable, tagged releases such as "1.0.0". +# * `tip` - Pre-release versions generated from each commit to the +# main branch. This is the version that was in use during private +# beta testing by thousands of people. It is generally stable but +# will likely have more bugs than the stable channel. +# +# Changing this configuration requires a full restart of +# Ghostty to take effect. +# +# This only works on macOS since only macOS has an auto-update feature. +auto-update-channel = + diff --git a/config/git/config b/config/git/config new file mode 100644 index 00000000..f6acb8e9 --- /dev/null +++ b/config/git/config @@ -0,0 +1,56 @@ +[user] + email = dev@kristofers.xyz + name = Kristofers Solo +[init] + defaultBranch = main +[core] + editor = nvim + +# https://blog.gitbutler.com/how-git-core-devs-configure-git/ +# clearly makes git better +[column] + ui = auto +[branch] + sort = -committerdate +[tag] + sort = version:refname +[diff] + algorithm = histogram + colorMoved = plain + mnemonicPrefix = true + renames = true +[push] + default = simple + autoSetupRemote = true + followTags = true +[fetch] + prune = true + pruneTags = true + all = true + +# why the hell not? + +[help] + autocorrect = prompt +[commit] + verbose = true +[rerere] + enabled = true + autoupdate = true +[core] + excludesfile = ~/.gitignore +[rebase] + autoSquash = true + autoStash = true + updateRefs = true + +# a matter of taste (uncomment if you dare) + +[core] + # fsmonitor = true + # untrackedCache = true +[merge] + # (just 'diff3' if git version < 2.3) + # conflictstyle = zdiff3 +[pull] + # rebase = true diff --git a/config/hypr/hyprland.conf b/config/hypr/hyprland.conf index 125ecd65..50aaa38f 100644 --- a/config/hypr/hyprland.conf +++ b/config/hypr/hyprland.conf @@ -1,4 +1,4 @@ -# This is an example Hyprland config file. +# This is an example Hyprland config file.hypr # Refer to the wiki for more information. # https://wiki.hyprland.org/Configuring/ @@ -15,8 +15,9 @@ ################ # See https://wiki.hyprland.org/Configuring/Monitors/ -monitor=eDP-1,prefered,auto,1.5 -monitor=DP-1,prefered,auto,1 +monitor=eDP-1,prefered,0x0,1 +monitor=DP-1,prefered,180x-1440,1 +# monitor=DP-1,disabled ################### ### MY PROGRAMS ### @@ -30,7 +31,6 @@ $fileManager = $terminal -e yazi $menu = wofi --show drun -ib $browser = zen-browser - ################# ### AUTOSTART ### ################# @@ -38,8 +38,8 @@ $browser = zen-browser # Autostart necessary processes (like notifications daemons, status bars, etc.) # Or execute your favorite apps at launch like this: -exec-once=pipewire & pipewire-pulse & wireplumber & eww daemon & eww open bar & dbus-update-activation-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP DISPLAY XAUTHORITY -exec-once=nextcloud & /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1 & xrdb "$XDG_CONFIG_HOME/x11/xresources" & transmission-daemon & dunst & hyprpaper +exec-once=pipewire & pipewire-pulse & wireplumber & eww daemon & eww open bar #& dbus-update-activation-environment WAYLAND_DISPLAY XDG_CURRENT_DESKTOP DISPLAY XAUTHORITY +exec-once=nextcloud & /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1 & xrdb "$XDG_CONFIG_HOME/x11/xresources" & transmission-daemon & dunst & hyprland-per-window-layout exec-once=[workspace 1 silent] $browser exec-once=[workspace 8 silent] vesktop & kotatogram-desktop exec-once=[workspace 9 silent] spotify-launcher @@ -55,6 +55,11 @@ source=~/.config/hypr/rose-pine.conf env = XCURSOR_SIZE,24 env = HYPRCURSOR_SIZE,24 +# env = DBUS_SESSION_BUS_ADDRESS,unix:path=/run/user/1000/bus +env = XDG_CURRENT_DESKTOP,Hyprland +env = XDG_SESSION_TYPE,wayland +env = XDG_SESSION_DESKTOP,Hyprland +env = WM,Hyprland ##################### @@ -218,11 +223,12 @@ bind = $mainMod SHIFT, Q, killactive bind = $mainMod, N, exec, $fileManager bind = $mainMod CTRL, SPACE, togglefloating bind = $mainMod, P, exec, $menu +bind = $mainMod SHIFT, P, exec, cliphist list | wofi -S dmenu | cliphist decode | wl-copy bind = $mainMod, F, fullscreen bind = $mainMod, M, bringactivetotop bind = $mainMod, A, pin bind = $mainMod, B, exec, $browser -#bind = $mainMod, O, split-changemonitor, +1 +# bind = $mainMod, O, split-changemonitor, +1 # bind = $mainMod, P, pseudo, # dwindle # bind = $mainMod, J, togglesplit, # dwindle @@ -254,6 +260,16 @@ bind = $mainMod, 7, workspace, 7 bind = $mainMod, 8, workspace, 8 bind = $mainMod, 9, workspace, 9 bind = $mainMod, 0, workspace, 10 +# bind = $mainMod, 1, split-workspace, 1 +# bind = $mainMod, 2, split-workspace, 2 +# bind = $mainMod, 3, split-workspace, 3 +# bind = $mainMod, 4, split-workspace, 4 +# bind = $mainMod, 5, split-workspace, 5 +# bind = $mainMod, 6, split-workspace, 6 +# bind = $mainMod, 7, split-workspace, 7 +# bind = $mainMod, 8, split-workspace, 8 +# bind = $mainMod, 9, split-workspace, 9 +# bind = $mainMod, 0, split-workspace, 10 # Move active window to a workspace with mainMod + SHIFT + [0-9] bind = $mainMod SHIFT, 1, movetoworkspace, 1 @@ -266,6 +282,16 @@ bind = $mainMod SHIFT, 7, movetoworkspace, 7 bind = $mainMod SHIFT, 8, movetoworkspace, 8 bind = $mainMod SHIFT, 9, movetoworkspace, 9 bind = $mainMod SHIFT, 0, movetoworkspace, 10 +# bind = $mainMod SHIFT, 1, split-movetoworkspace, 1 +# bind = $mainMod SHIFT, 2, split-movetoworkspace, 2 +# bind = $mainMod SHIFT, 3, split-movetoworkspace, 3 +# bind = $mainMod SHIFT, 4, split-movetoworkspace, 4 +# bind = $mainMod SHIFT, 5, split-movetoworkspace, 5 +# bind = $mainMod SHIFT, 6, split-movetoworkspace, 6 +# bind = $mainMod SHIFT, 7, split-movetoworkspace, 7 +# bind = $mainMod SHIFT, 8, split-movetoworkspace, 8 +# bind = $mainMod SHIFT, 9, split-movetoworkspace, 9 +# bind = $mainMod SHIFT, 0, split-movetoworkspace, 10 # Example special workspace (scratchpad) # bind = $mainMod, S, togglespecialworkspace, magic @@ -305,12 +331,13 @@ bindl = , XF86AudioPrev, exec, playerctl previous bindel = , XF86AudioPlay, exec, playerctl -a play-pause # pause/play everything bindel = $mainMod, Space, exec, hyprlock +bindl = , switch:[Lid Switch], exec, hyprlock # bind = $mainMod CTRL, P, exec, wayshot -f "$(HOME)/Pictures/screenshots/$(date + '%s.png')" -s "$(slurp -f '%x %y %w %y')" --stdout | wl-copy # bind = $mainMod CTRL, P, exec, wayshot -s "$(slurp -f '%x %y %w %y')" --stdout | wl-copy # bind = $mainMod, , exec, wayshot -f "$(HOME)/Pictures/screenshots/$(date + '%s.png')" bind = CTRL, PRINT, exec, hyprshot -m region -bind = , PRINT, exec, hyprshot -m output +bind = , PRINT, exec, hyprshot -m output bind = SHIFT, PRINT, exec, hyprshot -m window bind = $mainMod ALT, P, exec, hyprpicker -a # colorpicker @@ -356,9 +383,13 @@ windowrulev2 = opacity 0.9, class:teams-for-linux windowrulev2 = workspace 5, class:Ferdium windowrulev2 = opacity 0.9, class:Ferdium +windowrulev2 = workspace 9, class:com.github.wwmm.easyeffects windowrulev2 = workspace 9, class:Spotify windowrulev2 = opacity 0.9, class:Spotify -windowrulev2 = tile, class:Spotify +# windowrulev2 = tile, class:Spotify +# https://github.com/alexhulbert/Hyprchroma +# windowrulev2 = plugin:chromakey, class:Spotify +# chromakey_background = 7,8,17 windowrulev2 = opacity 0.95, class:qbittorrent windowrulev2 = opacity 0.95, class:lutris @@ -374,4 +405,3 @@ windowrulev2 = float, class:galculator windowrulev2=float,title:^(flameshot) windowrulev2=move 0 0,title:^(flameshot) windowrulev2=suppressevent fullscreen,title:^(flameshot) - diff --git a/config/hypr/hyprlock.conf b/config/hypr/hyprlock.conf index 22d0a6aa..06b61a1a 100644 --- a/config/hypr/hyprlock.conf +++ b/config/hypr/hyprlock.conf @@ -1,15 +1,25 @@ $font = JetBrainsMonoNF -$bg = rgb(1a1b26) -$fg = rgb(c0caf5) -$border_highlight = rgb(27a1b9) -$error = rgb(db4b4b) -$warning = rgb(e0af68) +$base = rgb(191724) +$surface = rgb(1f1d2e) +$overlay = rgb(26233a) +$muted = rgb(6e6a86) +$subtle = rgb(908caa) +$text = rgb(e0def4) +$love = rgb(eb6f92) +$gold = rgb(f6c177) +$rose = rgb(ebbcba) +$pine = rgb(31748f) +$foam = rgb(9ccfd8) +$iris = rgb(c4a7e7) +$highlight_low = rgb(21202e) +$highlight_med = rgb(403d52) +$highlight_high = rgb(524f67) background { monitor = path = screenshot - color = $bg + color = $base blur_passes = 1 blur_size = 10 @@ -23,7 +33,7 @@ background { # TIME label { monitor = - text = cmd[update:1000] echo "$(date +"%H:%M:%S")" + text = cmd[update:1000] echo "$(date +"%H:%M:%S")" color = $text font_size = 90 font_family = $font @@ -32,10 +42,10 @@ label { valign = top } -# DATE +# DATE label { - monitor = - text = cmd[update:43200000] echo "$(date +"%d.%m.%Y")" + monitor = + text = cmd[update:43200000] echo "$(date +"%d.%m.%Y")" color = $text font_size = 25 font_family = $font @@ -46,10 +56,10 @@ label { image { - monitor = + monitor = path = ~/.local/share/profile size = 300 - border_color = $border_highlight + border_color = $pine position = 0, 200 halign = center @@ -59,21 +69,21 @@ image { input-field { monitor = size = 300, 60 - outline_thickness = 4 + outline_thickness = 3 dots_size = 0.2 dots_spacing = 0.2 dots_center = true dots_rounding = -1 # -1 default circle, -2 follow input-field rounding - outer_color = $border_highlight - inner_color = $fg - font_color = $bg + outer_color = $pine + inner_color = $text + font_color = $base fade_on_empty = true fade_timeout = 1000 # Milliseconds before fade_on_empty is triggered. placeholder_text = Input Password... # Text rendered in the input box when it's empty. hide_input = false rounding = -1 # -1 means complete rounding (circle/oval) - check_color = rgb(e0af68) - fail_color = rgb(db4b4b) # if authentication failed, changes outer_color and fail message color + check_color = $gold + fail_color = $love # if authentication failed, changes outer_color and fail message color fail_text = $FAIL ($ATTEMPTS) # can be set to empty fail_transition = 300 # transition time in ms between normal outer_color and fail_color capslock_color = -1 diff --git a/config/hypr/hyprpaper.conf b/config/hypr/hyprpaper.conf index 972e965d..7329a7bd 100644 --- a/config/hypr/hyprpaper.conf +++ b/config/hypr/hyprpaper.conf @@ -2,5 +2,6 @@ ipc = off splash = true preload = ~/Pictures/wallpapers/Linux-Dynamic-Wallpapers/Firewatch2/Firewatch2-1.png preload = ~/Pictures/wallpapers/Linux-Dynamic-Wallpapers/LakesideDeerComplete/LakesideDeer-03.png +preload = ~/Pictures/wallpapers/abstract/GDWP-789-4K-No-Logo.jpg -wallpaper = ,~/Pictures/wallpapers/Linux-Dynamic-Wallpapers/LakesideDeerComplete/LakesideDeer-03.png +wallpaper = ,~/Pictures/wallpapers/abstract/GDWP-789-4K-No-Logo.jpg diff --git a/config/lf/cleaner b/config/lf/cleaner old mode 100755 new mode 100644 diff --git a/config/lf/lfrc b/config/lf/lfrc old mode 100755 new mode 100644 diff --git a/config/nsxiv/exec/image-info b/config/nsxiv/exec/image-info old mode 100755 new mode 100644 diff --git a/config/nsxiv/exec/key-handler b/config/nsxiv/exec/key-handler old mode 100755 new mode 100644 diff --git a/config/nsxiv/exec/nsxiv-url b/config/nsxiv/exec/nsxiv-url old mode 100755 new mode 100644 diff --git a/config/nsxiv/exec/thumb-info b/config/nsxiv/exec/thumb-info old mode 100755 new mode 100644 diff --git a/config/nsxiv/exec/win-title b/config/nsxiv/exec/win-title old mode 100755 new mode 100644 diff --git a/config/shell/env b/config/shell/env index 7f1d5b2b..f3fb36e9 100644 --- a/config/shell/env +++ b/config/shell/env @@ -17,13 +17,12 @@ export LESSHISTFILE=- export $(dbus-launch) export LIBSEAT_BACKEND=logind -# export XDG_RUNTIME_DIR="/run/user/$UID" unsetopt PROMPT_SP # Default Apps -export BROWSER="floorp" +export BROWSER="zen-browser" export EDITOR="nvim" export IMAGE="nsxiv" export READER="zathura" @@ -87,8 +86,8 @@ export XAUTHORITY="$XDG_RUNTIME_DIR/Xauthority" export XCURSOR_PATH="/usr/share/icons:$XDG_DATA_HOME/icons" export XINITRC="$XDG_CONFIG_HOME/x11/xinitrc" export ZDOTDIR="$XDG_CONFIG_HOME/zsh" -export _JAVA_OPTIONS="-Djava.util.prefs.userRoot=${XDG_CONFIG_HOME}/java - Djavafx.cachedir=${XDG_CACHE_HOME}/openjfx" -export _JAVA_OPTIONS=-Djava.util.prefs.userRoot="$XDG_CONFIG_HOME/java" +# export _JAVA_OPTIONS="-Djava.util.prefs.userRoot=${XDG_CONFIG_HOME}/java - Djavafx.cachedir=${XDG_CACHE_HOME}/openjfx" +# export _JAVA_OPTIONS=-Djava.util.prefs.userRoot="$XDG_CONFIG_HOME/java" # Other program settings export AWT_TOOLKIT="MToolkit wmname LG3D" # May have to install wmname @@ -112,6 +111,5 @@ export SUDO_ASKPASS="$HOME/.local/bin/dmenupass" export _JAVA_AWT_WM_NONREPARENTING=1 # Fix for Java applications in dwm . "$XDG_DATA_HOME/cargo/env" -. "$XDG_DATA_HOME/rye/env" . "$XDG_CACHE_HOME/deno/.deno/env" . "/home/kristofers/.local/share/cargo/env" diff --git a/config/x11/opt-apps b/config/x11/opt-apps old mode 100755 new mode 100644 index 64fd4066..199e29bc --- a/config/x11/opt-apps +++ b/config/x11/opt-apps @@ -1,6 +1,6 @@ #!/bin/sh -autostart="picom nextcloud transmission-daemon zen-browser vesktop syncthing mullvad-vpn spotify-launcher kotatogram-desktop easyeffects" +autostart="picom nextcloud transmission-daemon zen-browser discord syncthing mullvad-vpn spotify-launcher kotatogram-desktop easyeffects" for program in $autostart; do pidof -sx "$program" || "$program" & diff --git a/config/yazi/keymap.toml b/config/yazi/keymap.toml index 04dfb064..4d63736f 100644 --- a/config/yazi/keymap.toml +++ b/config/yazi/keymap.toml @@ -41,8 +41,8 @@ keymap = [ {on = [ "" ], run = "seek -5", desc = "Seek up 5 units in the preview"}, {on = [ "" ], run = "seek 5", desc = "Seek down 5 units in the preview"}, - {on = [ "g", "g" ], run = "arrow -99999999", desc = "Move cursor to the top"}, - {on = [ "G" ], run = "arrow 99999999", desc = "Move cursor to the bottom"}, + {on = [ "g", "g" ], run = "arrow top", desc = "Move cursor to the top"}, + {on = [ "G" ], run = "arrow bot", desc = "Move cursor to the bottom"}, # Selection {on = [ "" ], run = [ "toggle --state=none", "arrow 1" ], desc = "Toggle the current selection state"}, diff --git a/config/yazi/package.toml b/config/yazi/package.toml index 117390ef..ab2d724c 100644 --- a/config/yazi/package.toml +++ b/config/yazi/package.toml @@ -1,17 +1,17 @@ [[plugin.deps]] use = "AnirudhG07/nbpreview" -rev = "1d85745" +rev = "f8879b3" hash = "d378328e5d0a1b9fb9f04ab3aade4575" [[plugin.deps]] use = "Reledia/glow" -rev = "5ce76dc" -hash = "52e5f5c602962e7cbf874da28f52ba45" +rev = "c76bf4f" +hash = "a6b78bf9af5390e3a85a6951fbb7b93" [[plugin.deps]] use = "Reledia/hexyl" -rev = "39d3d4e" -hash = "dd624cbaff94af65f39fd86bc57b340" +rev = "228a9ef" +hash = "cdc65cfe4e60e1bf5afe5769d074fa9c" [[plugin.deps]] use = "Reledia/miller" @@ -25,13 +25,13 @@ hash = "a8e15d3c21c02a5af41d46ed04778a02" [[plugin.deps]] use = "dedukun/relative-motions" -rev = "df97039" -hash = "395940d2b22941e0acb1232579c9d4cf" +rev = "a1466a9" +hash = "26d7fd10e163e0624d733c067eba4b61" [[plugin.deps]] use = "hankertrix/augment-command" -rev = "e337feb" -hash = "feeb35edcf1677c7bafac2bc573670bb" +rev = "af31941" +hash = "71c1ef899b40a54fcdf5a41f87daa967" [[plugin.deps]] use = "imsi32/yatline" @@ -40,13 +40,13 @@ hash = "3e51d1fd8a2e481fcfa8eab1251d1c5f" [[plugin.deps]] use = "kirasok/torrent-preview" -rev = "c9e67df" -hash = "f0d9a684da8e4ab9ccbcd255a97cf42b" +rev = "4ca5996" +hash = "6af40ce6b2cd849b5fa32de04a598b06" [[plugin.deps]] use = "ndtoan96/ouch" -rev = "083d564" -hash = "1e4c0ac1fca31a23412324710193358a" +rev = "ce6fb75" +hash = "ed6c185514109d7c5463f609282b220c" [[plugin.deps]] use = "pirafrank/what-size" @@ -55,28 +55,28 @@ hash = "98e5f5af3efd3ba8bc2db0720187cc83" [[plugin.deps]] use = "yazi-rs/plugins:chmod" -rev = "f202fa8" -hash = "4c7e8fd0266eedee7b619d966bd2d025" +rev = "5186af7" +hash = "f28138c2e11e87962b66d583fef724c3" [[plugin.deps]] use = "yazi-rs/plugins:full-border" -rev = "f202fa8" -hash = "882ed23839778f82dc137248979c8681" +rev = "5186af7" +hash = "ae9e1d0c6bfd68cdebc98cc684c22b45" [[plugin.deps]] use = "yazi-rs/plugins:git" -rev = "f202fa8" -hash = "4d6a07559118975e2dee983d27474d70" +rev = "5186af7" +hash = "771f18427fb75fb19990ce602bb322f4" [[plugin.deps]] use = "yazi-rs/plugins:hide-preview" -rev = "f202fa8" -hash = "5be5885898ca9df783bdec0d402bf4b0" +rev = "5186af7" +hash = "1e31898370b752e4faf335b762b3eeaf" [[plugin.deps]] use = "yazi-rs/plugins:max-preview" -rev = "f202fa8" -hash = "9bc26d10d2f6e2aa93b10905b1b76979" +rev = "5186af7" +hash = "a8025f2bb311e869069364fba01abffc" [flavor] deps = [] diff --git a/config/yazi/plugins/augment-command.yazi/README.md b/config/yazi/plugins/augment-command.yazi/README.md index b4170409..a442af87 100644 --- a/config/yazi/plugins/augment-command.yazi/README.md +++ b/config/yazi/plugins/augment-command.yazi/README.md @@ -2,7 +2,7 @@ A [Yazi][yazi-link] plugin that enhances Yazi's default commands. This plugin is inspired by the -[Yazi tips page](https://yazi-rs.github.io/docs/tips), +[Yazi tips page][yazi-tips-page], the [bypass.yazi](https://github.com/Rolv-Apneseth/bypass.yazi) plugin and the [fast-enter.yazi](https://github.com/ourongxing/fast-enter.yazi) plugin. @@ -20,7 +20,7 @@ plugin. ## Requirements -- [Yazi](https://github.com/sxyazi/yazi) v0.4.2+ +- [Yazi][yazi-link] v25.2.7+ - [`7z` or `7zz` command][7z-link] - [`file` command][file-command-link] @@ -51,6 +51,7 @@ ya pack -u | `smart_paste` | `true` or `false` | `false` | Paste items into a directory without entering it. The behaviour is exactly the same as the [smart paste tip on Yazi's documentation][smart-paste-tip]. Setting this option to `false` will use the default `paste` behaviour. You can also enable this behaviour by passing the `--smart` flag to the `paste` command. | | `smart_tab_create` | `true` or `false` | `false` | Create tabs in the directory that is being hovered instead of the current directory. The behaviour is exactly the same as the [smart tab tip on Yazi's documentation][smart-tab-tip]. Setting this option to `false` will use the default `tab_create` behaviour, which means you need to pass the `--current` flag to the command. You can also enable this behaviour by passing the `--smart` flag to the `tab_create` command. | | `smart_tab_switch` | `true` or `false` | `false` | If the tab that is being switched to does not exist yet, setting this option to `true` will create all the tabs in between the current number of open tabs, and the tab that is being switched to. The behaviour is exactly the same as the [smart switch tip on Yazi's documentation][smart-switch-tip]. Setting this option to `false` will use the default `tab_switch` behaviour. You can also enable this behaviour by passing the `--smart` flag to the `tab_switch` command. | +| `confirm_on_quit` | `true` or `false` | `true` | Setting this option to `true` will cause Yazi to prompt you for a confirmation before quitting when there is more than 1 tab open. Setting this option to `false` will use the default `quit` behaviour, which is to immediately quit Yazi. You can also enable this behaviour by passing the `--confirm` flag to the `quit` command. | | `open_file_after_creation` | `true` or `false` | `false` | This option determines whether the plugin will open a file after it has been created. Setting this option to `true` will cause the plugin to open the created file. You can also enable this behaviour by passing the `--open` flag to the `create` command. | | `enter_directory_after_creation` | `true` or `false` | `false` | This option determines whether the plugin will enter a directory after it has been created. Setting this option to `true` will cause the plugin to enter the created directory. You can also enable this behaviour by passing the `--enter` flag to the `create` command. | | `use_default_create_behaviour` | `true` or `false` | `false` | This option determines whether the plugin will use the behaviour of Yazi's `create` command. Setting this option to `true` will use the behaviour of Yazi's `create` command. You can also enable this behaviour by passing the `--default-behaviour` flag to the `create` command. | @@ -65,13 +66,12 @@ ya pack -u If you would like to use the default configuration, which is shown below, you don't need to add anything to your `~/.config/yazi/init.lua` -file on Linux and macOS, or your -`C:\Users\USERNAME\AppData\Roaming\yazi\config\init.lua` -file on Windows, where `USERNAME` is your Windows username. +file on Linux and macOS, or your `%AppData%\yazi\config\init.lua` +file on Windows. ```lua -- ~/.config/yazi/init.lua for Linux and macOS --- C:\Users\USERNAME\AppData\Roaming\yazi\config\init.lua for Windows +-- %AppData%\yazi\config\init.lua for Windows -- Using the default configuration require("augment-command"):setup({ @@ -81,6 +81,7 @@ require("augment-command"):setup({ smart_paste = false, smart_tab_create = false, smart_tab_switch = false, + confirm_on_quit = true, open_file_after_creation = false, enter_directory_after_creation = false, use_default_create_behaviour = false, @@ -98,15 +99,14 @@ require("augment-command"):setup({ However, if you would like to configure the plugin, you can add your desired configuration options to your `~/.config/yazi/init.lua` file on Linux and macOS, or your -`C:\Users\USERNAME\AppData\Roaming\yazi\config\init.lua` -file on Windows, where `USERNAME` is your Windows username. +`%AppData%\yazi\config\init.lua` file on Windows. You can leave out configuration options that you would like to be left as default. An example configuration is shown below: ```lua -- ~/.config/yazi/init.lua for Linux and macOS --- C:\Users\USERNAME\AppData\Roaming\yazi\config\init.lua for Windows +-- %AppData%\yazi\config\init.lua for Windows -- Custom configuration require("augment-command"):setup({ @@ -238,8 +238,7 @@ then it will operate on the selected items. [`extract` openers section][yazi-yazi-toml-extract-openers] in [Yazi's default `yazi.toml`][yazi-yazi-toml] into your `yazi.toml`, which is located at `~/.config/yazi/yazi.toml` for Linux and macOS, and - `C:\Users\USERNAME\AppData\Roaming\yazi\config\yazi.toml` - file on Windows, where `USERNAME` is your Windows username. + `%AppData%\yazi\config\yazi.toml` file on Windows. Make sure that the `extract` openers are under the `opener` key in your `yazi.toml`. Then replace `extract` with `augmented-extract`, and you will be using the plugin's `extract` command instead of @@ -249,7 +248,7 @@ then it will operate on the selected items. ```toml # ~/.config/yazi/yazi.toml for Linux and macOS - # C:\Users\USERNAME\AppData\Roaming\yazi\config\yazi.toml for Windows + # %AppData%\yazi\config\yazi.toml for Windows [opener] extract = [ @@ -262,7 +261,7 @@ then it will operate on the selected items. ```toml # ~/.config/yazi/yazi.toml for Linux and macOS - # C:\Users\USERNAME\AppData\Roaming\yazi\config\yazi.toml for Windows + # %AppData%\yazi\config\yazi.toml for Windows [[opener.extract]] run = 'ya pub augmented-extract --list "$@"' @@ -452,16 +451,7 @@ then it will operate on the selected items. create directories to ensure that the path given exists. It also supports all the options supported by Yazi's `create` command, so you can pass them to the command and expect the same behaviour. - However, due to the - [`confirm` component](https://github.com/sxyazi/yazi/issues/2082) - currently not being exposed to plugin developers, it uses Yazi's input - component to prompt for a confirmation, like in Yazi v0.3.0 and below. - This is not ideal, but it shouldn't happen that often and - hopefully wouldn't be too annoying. - If you are using the latest version of Yazi from the main branch, - the `confirm` component is now exposed to plugin developers and - the plugin will use the `confirm` component instead. - However, the separator in the `confirm` component will be the text colour + Do note that the separator in the `confirm` component will be the text colour instead of your configured border colour for the `confirm` component as the `list` part of the `confirm` component has not been exposed to plugin developers, so the separator is made using text. @@ -536,43 +526,15 @@ then it will operate on the selected items. - To use this command, the syntax is exactly the same as the default `shell` command provided by Yazi. You just provide the command you want and provide any Yazi shell variable, which is documented - [here](https://yazi-rs.github.io/docs/configuration/keymap/#manager.shell). + [here][yazi-shell-variables]. The plugin will automatically replace the shell variable you give with the file paths for the item group before executing the command. -- You will also need to escape the quotes when giving the shell command - if you use the same quotes to quote the given arguments to the plugin. - For example, if you pass the arguments to the plugin with double quotes, - i.e. `--args="shell"`, you will have to escape the double quotes with a - backslash character, like shown below: - ```toml - # ~/.config/yazi/keymap.toml on Linux and macOS - # C:\Users\USERNAME\AppData\Roaming\yazi\config\keymap.toml on Windows - - [[manager.prepend_keymap]] - on = [ "o" ] - run = 'plugin augment-command --args="shell \"$EDITOR $@\" --block"' - desc = "Open the editor" - ``` - -- Alternatively, you can use the triple single quote `'''` delimiter - for the run string and avoid the escaping the shell command altogether, - like the two examples below: - - ```toml - # ~/.config/yazi/keymap.toml on Linux and macOS - # C:\Users\USERNAME\AppData\Roaming\yazi\config\keymap.toml on Windows - - [[manager.prepend_keymap]] - on = [ "o" ] - run = '''plugin augment-command --args='shell "$EDITOR $@" --block'''' - desc = "Open the editor" - - [[manager.prepend_keymap]] - on = [ "i" ] - run = '''plugin augment-command --args="shell '$PAGER $@' --block"''' - desc = "Open the pager" - ``` +- There is no need to quote the shell variable on Linux and macOS, + as it is expanded by the plugin instead of the shell, + and the paths are already quoted using the `ya.quote` function + before execution, so quoting is entirely unnecessary + and may result in unexpected behaviour. - `--exit-if-dir` flag to stop the shell command given from executing if the item group consists only of directories. @@ -586,11 +548,11 @@ then it will operate on the selected items. ```toml # ~/.config/yazi/keymap.toml on Linux and macOS - # C:\Users\USERNAME\AppData\Roaming\yazi\config\keymap.toml on Windows + # %AppData%\yazi\config\keymap.toml on Windows [[manager.prepend_keymap]] - on = [ "i" ] - run = '''plugin augment-command --args="shell '$PAGER $@' --block --exit-if-dir"''' + on = "i" + run = "plugin augment-command -- shell '$PAGER $@' --block --exit-if-dir" desc = "Open the pager" ``` @@ -601,18 +563,118 @@ then it will operate on the selected items. ```toml # ~/.config/yazi/keymap.toml on Linux and macOS - # C:\Users\USERNAME\AppData\Roaming\yazi\config\keymap.toml on Windows + # %AppData%\yazi\config\keymap.toml on Windows [[manager.prepend_keymap]] - on = [ "i" ] - run = '''plugin augment-command --args="shell '$EDITOR $@' --block --exit-if-dir"''' - desc = "Open the pager" + on = "o" + run = "plugin augment-command -- shell '$EDITOR $@' --block --exit-if-dir" + desc = "Open the editor" ``` Video: [shell-exit-if-directory-video] +#### Passing arguments to the `shell` command + +Ideally, you will want to avoid using backslashes to escape the shell command +arguments, so here are a few ways to do it: + +1. Shell arguments that don't have special shell variables + on Linux and macOS, like `$SHELL`, or don't have special shell characters + like `>`, `|` or spaces, need not be quoted with double quotes `"` + or single quotes `'` respectively. + For example: + + ```toml + # ~/.config/yazi/keymap.toml on Linux and macOS + # %AppData%\yazi\config\keymap.toml on Windows + [[manager.prepend_keymap]] + on = "i" + run = "plugin augment-command -- shell --block 'bat -p --pager less $@'" + desc = "Open with bat" + ``` + + Even though the `$@` argument above is considered a shell variable in Linux + and macOS, the plugin automatically replaces it with the full path + of the items in the item group, so it does not need to be quoted with + double quotes `"`, as it is expanded by the plugin, + and not meant to be expanded by the shell. + +2. If the arguments to the `shell` command have special shell variables + on Linux and macOS, like `$SHELL`, or special shell characters like + `>`, `|`, or spaces, use `--` to denote the end of the flags and options + passed to the `shell` command. + For example: + + ```toml + # ~/.config/yazi/keymap.toml on Linux and macOS + # %AppData%\yazi\config\keymap.toml on Windows + [[manager.prepend_keymap]] + on = "" + run = 'plugin augment-command -- shell --block -- sh -c "$SHELL"' + desc = "Open a shell inside of a shell here" + ``` + + ```toml + # ~/.config/yazi/keymap.toml on Linux and macOS + # %AppData%\yazi\config\keymap.toml on Windows + [[manager.prepend_keymap]] + on = "" + run = "plugin augment-command -- shell --block -- sh -c 'echo hello'" + desc = "Open a shell and say hello inside the opened shell" + ``` + +3. If the arguments passed to the `shell` command themselves contain arguments + that have special shell variables on Linux and macOS, like `$SHELL`, + or special shell characters like `>`, `|`, or spaces, + use the triple single quote `'''` delimiter for the `run` string. + + ```toml + # ~/.config/yazi/keymap.toml on Linux and macOS + # %AppData%\yazi\config\keymap.toml on Windows + [[manager.prepend_keymap]] + on = "" + run = '''plugin augment-command -- shell --block -- sh -c 'sh -c "$SHELL"'''' + desc = "Open a shell inside of a shell inside of a shell here" + ``` + + ```toml + # ~/.config/yazi/keymap.toml on Linux and macOS + # %AppData%\yazi\config\keymap.toml on Windows + [[manager.prepend_keymap]] + on = "" + run = '''plugin augment-command -- + shell --block -- sh -c "$SHELL -c 'echo hello'" + ''' + desc = "Open a shell inside of a shell and say hello inside the opened shell" + ``` + + A more legitimate use case for this would be something like + [Yazi's tip to email files using Mozilla Thunderbird][thunderbird-tip]: + + ```toml + # ~/.config/yazi/keymap.toml on Linux and macOS + # %AppData%\yazi\config\keymap.toml on Windows + [[manager.prepend_keymap]] + on = "" + run = '''plugin augment-command -- + shell -- + paths=$(for p in $@; do echo "$p"; done | paste -s -d,) + thunderbird -compose "attachment='$paths'" + ''' + desc = "Email files using Mozilla Thunderbird" + ``` + + Once again, the `$@` variable above does not need to be quoted in double + quotes `"` as it is expanded by the plugin instead of the shell. + +If the above few methods to avoid using backslashes within your shell command +to escape the quotes are still insufficient for your use case, +it is probably more appropriate to write a shell script in a separate file +and execute that instead of writing the shell command inline +in your `keymap.toml` file. + ### Paste (`paste`) - When `smart_paste` is set to `true`, @@ -699,6 +761,35 @@ then it will operate on the selected items. [smart-tab-switch-video] +### Quit (`quit`) + +- You should use Yazi's default `quit` command instead of this augmented + command if you don't want to have a prompt when quitting Yazi + with multiple tabs open. + This command has a visual side effect of showing a confirmation prompt + for a split second before closing Yazi when quitting Yazi + with only 1 tab open, which can be annoying. + This confirmation prompt is due to the plugin still running for a bit + after the `quit` command is emitted, causing Yazi to prompt you for + confirmation as there are tasks still running. + However, once the plugin has stopped running, which is a split second + after the `quit` command is emitted, Yazi will exit. + You can observe this visual effect in the video demonstration below. +- When `confirm_on_quit` is set to `true`, the plugin will prompt you for + confirmation when there is more than 1 tab open. Otherwise, it will + immediately quit Yazi, just like the default `quit` command. +- `--confirm` flag to get the plugin to prompt you for confirmation when + quitting with multiple tabs open. + This flag will cause the `quit` command to prompt you for confirmation + when quitting with multiple tabs open even when `confirm_on_quit` is + set to `false`. + This allows you to set a specific key to use this behaviour with the + `quit` command instead of using it for every `quit` command. + + Video: + + [quit-with-confirmation-video] + ### Arrow (`arrow`) - When `wraparound_file_navigation` is set to `true`, @@ -738,19 +829,19 @@ then it will operate on the selected items. ```toml # ~/.config/yazi/keymap.toml on Linux and macOS - # C:\Users\USERNAME\AppData\Roaming\yazi\config\keymap.toml on Windows + # %AppData%\yazi\config\keymap.toml on Windows # Use K to move up in the parent directory [[manager.prepend_keymap]] - on = [ "K" ] - run = [ "leave", "arrow -1", "enter" ] + on = "K" + run = ["leave", "arrow -1", "enter"] desc = "Move up in the parent directory" # Use J to move down in the parent directory [[manager.prepend_keymap]] - on = [ "J" ] - run = [ "leave", "arrow 1", "enter" ] + on = "J" + run = ["leave", "arrow 1", "enter"] desc = "Move down in the parent directory" ``` @@ -815,16 +906,16 @@ then it will operate on the selected items. Add the commands that you would like to use to your `keymap.toml` file, located at `~/.config/yazi/keymap.toml` on Linux and macOS -and at `C:\Users\USERNAME\AppData\Roaming\yazi\config\keymap.toml` +and at `%AppData%\yazi\config\keymap.toml` on Windows, in this format: ```toml # ~/.config/yazi/keymap.toml on Linux and macOS -# C:\Users\USERNAME\AppData\Roaming\yazi\config\keymap.toml on Windows +# %AppData%\yazi\config\keymap.toml on Windows [[manager.prepend_keymap]] -on = [ "key" ] -run = "plugin augment-command --args='command arguments --flags --options=42'" +on = "key" +run = "plugin augment-command -- command arguments --flags --options=42" desc = "Description" ``` @@ -832,11 +923,11 @@ For example, to use the augmented `enter` command: ```toml # ~/.config/yazi/keymap.toml on Linux and macOS -# C:\Users\USERNAME\AppData\Roaming\yazi\config\keymap.toml on Windows +# %AppData%\yazi\config\keymap.toml on Windows [[manager.prepend_keymap]] -on = [ "l" ] -run = "plugin augment-command --args='enter'" +on = "l" +run = "plugin augment-command -- enter" desc = "Enter a directory and skip directories with only a single subdirectory" ``` @@ -845,33 +936,35 @@ are also supported, for example: ```toml # ~/.config/yazi/keymap.toml on Linux and macOS -# C:\Users\USERNAME\AppData\Roaming\yazi\config\keymap.toml on Windows +# %AppData%\yazi\config\keymap.toml on Windows [[manager.prepend_keymap]] -on = [ "k" ] -run = "plugin augment-command --args='arrow -1'" +on = "k" +run = "plugin augment-command -- arrow -1" desc = "Move cursor up" [[manager.prepend_keymap]] -on = [ "r" ] -run = "plugin augment-command --args='rename --cursor=before_ext'" +on = "r" +run = "plugin augment-command -- rename --cursor=before_ext" desc = "Rename a file or directory" [[manager.prepend_keymap]] -on = [ "D" ] -run = "plugin augment-command --args='remove --permanently'" +on = "D" +run = "plugin augment-command -- remove --permanently" desc = "Permanently delete the files" + +[[manager.prepend_keymap]] +on = ["g", "j"] +run = "plugin augment-command -- parent_arrow 1" ``` For the default descriptions of the commands, you can refer to [Yazi's default `keymap.toml` file][yazi-keymap-toml]. Essentially, all you need to do to use this plugin -is to wrap a Yazi command in single quotes, -like `'enter'`, -then add `plugin augment-command --args=` -in front of it, which results in -`plugin augment-command --args='enter'`. +is to add `plugin augment-command --`, with a space at the end, +in front of a Yazi command, such as `enter`, +which results in `plugin augment-command -- enter'`. ### Using the `extract` command as an opener @@ -883,7 +976,7 @@ for details on how to do so. ### Full configuration example For a full configuration example, -you can take a look at [my `keymap.toml` file][my-keymap-toml] +you can have a look at [my `keymap.toml` file][my-keymap-toml] and [my `yazi.toml` file][my-yazi-toml]. ## [Licence] @@ -894,6 +987,7 @@ You can view the full licence in the [`LICENSE`][Licence] file. [yazi-link]: https://github.com/sxyazi/yazi +[yazi-tips-page]: https://yazi-rs.github.io/docs/tips [smart-paste-tip]: https://yazi-rs.github.io/docs/tips#smart-paste [smart-tab-tip]: https://yazi-rs.github.io/docs/tips#smart-tab [smart-switch-tip]: https://yazi-rs.github.io/docs/tips#smart-switch @@ -905,6 +999,8 @@ You can view the full licence in the [`LICENSE`][Licence] file. [brew-link]: https://brew.sh/ [yazi-yazi-toml-extract-openers]: https://github.com/sxyazi/yazi/blob/main/yazi-config/preset/yazi-default.toml#L51-L54 [yazi-yazi-toml]: https://github.com/sxyazi/yazi/blob/main/yazi-config/preset/yazi-default.toml +[yazi-shell-variables]: https://yazi-rs.github.io/docs/configuration/keymap/#manager.shell +[thunderbird-tip]: https://yazi-rs.github.io/docs/tips#email-selected-files-using-thunderbird [yazi-keymap-toml]: https://github.com/sxyazi/yazi/blob/main/yazi-config/preset/keymap-default.toml [my-keymap-toml]: https://github.com/hankertrix/Dotfiles/blob/main/.config/yazi/keymap.toml [my-yazi-toml]: https://github.com/hankertrix/Dotfiles/blob/main/.config/yazi/yazi.toml @@ -979,6 +1075,10 @@ You can view the full licence in the [`LICENSE`][Licence] file. [smart-tab-switch-video]: https://github.com/user-attachments/assets/1afb540d-47a9-4625-ae59-95d5cd91aa35 + + +[quit-with-confirmation-video]: https://github.com/user-attachments/assets/b6206ee4-766b-44ce-b90b-15b015ae71f9 + [wraparound-arrow-video]: https://github.com/user-attachments/assets/41ea1fb0-a526-4549-95a2-547c3c4b0498 diff --git a/config/yazi/plugins/augment-command.yazi/main.lua b/config/yazi/plugins/augment-command.yazi/main.lua index 517c7245..5d7e6393 100644 --- a/config/yazi/plugins/augment-command.yazi/main.lua +++ b/config/yazi/plugins/augment-command.yazi/main.lua @@ -1,3 +1,5 @@ +--- @since 25.2.7 + -- Plugin to make some Yazi commands smarter -- Written in Lua 5.4 @@ -9,29 +11,29 @@ -- The type for the function to handle a command -- -- Description of the function parameters: --- args: The arguments to pass to the command --- config: The configuration object +-- args: The arguments to pass to the command +-- config: The configuration object ---@alias CommandFunction fun( ---- args: Arguments, ---- config: Configuration): nil +--- args: Arguments, +--- config: Configuration): nil -- The type of the command table ---@alias CommandTable table -- The type for the extractor list items command ---@alias ExtractorListItemsCommand fun( ---- self: Extractor, +--- self: Extractor, ---): output: CommandOutput|nil, error: Error|nil -- The type for the extractor get items function ---@alias ExtractorGetItems fun( ---- self: Extractor, +--- self: Extractor, ---): files: string[], directories: string[], error: string|nil -- The type for the extractor extract function. ---@alias ExtractorExtract fun( ---- self: Extractor, ---- has_only_one_file: boolean|nil, +--- self: Extractor, +--- has_only_one_file: boolean|nil, ---): ExtractionResult -- The type for the extractor function @@ -48,6 +50,7 @@ ---@field smart_paste boolean Whether to use smart paste ---@field smart_tab_create boolean Whether to use smart tab create ---@field smart_tab_switch boolean Whether to use smart tab switch +---@field confirm_on_quit boolean Whether to show a confirmation when quitting ---@field open_file_after_creation boolean Whether to open after creation ---@field enter_directory_after_creation boolean Whether to enter after creation ---@field use_default_create_behaviour boolean Use Yazi's create behaviour? @@ -85,68 +88,70 @@ local PLUGIN_NAME = "augment-command" -- The enum for the supported commands ---@enum SupportedCommands local Commands = { - Open = "open", - Extract = "extract", - Enter = "enter", - Leave = "leave", - Rename = "rename", - Remove = "remove", - Create = "create", - Shell = "shell", - Paste = "paste", - TabCreate = "tab_create", - TabSwitch = "tab_switch", - Arrow = "arrow", - ParentArrow = "parent_arrow", - Editor = "editor", - Pager = "pager", + Open = "open", + Extract = "extract", + Enter = "enter", + Leave = "leave", + Rename = "rename", + Remove = "remove", + Create = "create", + Shell = "shell", + Paste = "paste", + TabCreate = "tab_create", + TabSwitch = "tab_switch", + Quit = "quit", + Arrow = "arrow", + ParentArrow = "parent_arrow", + Editor = "editor", + Pager = "pager", } -- The enum for which group of items to operate on ---@enum ItemGroup local ItemGroup = { - Hovered = "hovered", - Selected = "selected", - None = "none", - Prompt = "prompt", + Hovered = "hovered", + Selected = "selected", + None = "none", + Prompt = "prompt", } -- The default configuration for the plugin ---@type UserConfiguration local DEFAULT_CONFIG = { - prompt = false, - default_item_group_for_prompt = ItemGroup.Hovered, - smart_enter = true, - smart_paste = false, - smart_tab_create = false, - smart_tab_switch = false, - open_file_after_creation = false, - enter_directory_after_creation = false, - use_default_create_behaviour = false, - enter_archives = true, - extract_retries = 3, - recursively_extract_archives = true, - preserve_file_permissions = false, - must_have_hovered_item = true, - skip_single_subdirectory_on_enter = true, - skip_single_subdirectory_on_leave = true, - wraparound_file_navigation = false, + prompt = false, + default_item_group_for_prompt = ItemGroup.Hovered, + smart_enter = true, + smart_paste = false, + smart_tab_create = false, + smart_tab_switch = false, + confirm_on_quit = true, + open_file_after_creation = false, + enter_directory_after_creation = false, + use_default_create_behaviour = false, + enter_archives = true, + extract_retries = 3, + recursively_extract_archives = true, + preserve_file_permissions = false, + must_have_hovered_item = true, + skip_single_subdirectory_on_enter = true, + skip_single_subdirectory_on_leave = true, + wraparound_file_navigation = false, } -- The default input options for this plugin local DEFAULT_INPUT_OPTIONS = { - position = { "top-center", x = 0, y = 2, w = 50, h = 3 }, + position = { "top-center", x = 0, y = 2, w = 50, h = 3 }, } -- The default confirm options for this plugin local DEFAULT_CONFIRM_OPTIONS = { - pos = { "center", x = 0, y = 0, w = 50, h = 15 }, + pos = { "center", x = 0, y = 0, w = 50, h = 15 }, } -- The default notification options for this plugin local DEFAULT_NOTIFICATION_OPTIONS = { - title = "Augment Command Plugin", - timeout = 5, + title = "Augment Command Plugin", + timeout = 5, } -- The tab preference keys. @@ -155,60 +160,60 @@ local DEFAULT_NOTIFICATION_OPTIONS = { -- different types for the same thing. ---@type tab.Preference local TAB_PREFERENCE_KEYS = { - sort_by = "alphabetical", - sort_sensitive = false, - sort_reverse = false, - sort_dir_first = true, - sort_translit = false, - linemode = "none", - show_hidden = false, + sort_by = "alphabetical", + sort_sensitive = false, + sort_reverse = false, + sort_dir_first = true, + sort_translit = false, + linemode = "none", + show_hidden = false, } -- The table of input options for the prompt ---@type table local INPUT_OPTIONS_TABLE = { - [ItemGroup.Hovered] = "(H/s)", - [ItemGroup.Selected] = "(h/S)", - [ItemGroup.None] = "(h/s)", + [ItemGroup.Hovered] = "(H/s)", + [ItemGroup.Selected] = "(h/S)", + [ItemGroup.None] = "(h/s)", } -- The extractor names ---@enum ExtractorName local ExtractorName = { - SevenZip = "7-Zip", - Tar = "Tar", + SevenZip = "7-Zip", + Tar = "Tar", } -- The extract behaviour flags ---@enum ExtractBehaviour local ExtractBehaviour = { - Overwrite = "overwrite", - Rename = "rename", + Overwrite = "overwrite", + Rename = "rename", } -- The list of archive file extensions ---@type table local ARCHIVE_FILE_EXTENSIONS = { - ["7z"] = true, - boz = true, - bz = true, - bz2 = true, - bzip2 = true, - cb7 = true, - cbr = true, - cbt = true, - cbz = true, - gz = true, - gzip = true, - rar = true, - s7z = true, - tar = true, - tbz = true, - tbz2 = true, - tgz = true, - txz = true, - xz = true, - zip = true, + ["7z"] = true, + boz = true, + bz = true, + bz2 = true, + bzip2 = true, + cb7 = true, + cbr = true, + cbt = true, + cbz = true, + gz = true, + gzip = true, + rar = true, + s7z = true, + tar = true, + tbz = true, + tbz2 = true, + tgz = true, + txz = true, + xz = true, + zip = true, } -- The error for the base extractor class @@ -216,8 +221,8 @@ local ARCHIVE_FILE_EXTENSIONS = { -- does not implement any functionality ---@type string local BASE_EXTRACTOR_ERROR = table.concat({ - "The Extractor class is does not implement any functionality.", - "How did you even manage to get here?", + "The Extractor class is does not implement any functionality.", + "How did you even manage to get here?", }, "\n") -- Class definitions @@ -234,27 +239,27 @@ local BASE_EXTRACTOR_ERROR = table.concat({ --- The map of the extract behaviour strings to the command flags ---@field extract_behaviour_map table local Extractor = { - name = "BaseExtractor", - command = "", - commands = {}, - supports_file_permissions = false, - extract_behaviour_map = {}, + name = "BaseExtractor", + command = "", + commands = {}, + supports_file_permissions = false, + extract_behaviour_map = {}, } -- The function to create a subclass of the abstract base extractor ---@param subclass table The subclass to create ---@return Extractor subclass Subclass of the base extractor function Extractor:subclass(subclass) - -- + -- - -- Create a new instance - local instance = setmetatable(subclass or {}, self) + -- Create a new instance + local instance = setmetatable(subclass or {}, self) - -- Set where to find the object's methods or properties - self.__index = self + -- Set where to find the object's methods or properties + self.__index = self - -- Return the instance - return instance + -- Return the instance + return instance end -- The method to get the archive items @@ -264,43 +269,43 @@ function Extractor:get_items() return {}, {}, BASE_EXTRACTOR_ERROR end -- The method to extract the archive ---@type ExtractorExtract function Extractor:extract(_) - return { - successful = false, - error = BASE_EXTRACTOR_ERROR, - } + return { + successful = false, + error = BASE_EXTRACTOR_ERROR, + } end -- The 7-Zip extractor ---@class SevenZip: Extractor ---@field password string The password to the archive local SevenZip = Extractor:subclass({ - name = ExtractorName.SevenZip, - commands = { "7z", "7zz" }, + name = ExtractorName.SevenZip, + commands = { "7z", "7zz" }, - -- https://documentation.help/7-Zip/overwrite.htm - extract_behaviour_map = { - [ExtractBehaviour.Overwrite] = "-aoa", - [ExtractBehaviour.Rename] = "-aou", - }, + -- https://documentation.help/7-Zip/overwrite.htm + extract_behaviour_map = { + [ExtractBehaviour.Overwrite] = "-aoa", + [ExtractBehaviour.Rename] = "-aou", + }, - password = "", + password = "", }) -- The Tar extractor ---@class Tar: Extractor local Tar = Extractor:subclass({ - name = ExtractorName.Tar, - commands = { "gtar", "tar" }, - supports_file_permissions = true, + name = ExtractorName.Tar, + commands = { "gtar", "tar" }, + supports_file_permissions = true, - -- https://www.man7.org/linux/man-pages/man1/tar.1.html - -- https://ss64.com/mac/tar.html - extract_behaviour_map = { + -- https://www.man7.org/linux/man-pages/man1/tar.1.html + -- https://ss64.com/mac/tar.html + extract_behaviour_map = { - -- Tar overwrites by default - [ExtractBehaviour.Overwrite] = "", - [ExtractBehaviour.Rename] = "-k", - }, + -- Tar overwrites by default + [ExtractBehaviour.Overwrite] = "", + [ExtractBehaviour.Rename] = "-k", + }, }) -- The default extractor, which is set to 7-Zip @@ -310,14 +315,14 @@ local DefaultExtractor = SevenZip:subclass({}) -- The table of archive mime types ---@type table local ARCHIVE_MIME_TYPE_TO_EXTRACTOR_MAP = { - ["application/zip"] = DefaultExtractor, - ["application/gzip"] = DefaultExtractor, - ["application/tar"] = Tar, - ["application/bzip"] = DefaultExtractor, - ["application/bzip2"] = DefaultExtractor, - ["application/7z-compressed"] = DefaultExtractor, - ["application/rar"] = DefaultExtractor, - ["application/xz"] = DefaultExtractor, + ["application/zip"] = DefaultExtractor, + ["application/gzip"] = DefaultExtractor, + ["application/tar"] = Tar, + ["application/bzip"] = DefaultExtractor, + ["application/bzip2"] = DefaultExtractor, + ["application/7z-compressed"] = DefaultExtractor, + ["application/rar"] = DefaultExtractor, + ["application/xz"] = DefaultExtractor, } -- Patterns @@ -329,14 +334,14 @@ local ARCHIVE_MIME_TYPE_TO_EXTRACTOR_MAP = { -- characters need to be escaped ---@type string[] local MIME_TYPE_PREFIXES_TO_REMOVE = { - "x%-", - "vnd%.", + "x%-", + "vnd%.", } -- The pattern template to get the mime type without a prefix ---@type string local get_mime_type_without_prefix_template_pattern = - "^(%%a-)/%s([%%-%%d%%a]-)$" + "^(%%a-)/%s([%%-%%d%%a]-)$" -- The pattern to get the file extension ---@type string @@ -346,9 +351,9 @@ local file_extension_pattern = "%.([%a]+)$" ---@type string local shell_variable_pattern = "[%$%%][%*@0]" --- The pattern to match the bat command with the pager option passed +-- The pattern to match the bat command ---@type string -local bat_command_with_pager_pattern = "%f[%a]bat%f[%A].*%-%-pager%s+" +local bat_command_pattern = "%f[%a]bat%f[%A]" -- Utility functions @@ -365,46 +370,46 @@ local bat_command_with_pager_pattern = "%f[%a]bat%f[%A].*%-%-pager%s+" ---@param ... table[] The tables to merge ---@return table merged_table The merged table local function merge_tables(...) - -- + -- - -- Initialise a new table - local new_table = {} + -- Initialise a new table + local new_table = {} - -- Initialise the index variable - local index = 1 + -- Initialise the index variable + local index = 1 - -- Iterates over the tables given - for _, table in ipairs({ ... }) do - -- + -- Iterates over the tables given + for _, table in ipairs({ ... }) do + -- - -- Iterate over all of the keys and values - for key, value in pairs(table) do - -- + -- Iterate over all of the keys and values + for key, value in pairs(table) do + -- - -- If the key is a number, then add using the index - -- instead of the key. - -- This is to allow lists to be merged. - if type(key) == "number" then - -- + -- If the key is a number, then add using the index + -- instead of the key. + -- This is to allow lists to be merged. + if type(key) == "number" then + -- - -- Set the value mapped to the index - new_table[index] = value + -- Set the value mapped to the index + new_table[index] = value - -- Increment the index - index = index + 1 + -- Increment the index + index = index + 1 - -- Otherwise, the key isn't a number - else - -- + -- Otherwise, the key isn't a number + else + -- - -- Set the key in the new table to the value given - new_table[key] = value - end - end - end + -- Set the key in the new table to the value given + new_table[key] = value + end + end + end - -- Return the new table - return new_table + -- Return the new table + return new_table end -- Function to split a string into a list @@ -412,35 +417,35 @@ end ---@param separator string|nil The character to split the string by ---@return string[] splitted_strings The list of strings split by the character local function string_split(given_string, separator) - -- + -- - -- If the separator isn't given, set it to the whitespace character - separator = separator or "%s" + -- If the separator isn't given, set it to the whitespace character + separator = separator or "%s" - -- Initialise the list of splitted strings - local splitted_strings = {} + -- Initialise the list of splitted strings + local splitted_strings = {} - -- Iterate over all of the strings found by pattern - for string in string.gmatch(given_string, "([^" .. separator .. "]+)") do - -- + -- Iterate over all of the strings found by pattern + for string in string.gmatch(given_string, "([^" .. separator .. "]+)") do + -- - -- Add the string to the list of splitted strings - table.insert(splitted_strings, string) - end + -- Add the string to the list of splitted strings + table.insert(splitted_strings, string) + end - -- Return the list of splitted strings - return splitted_strings + -- Return the list of splitted strings + return splitted_strings end -- Function to trim a string ---@param string string The string to trim ---@return string trimmed_string The trimmed string local function string_trim(string) - -- + -- - -- Return the string with the whitespace characters - -- removed from the start and end - return string:match("^%s*(.-)%s*$") + -- Return the string with the whitespace characters + -- removed from the start and end + return string:match("^%s*(.-)%s*$") end -- Function to get a value from a table @@ -456,17 +461,17 @@ local function table_get(table, key, default) return table[key] or default end ---@param default any The default value to return if the key doesn't exist ---@return any value The value of the key or the default value local function table_pop(table, key, default) - -- + -- - -- Get the value of the key from the table - local value = table[key] + -- Get the value of the key from the table + local value = table[key] - -- Remove the key from the table - table[key] = nil + -- Remove the key from the table + table[key] = nil - -- Return the value if it exist, - -- otherwise return the default value - return value or default + -- Return the value if it exist, + -- otherwise return the default value + return value or default end -- Function to escape a percentage sign % @@ -474,93 +479,93 @@ end ---@param replacement_string string The string to escape ---@return string replacement_result The escaped string local function escape_replacement_string(replacement_string) - -- + -- - -- Get the result of the replacement - local replacement_result = replacement_string:gsub("%%", "%%%%") + -- Get the result of the replacement + local replacement_result = replacement_string:gsub("%%", "%%%%") - -- Return the result of the replacement - return replacement_result + -- Return the result of the replacement + return replacement_result end -- Function to parse the number arguments to the number type ---@param args Arguments The arguments to parse ---@return Arguments parsed_args The parsed arguments local function parse_number_arguments(args) - -- + -- - -- The parsed arguments - ---@type Arguments - local parsed_args = {} + -- The parsed arguments + ---@type Arguments + local parsed_args = {} - -- Iterate over the arguments given - for arg_name, arg_value in pairs(args) do - -- + -- Iterate over the arguments given + for arg_name, arg_value in pairs(args) do + -- - -- Try to convert the argument to a number - local number_arg_value = tonumber(arg_value) + -- Try to convert the argument to a number + local number_arg_value = tonumber(arg_value) - -- Set the argument to the number argument value - -- if the argument is a number, - -- otherwise just set it to the given argument value - parsed_args[arg_name] = number_arg_value or arg_value - end + -- Set the argument to the number argument value + -- if the argument is a number, + -- otherwise just set it to the given argument value + parsed_args[arg_name] = number_arg_value or arg_value + end - -- Return the parsed arguments - return parsed_args + -- Return the parsed arguments + return parsed_args end -- Function to convert a table of arguments to a string ---@param args Arguments The arguments to convert ---@return string args_string The string of the arguments local function convert_arguments_to_string(args) - -- + -- - -- The table of string arguments - ---@type string[] - local string_arguments = {} + -- The table of string arguments + ---@type string[] + local string_arguments = {} - -- Iterate all the items in the argument table - for key, value in pairs(args) do - -- + -- Iterate all the items in the argument table + for key, value in pairs(args) do + -- - -- If the key is a number - if type(key) == "number" then - -- + -- If the key is a number + if type(key) == "number" then + -- - -- Add the stringified value to the string arguments table - table.insert(string_arguments, tostring(value)) + -- Add the stringified value to the string arguments table + table.insert(string_arguments, tostring(value)) - -- Otherwise, if the key is a string - elseif type(key) == "string" then - -- + -- Otherwise, if the key is a string + elseif type(key) == "string" then + -- - -- Replace the underscores and spaces in the key with dashes - local key_with_dashes = key:gsub("_", "-"):gsub("%s", "-") + -- Replace the underscores and spaces in the key with dashes + local key_with_dashes = key:gsub("_", "-"):gsub("%s", "-") - -- If the value is a boolean and the boolean is true, - -- add the value to the string - if type(value) == "boolean" and value then - table.insert( - string_arguments, - string.format("--%s", key_with_dashes) - ) + -- If the value is a boolean and the boolean is true, + -- add the value to the string + if type(value) == "boolean" and value then + table.insert( + string_arguments, + string.format("--%s", key_with_dashes) + ) - -- Otherwise, just add the key and the value to the string - else - table.insert( - string_arguments, - string.format("--%s=%s", key_with_dashes, value) - ) - end - end - end + -- Otherwise, just add the key and the value to the string + else + table.insert( + string_arguments, + string.format("--%s=%s", key_with_dashes, value) + ) + end + end + end - -- Combine the string arguments into a single string - local string_args = table.concat(string_arguments, " ") + -- Combine the string arguments into a single string + local string_args = table.concat(string_arguments, " ") - -- Return the string arguments - return string_args + -- Return the string arguments + return string_args end -- Function to show a warning @@ -568,10 +573,10 @@ end ---@param options YaziNotificationOptions|nil Options for the notification ---@return nil local function show_warning(warning_message, options) - return ya.notify(merge_tables(DEFAULT_NOTIFICATION_OPTIONS, options or {}, { - content = warning_message, - level = "warn", - })) + return ya.notify(merge_tables(DEFAULT_NOTIFICATION_OPTIONS, options or {}, { + content = warning_message, + level = "warn", + })) end -- Function to show an error @@ -579,10 +584,10 @@ end ---@param options YaziNotificationOptions|nil Options for the notification ---@return nil local function show_error(error_message, options) - return ya.notify(merge_tables(DEFAULT_NOTIFICATION_OPTIONS, options or {}, { - content = error_message, - level = "error", - })) + return ya.notify(merge_tables(DEFAULT_NOTIFICATION_OPTIONS, options or {}, { + content = error_message, + level = "error", + })) end -- Function to get the user's input @@ -591,127 +596,101 @@ end ---@return string|nil user_input The user's input ---@return InputEvent event The event for the input function local function get_user_input(prompt, options) - return ya.input(merge_tables(DEFAULT_INPUT_OPTIONS, options or {}, { - title = prompt, - })) + return ya.input(merge_tables(DEFAULT_INPUT_OPTIONS, options or {}, { + title = prompt, + })) end -- Function to get the user's confirmation --- TODO: Remove the `ya.input` version once `ya.confirm` is stable ----@param prompt string The prompt to show to the user ---@param title string|ui.Line The title of the confirmation prompt ---@param content string|ui.Text The content of the confirmation prompt ---@return boolean confirmation Whether the user has confirmed or not -local function get_user_confirmation(prompt, title, content) - -- +local function get_user_confirmation(title, content) + -- - -- If the ya.confirm API exists, use it - if ya.confirm then - -- + -- Get the user's confirmation + local confirmation = ya.confirm(merge_tables(DEFAULT_CONFIRM_OPTIONS, { + title = title, + content = content, + })) - -- Get the user's confirmation - local confirmation = ya.confirm(merge_tables(DEFAULT_CONFIRM_OPTIONS, { - title = title, - content = content, - })) - - -- Return the result of the confirmation - return confirmation - end - - -- TODO: Remove everything after this when `ya.confirm` is stable - - -- Get the user's input - local user_input, event = get_user_input(prompt) - - -- If the user has not confirmed the input, - -- or the user input is nil, - -- then return false - if not user_input or event ~= 1 then return false end - - -- Lowercase the user's input - user_input = user_input:lower() - - -- If the user input starts with a "y", then return true - if user_input:find("^y") then return true end - - -- Otherwise, return false - return false + -- Return the result of the confirmation + return confirmation end -- Function to merge the given configuration table with the default one ---@param config UserConfiguration|nil The configuration table to merge ---@return UserConfiguration merged_config The merged configuration table local function merge_configuration(config) - -- + -- - -- If the configuration isn't given, then use the default one - if config == nil then return DEFAULT_CONFIG end + -- If the configuration isn't given, then use the default one + if config == nil then return DEFAULT_CONFIG end - -- Initialise the list of invalid configuration options - local invalid_configuration_options = {} + -- Initialise the list of invalid configuration options + local invalid_configuration_options = {} - -- Initialise the merged configuration - local merged_config = {} + -- Initialise the merged configuration + local merged_config = {} - -- Iterate over the default configuration table - for key, value in pairs(DEFAULT_CONFIG) do - -- + -- Iterate over the default configuration table + for key, value in pairs(DEFAULT_CONFIG) do + -- - -- Add the default configuration to the merged configuration - merged_config[key] = value - end + -- Add the default configuration to the merged configuration + merged_config[key] = value + end - -- Iterate over the given configuration table - for key, value in pairs(config) do - -- + -- Iterate over the given configuration table + for key, value in pairs(config) do + -- - -- If the key is not in the merged configuration - if merged_config[key] == nil then - -- + -- If the key is not in the merged configuration + if merged_config[key] == nil then + -- - -- Add the key to the list of invalid configuration options - table.insert(invalid_configuration_options, key) + -- Add the key to the list of invalid configuration options + table.insert(invalid_configuration_options, key) - -- Continue the loop - goto continue - end + -- Continue the loop + goto continue + end - -- Otherwise, overwrite the value in the merged configuration - merged_config[key] = value + -- Otherwise, overwrite the value in the merged configuration + merged_config[key] = value - -- The label to continue the loop - ::continue:: - end + -- The label to continue the loop + ::continue:: + end - -- If there are no invalid configuration options, - -- then return the merged configuration - if #invalid_configuration_options <= 0 then return merged_config end + -- If there are no invalid configuration options, + -- then return the merged configuration + if #invalid_configuration_options <= 0 then return merged_config end - -- Otherwise, warn the user of the invalid configuration options - show_warning( - "Invalid configuration options: " - .. table.concat(invalid_configuration_options, ", ") - ) + -- Otherwise, warn the user of the invalid configuration options + show_warning( + "Invalid configuration options: " + .. table.concat(invalid_configuration_options, ", ") + ) - -- Return the merged configuration - return merged_config + -- Return the merged configuration + return merged_config end -- Function to initialise the configuration ---@type fun( ---- user_config: Configuration|nil, -- The configuration object +--- user_config: Configuration|nil, -- The configuration object ---): Configuration The initialised configuration object local initialise_config = ya.sync(function(state, user_config) - -- + -- - -- Merge the default configuration with the user given one, - -- as well as the additional data given, - -- and set it to the state. - state.config = merge_configuration(user_config) + -- Merge the default configuration with the user given one, + -- as well as the additional data given, + -- and set it to the state. + state.config = merge_configuration(user_config) - -- Return the configuration object for async functions - return state.config + -- Return the configuration object for async functions + return state.config end) -- Function to try if a shell command exists @@ -719,17 +698,17 @@ end) ---@param args string[]|nil The arguments to the shell command ---@return boolean shell_command_exists Whether the shell command exists local function async_shell_command_exists(shell_command, args) - -- + -- - -- Get the output of the shell command with the given arguments - local output = Command(shell_command) - :args(args or {}) - :stdout(Command.PIPED) - :stderr(Command.PIPED) - :output() + -- Get the output of the shell command with the given arguments + local output = Command(shell_command) + :args(args or {}) + :stdout(Command.PIPED) + :stderr(Command.PIPED) + :output() - -- Return true if there's an output and false otherwise - return output ~= nil + -- Return true if there's an output and false otherwise + return output ~= nil end -- Function to emit a plugin command @@ -737,53 +716,49 @@ end ---@param args Arguments The arguments to pass to the plugin command ---@return nil local function emit_plugin_command(command, args) - return ya.manager_emit("plugin", { - PLUGIN_NAME, - args = string.format( - "%s %s", - command, - convert_arguments_to_string(args) - ), - }) + return ya.manager_emit("plugin", { + PLUGIN_NAME, + string.format("%s %s", command, convert_arguments_to_string(args)), + }) end -- Function to subscribe to the augmented-extract event ---@type fun(): nil local subscribe_to_augmented_extract_event = ya.sync(function(_) - return ps.sub_remote("augmented-extract", function(args) - -- + return ps.sub_remote("augmented-extract", function(args) + -- - -- If the arguments given isn't a table, - -- exit the function - if type(args) ~= "table" then return end + -- If the arguments given isn't a table, + -- exit the function + if type(args) ~= "table" then return end - -- Iterate over the arguments - for _, arg in ipairs(args) do - -- + -- Iterate over the arguments + for _, arg in ipairs(args) do + -- - -- Emit the command to call the plugin's extract function - -- with the given arguments and flags - emit_plugin_command("extract", { - archive_path = ya.quote(arg), - }) - end - end) + -- Emit the command to call the plugin's extract function + -- with the given arguments and flags + emit_plugin_command("extract", { + archive_path = ya.quote(arg), + }) + end + end) end) -- Function to initialise the plugin ---@param opts Configuration|nil The options given to the plugin ---@return Configuration config The initialised configuration object local function initialise_plugin(opts) - -- + -- - -- Subscribe to the augmented extract event - subscribe_to_augmented_extract_event() + -- Subscribe to the augmented extract event + subscribe_to_augmented_extract_event() - -- Initialise the configuration object - local config = initialise_config(opts) + -- Initialise the configuration object + local config = initialise_config(opts) - -- Return the configuration object - return config + -- Return the configuration object + return config end -- Function to standardise the mime type of a file. @@ -792,50 +767,50 @@ end ---@param mime_type string The mime type of the file ---@return string standardised_mime_type The standardised mime type of the file local function standardise_mime_type(mime_type) - -- + -- - -- Trim the whitespace from the mime type - local trimmed_mime_type = string_trim(mime_type) + -- Trim the whitespace from the mime type + local trimmed_mime_type = string_trim(mime_type) - -- Iterate over the mime type prefixes to remove - for _, prefix in ipairs(MIME_TYPE_PREFIXES_TO_REMOVE) do - -- + -- Iterate over the mime type prefixes to remove + for _, prefix in ipairs(MIME_TYPE_PREFIXES_TO_REMOVE) do + -- - -- Get the pattern to remove the mime type prefix - local pattern = - get_mime_type_without_prefix_template_pattern:format(prefix) + -- Get the pattern to remove the mime type prefix + local pattern = + get_mime_type_without_prefix_template_pattern:format(prefix) - -- Remove the prefix from the mime type - local mime_type_without_prefix, replacement_count = - trimmed_mime_type:gsub(pattern, "%1/%2") + -- Remove the prefix from the mime type + local mime_type_without_prefix, replacement_count = + trimmed_mime_type:gsub(pattern, "%1/%2") - -- If the replacement count is greater than zero, - -- return the mime type without the prefix - if replacement_count > 0 then return mime_type_without_prefix end - end + -- If the replacement count is greater than zero, + -- return the mime type without the prefix + if replacement_count > 0 then return mime_type_without_prefix end + end - -- Return the mime type with whitespace removed - return trimmed_mime_type + -- Return the mime type with whitespace removed + return trimmed_mime_type end -- Function to check if a given mime type is an archive ---@param mime_type string|nil The mime type of the file ---@return boolean is_archive Whether the mime type is an archive local function is_archive_mime_type(mime_type) - -- + -- - -- If the mime type is nil, return false - if not mime_type then return false end + -- If the mime type is nil, return false + if not mime_type then return false end - -- Standardise the mime type - local standardised_mime_type = standardise_mime_type(mime_type) + -- Standardise the mime type + local standardised_mime_type = standardise_mime_type(mime_type) - -- Get the archive extractor for the mime type - local archive_extractor = - ARCHIVE_MIME_TYPE_TO_EXTRACTOR_MAP[standardised_mime_type] + -- Get the archive extractor for the mime type + local archive_extractor = + ARCHIVE_MIME_TYPE_TO_EXTRACTOR_MAP[standardised_mime_type] - -- Return if an extractor exists for the mime type - return archive_extractor ~= nil + -- Return if an extractor exists for the mime type + return archive_extractor ~= nil end -- Function to check if a given file extension @@ -843,58 +818,58 @@ end ---@param file_extension string|nil The file extension of the file ---@return boolean is_archive Whether the file extension is an archive local function is_archive_file_extension(file_extension) - -- + -- - -- If the file extension is nil, return false - if not file_extension then return false end + -- If the file extension is nil, return false + if not file_extension then return false end - -- Make the file extension lower case - file_extension = file_extension:lower() + -- Make the file extension lower case + file_extension = file_extension:lower() - -- Trim the whitespace from the file extension - file_extension = string_trim(file_extension) + -- Trim the whitespace from the file extension + file_extension = string_trim(file_extension) - -- Get if the file extension is an archive - local is_archive = table_get(ARCHIVE_FILE_EXTENSIONS, file_extension, false) + -- Get if the file extension is an archive + local is_archive = table_get(ARCHIVE_FILE_EXTENSIONS, file_extension, false) - -- Return if the file extension is an archive file extension - return is_archive + -- Return if the file extension is an archive file extension + return is_archive end -- Function to get the mime type of a file ---@param file_path string The path to the file ---@return string mime_type The mime type of the file local function get_mime_type(file_path) - -- + -- - -- Get the output of the file command - local output, _ = Command("file") - :args({ + -- Get the output of the file command + local output, _ = Command("file") + :args({ - -- Don't prepend file names to the output - "-b", + -- Don't prepend file names to the output + "-b", - -- Print the mime type of the file - "--mime-type", + -- Print the mime type of the file + "--mime-type", - -- The file path to get the mime type of - file_path, - }) - :stdout(Command.PIPED) - :stderr(Command.PIPED) - :output() + -- The file path to get the mime type of + file_path, + }) + :stdout(Command.PIPED) + :stderr(Command.PIPED) + :output() - -- If there is no output, then return an empty string - if not output then return "" end + -- If there is no output, then return an empty string + if not output then return "" end - -- Otherwise, get the mime type from the standard output - local mime_type = string_trim(output.stdout) + -- Otherwise, get the mime type from the standard output + local mime_type = string_trim(output.stdout) - -- Standardise the mime type - local standardised_mime_type = standardise_mime_type(mime_type) + -- Standardise the mime type + local standardised_mime_type = standardise_mime_type(mime_type) - -- Return the standardised mime type - return standardised_mime_type + -- Return the standardised mime type + return standardised_mime_type end -- Function to get a temporary name. @@ -902,8 +877,8 @@ end ---@param path string The path to the item to create a temporary name ---@return string temporary_name The temporary name for the item local function get_temporary_name(path) - return ".tmp_" - .. ya.hash(string.format("extract//%s//%.10f", path, ya.time())) + return ".tmp_" + .. ya.hash(string.format("extract//%s//%.10f", path, ya.time())) end -- Function to get a temporary directory url @@ -912,165 +887,169 @@ end ---@param destination_given boolean|nil Whether the destination was given ---@return Url|nil url The url of the temporary directory local function get_temporary_directory_url(path, destination_given) - -- + -- - -- Get the url of the path given - ---@type Url - local path_url = Url(path) + -- Get the url of the path given + ---@type Url + local path_url = Url(path) - -- Initialise the parent directory to be the path given - ---@type Url - local parent_directory_url = path_url + -- Initialise the parent directory to be the path given + ---@type Url + local parent_directory_url = path_url - -- If the destination is not given - if not destination_given then - -- + -- If the destination is not given + if not destination_given then + -- - -- Get the parent directory of the given path - parent_directory_url = Url(path):parent() + -- Get the parent directory of the given path + parent_directory_url = Url(path):parent() - -- If the parent directory doesn't exist, return nil - if not parent_directory_url then return nil end - end + -- If the parent directory doesn't exist, return nil + if not parent_directory_url then return nil end + end - -- Create the temporary directory path - local temporary_directory_url = - fs.unique_name(parent_directory_url:join(get_temporary_name(path))) + -- Create the temporary directory path + local temporary_directory_url = + fs.unique_name(parent_directory_url:join(get_temporary_name(path))) - -- Return the temporary directory path - return temporary_directory_url + -- Return the temporary directory path + return temporary_directory_url end -- Function to get the configuration from an async function ---@type fun(): Configuration The configuration object local get_config = ya.sync(function(state) - -- + -- - -- Returns the configuration object - return state.config + -- Returns the configuration object + return state.config end) -- Function to get the current working directory ---@type fun(): string Returns the current working directory as a string local get_current_directory = ya.sync( - function(_) return tostring(cx.active.current.cwd) end + function(_) return tostring(cx.active.current.cwd) end ) -- Function to get the path of the hovered item ---@type fun( ---- quote: boolean|nil, -- Whether to escape the characters in the path +--- quote: boolean|nil, -- Whether to escape the characters in the path ---): string|nil The path of the hovered item local get_path_of_hovered_item = ya.sync(function(_, quote) - -- + -- - -- Get the hovered item - local hovered_item = cx.active.current.hovered + -- Get the hovered item + local hovered_item = cx.active.current.hovered - -- If there is no hovered item, exit the function - if not hovered_item then return end + -- If there is no hovered item, exit the function + if not hovered_item then return end - -- Convert the url of the hovered item to a string - local hovered_item_path = tostring(cx.active.current.hovered.url) + -- Convert the url of the hovered item to a string + local hovered_item_path = tostring(cx.active.current.hovered.url) - -- If the quote flag is passed, - -- then quote the path of the hovered item - if quote then hovered_item_path = ya.quote(hovered_item_path) end + -- If the quote flag is passed, + -- then quote the path of the hovered item + if quote then hovered_item_path = ya.quote(hovered_item_path) end - -- Return the path of the hovered item - return hovered_item_path + -- Return the path of the hovered item + return hovered_item_path end) -- Function to get if the hovered item is a directory ---@type fun(): boolean local hovered_item_is_dir = ya.sync(function(_) - -- + -- - -- Get the hovered item - local hovered_item = cx.active.current.hovered + -- Get the hovered item + local hovered_item = cx.active.current.hovered - -- Return if the hovered item exists and is a directory - return hovered_item and hovered_item.cha.is_dir + -- Return if the hovered item exists and is a directory + return hovered_item and hovered_item.cha.is_dir end) -- Function to get if the hovered item is an archive ---@type fun(): boolean local hovered_item_is_archive = ya.sync(function(_) - -- + -- - -- Get the hovered item - local hovered_item = cx.active.current.hovered + -- Get the hovered item + local hovered_item = cx.active.current.hovered - -- Return if the hovered item exists and is an archive - return hovered_item and is_archive_mime_type(hovered_item:mime()) + -- Return if the hovered item exists and is an archive + return hovered_item and is_archive_mime_type(hovered_item:mime()) end) -- Function to get the paths of the selected items ---@type fun( ---- quote: boolean|nil, -- Whether to escape the characters in the path +--- quote: boolean|nil, -- Whether to escape the characters in the path ---): string[]|nil The list of paths of the selected items local get_paths_of_selected_items = ya.sync(function(_, quote) - -- + -- - -- Get the selected items - local selected_items = cx.active.selected + -- Get the selected items + local selected_items = cx.active.selected - -- If there are no selected items, exit the function - if #selected_items == 0 then return end + -- If there are no selected items, exit the function + if #selected_items == 0 then return end - -- Initialise the list of paths of the selected items - local paths_of_selected_items = {} + -- Initialise the list of paths of the selected items + local paths_of_selected_items = {} - -- Iterate over the selected items - for _, item in pairs(selected_items) do - -- + -- Iterate over the selected items + for _, item in pairs(selected_items) do + -- - -- Convert the url of the item to a string - local item_path = tostring(item) + -- Convert the url of the item to a string + local item_path = tostring(item) - -- If the quote flag is passed, - -- then quote the path of the item - if quote then item_path = ya.quote(item_path) end + -- If the quote flag is passed, + -- then quote the path of the item + if quote then item_path = ya.quote(item_path) end - -- Add the path of the item to the list of paths - table.insert(paths_of_selected_items, item_path) - end + -- Add the path of the item to the list of paths + table.insert(paths_of_selected_items, item_path) + end - -- Return the list of paths of the selected items - return paths_of_selected_items + -- Return the list of paths of the selected items + return paths_of_selected_items end) +-- Function to get the number of tabs currently open +---@type fun(): number +local get_number_of_tabs = ya.sync(function() return #cx.tabs end) + -- Function to get the tab preferences ---@type fun(): tab.Preference local get_tab_preferences = ya.sync(function(_) - -- + -- - -- Create the table to store the tab preferences - local tab_preferences = {} + -- Create the table to store the tab preferences + local tab_preferences = {} - -- Iterate over the tab preference keys - for key, _ in pairs(TAB_PREFERENCE_KEYS) do - -- + -- Iterate over the tab preference keys + for key, _ in pairs(TAB_PREFERENCE_KEYS) do + -- - -- Set the key in the table to the value - -- from the state - tab_preferences[key] = cx.active.pref[key] - end + -- Set the key in the table to the value + -- from the state + tab_preferences[key] = cx.active.pref[key] + end - -- Return the tab preferences - return tab_preferences + -- Return the tab preferences + return tab_preferences end) -- Function to get if Yazi is loading ---@type fun(): boolean local yazi_is_loading = ya.sync( - function(_) return cx.active.current.stage.is_loading end + function(_) return cx.active.current.stage.is_loading end ) -- Function to wait until Yazi is loaded ---@return nil local function wait_until_yazi_is_loaded() - while yazi_is_loading() do - end + while yazi_is_loading() do + end end -- Function to choose which group of items to operate on. @@ -1080,120 +1059,120 @@ end -- to prompt the user. ---@type fun(): ItemGroup|nil The desired item group local get_item_group_from_state = ya.sync(function(state) - -- + -- - -- Get the hovered item - local hovered_item = cx.active.current.hovered + -- Get the hovered item + local hovered_item = cx.active.current.hovered - -- The boolean representing that there are no selected items - local no_selected_items = #cx.active.selected == 0 + -- The boolean representing that there are no selected items + local no_selected_items = #cx.active.selected == 0 - -- If there is no hovered item - if not hovered_item then - -- + -- If there is no hovered item + if not hovered_item then + -- - -- If there are no selected items, exit the function - if no_selected_items then - return + -- If there are no selected items, exit the function + if no_selected_items then + return - -- Otherwise, if the configuration is set to have a hovered item, - -- exit the function - elseif state.config.must_have_hovered_item then - return + -- Otherwise, if the configuration is set to have a hovered item, + -- exit the function + elseif state.config.must_have_hovered_item then + return - -- Otherwise, return the enum for the selected items - else - return ItemGroup.Selected - end + -- Otherwise, return the enum for the selected items + else + return ItemGroup.Selected + end - -- Otherwise, there is a hovered item - -- and if there are no selected items, - -- return the enum for the hovered item. - elseif no_selected_items then - return ItemGroup.Hovered + -- Otherwise, there is a hovered item + -- and if there are no selected items, + -- return the enum for the hovered item. + elseif no_selected_items then + return ItemGroup.Hovered - -- Otherwise if there are selected items and the user wants a prompt, - -- then tells the calling function to prompt them - elseif state.config.prompt then - return ItemGroup.Prompt + -- Otherwise if there are selected items and the user wants a prompt, + -- then tells the calling function to prompt them + elseif state.config.prompt then + return ItemGroup.Prompt - -- Otherwise, if the hovered item is selected, - -- then return the enum for the selected items - elseif hovered_item:is_selected() then - return ItemGroup.Selected + -- Otherwise, if the hovered item is selected, + -- then return the enum for the selected items + elseif hovered_item:is_selected() then + return ItemGroup.Selected - -- Otherwise, return the enum for the hovered item - else - return ItemGroup.Hovered - end + -- Otherwise, return the enum for the hovered item + else + return ItemGroup.Hovered + end end) -- Function to prompt the user for their desired item group ---@return ItemGroup|nil item_group The item group selected by the user local function prompt_for_desired_item_group() - -- + -- - -- Get the configuration - local config = get_config() + -- Get the configuration + local config = get_config() - -- Get the default item group - ---@type ItemGroup|nil - local default_item_group = config.default_item_group_for_prompt + -- Get the default item group + ---@type ItemGroup|nil + local default_item_group = config.default_item_group_for_prompt - -- Get the input options - local input_options = INPUT_OPTIONS_TABLE[default_item_group] + -- Get the input options + local input_options = INPUT_OPTIONS_TABLE[default_item_group] - -- If the default item group is None, then set it to nil - if default_item_group == ItemGroup.None then default_item_group = nil end + -- If the default item group is None, then set it to nil + if default_item_group == ItemGroup.None then default_item_group = nil end - -- Prompt the user for their input - local user_input, event = get_user_input( - "Operate on hovered or selected items? " .. input_options - ) + -- Prompt the user for their input + local user_input, event = get_user_input( + "Operate on hovered or selected items? " .. input_options + ) - -- If the user input is empty, then exit the function - if not user_input then return end + -- If the user input is empty, then exit the function + if not user_input then return end - -- Lowercase the user's input - user_input = user_input:lower() + -- Lowercase the user's input + user_input = user_input:lower() - -- If the user did not confirm the input, exit the function - if event ~= 1 then - return + -- If the user did not confirm the input, exit the function + if event ~= 1 then + return - -- Otherwise, if the user's input starts with "h", - -- return the item group representing the hovered item - elseif user_input:find("^h") then - return ItemGroup.Hovered + -- Otherwise, if the user's input starts with "h", + -- return the item group representing the hovered item + elseif user_input:find("^h") then + return ItemGroup.Hovered - -- Otherwise, if the user's input starts with "s", - -- return the item group representing the selected items - elseif user_input:find("^s") then - return ItemGroup.Selected + -- Otherwise, if the user's input starts with "s", + -- return the item group representing the selected items + elseif user_input:find("^s") then + return ItemGroup.Selected - -- Otherwise, return the default item group - else - return default_item_group - end + -- Otherwise, return the default item group + else + return default_item_group + end end -- Function to get the item group ---@return ItemGroup|nil item_group The desired item group local function get_item_group() - -- + -- - -- Get the item group from the state - local item_group = get_item_group_from_state() + -- Get the item group from the state + local item_group = get_item_group_from_state() - -- If the item group isn't the prompt one, - -- then return the item group immediately - if item_group ~= ItemGroup.Prompt then - return item_group + -- If the item group isn't the prompt one, + -- then return the item group immediately + if item_group ~= ItemGroup.Prompt then + return item_group - -- Otherwise, prompt the user for the desired item group - else - return prompt_for_desired_item_group() - end + -- Otherwise, prompt the user for the desired item group + else + return prompt_for_desired_item_group() + end end -- Function to get all the items in the given directory @@ -1202,93 +1181,93 @@ end ---@param directories_only boolean|nil Whether to only get directories ---@return string[] directory_items The list of urls to the directory items local function get_directory_items( - directory_path, - get_hidden_items, - directories_only + directory_path, + get_hidden_items, + directories_only ) - -- + -- - -- Initialise the list of directory items - ---@type string[] - local directory_items = {} + -- Initialise the list of directory items + ---@type string[] + local directory_items = {} - -- Read the contents of the directory - local directory_contents, _ = fs.read_dir(Url(directory_path), {}) + -- Read the contents of the directory + local directory_contents, _ = fs.read_dir(Url(directory_path), {}) - -- If there are no directory contents, - -- then return the empty list of directory items - if not directory_contents then return directory_items end + -- If there are no directory contents, + -- then return the empty list of directory items + if not directory_contents then return directory_items end - -- Iterate over the directory contents - for _, item in ipairs(directory_contents) do - -- + -- Iterate over the directory contents + for _, item in ipairs(directory_contents) do + -- - -- If the get hidden items flag is set to false - -- and the item is a hidden item, - -- then continue the loop - if not get_hidden_items and item.cha.is_hidden then goto continue end + -- If the get hidden items flag is set to false + -- and the item is a hidden item, + -- then continue the loop + if not get_hidden_items and item.cha.is_hidden then goto continue end - -- If the directories only flag is passed - -- and the item is not a directory, - -- then continue the loop - if directories_only and not item.cha.is_dir then goto continue end + -- If the directories only flag is passed + -- and the item is not a directory, + -- then continue the loop + if directories_only and not item.cha.is_dir then goto continue end - -- Otherwise, add the item path to the list of directory items - table.insert(directory_items, tostring(item.url)) + -- Otherwise, add the item path to the list of directory items + table.insert(directory_items, tostring(item.url)) - -- The continue label to continue the loop - ::continue:: - end + -- The continue label to continue the loop + ::continue:: + end - -- Return the list of directory items - return directory_items + -- Return the list of directory items + return directory_items end -- Function to skip child directories with only one directory ---@param initial_directory_path string The path of the initial directory ---@return nil local function skip_single_child_directories(initial_directory_path) - -- + -- - -- Initialise the directory variable to the initial directory given - local directory = initial_directory_path + -- Initialise the directory variable to the initial directory given + local directory = initial_directory_path - -- Get the tab preferences - local tab_preferences = get_tab_preferences() + -- Get the tab preferences + local tab_preferences = get_tab_preferences() - -- Start an infinite loop - while true do - -- + -- Start an infinite loop + while true do + -- - -- Get all the items in the current directory - local directory_items = - get_directory_items(directory, tab_preferences.show_hidden) + -- Get all the items in the current directory + local directory_items = + get_directory_items(directory, tab_preferences.show_hidden) - -- If the number of directory items is not 1, - -- then break out of the loop. - if #directory_items ~= 1 then break end + -- If the number of directory items is not 1, + -- then break out of the loop. + if #directory_items ~= 1 then break end - -- Otherwise, get the directory item - local directory_item = table.unpack(directory_items) + -- Otherwise, get the directory item + local directory_item = table.unpack(directory_items) - -- Get the cha object of the directory item - -- and don't follow symbolic links - local directory_item_cha = fs.cha(Url(directory_item), false) + -- Get the cha object of the directory item + -- and don't follow symbolic links + local directory_item_cha = fs.cha(Url(directory_item), false) - -- If the cha object of the directory item is nil - -- then break the loop - if not directory_item_cha then break end + -- If the cha object of the directory item is nil + -- then break the loop + if not directory_item_cha then break end - -- If the directory item is not a directory, - -- break the loop - if not directory_item_cha.is_dir then break end + -- If the directory item is not a directory, + -- break the loop + if not directory_item_cha.is_dir then break end - -- Otherwise, set the directory to the inner directory - directory = directory_item - end + -- Otherwise, set the directory to the inner directory + directory = directory_item + end - -- Emit the change directory command to change to the directory variable - ya.manager_emit("cd", { directory }) + -- Emit the change directory command to change to the directory variable + ya.manager_emit("cd", { directory }) end -- Class implementations @@ -1299,51 +1278,51 @@ end ---@param config Configuration The configuration object ---@return Extractor|nil instance An instance of the extractor if available function Extractor:new(archive_path, destination_path, config) - -- + -- - -- Initialise whether the extractor is available - local available = false + -- Initialise whether the extractor is available + local available = false - -- Iterate over the commands - for _, command in ipairs(self.commands) do - -- + -- Iterate over the commands + for _, command in ipairs(self.commands) do + -- - -- Call the shell command exists function - -- on the command - local exists = async_shell_command_exists(command) + -- Call the shell command exists function + -- on the command + local exists = async_shell_command_exists(command) - -- If the command exists - if exists then - -- + -- If the command exists + if exists then + -- - -- Save the command - self.command = command + -- Save the command + self.command = command - -- Set the available variable to true - available = true + -- Set the available variable to true + available = true - -- Break out of the loop - break - end - end + -- Break out of the loop + break + end + end - -- If none of the commands for the extractor are available, - -- then return nil - if not available then return nil end + -- If none of the commands for the extractor are available, + -- then return nil + if not available then return nil end - -- Otherwise, create a new instance - local instance = setmetatable({}, self) + -- Otherwise, create a new instance + local instance = setmetatable({}, self) - -- Set where to find the object's methods or properties - self.__index = self + -- Set where to find the object's methods or properties + self.__index = self - -- Save the parameters given - self.archive_path = archive_path - self.destination_path = destination_path - self.config = config + -- Save the parameters given + self.archive_path = archive_path + self.destination_path = destination_path + self.config = config - -- Return the instance - return instance + -- Return the instance + return instance end -- Function to retry the extractor @@ -1352,264 +1331,264 @@ end ---@param clean_up_wanted boolean|nil Whether to clean up the destination path ---@return ExtractionResult result Result of the extractor function function SevenZip:retry_extractor(extractor_function, clean_up_wanted) - -- + -- - -- Initialise the number of tries - -- to the number of retries plus 1 - local total_number_of_tries = self.config.extract_retries + 1 + -- Initialise the number of tries + -- to the number of retries plus 1 + local total_number_of_tries = self.config.extract_retries + 1 - -- Get the url of the archive - ---@type Url - local archive_url = Url(self.archive_path) + -- Get the url of the archive + ---@type Url + local archive_url = Url(self.archive_path) - -- Get the archive name - local archive_name = archive_url:name() + -- Get the archive name + local archive_name = archive_url:name() - -- If the archive name is nil, - -- return the result of the extractor function - if not archive_name then - return { - successful = false, - error = string.format("%s does not have a name", self.archive_path), - } - end + -- If the archive name is nil, + -- return the result of the extractor function + if not archive_name then + return { + successful = false, + error = string.format("%s does not have a name", self.archive_path), + } + end - -- Initialise the initial password prompt - local initial_password_prompt = string.format("%s password:", archive_name) + -- Initialise the initial password prompt + local initial_password_prompt = string.format("%s password:", archive_name) - -- Initialise the wrong password prompt - local wrong_password_prompt = - string.format("Wrong password, %s password:", archive_name) + -- Initialise the wrong password prompt + local wrong_password_prompt = + string.format("Wrong password, %s password:", archive_name) - -- Initialise the clean up function - local clean_up = clean_up_wanted - and function() fs.remove("dir_all", Url(self.destination_path)) end - or function() end + -- Initialise the clean up function + local clean_up = clean_up_wanted + and function() fs.remove("dir_all", Url(self.destination_path)) end + or function() end - -- Initialise the error message - local error_message = nil + -- Initialise the error message + local error_message = nil - -- Iterate over the number of times to try the extraction - for tries = 0, total_number_of_tries do - -- + -- Iterate over the number of times to try the extraction + for tries = 0, total_number_of_tries do + -- - -- Execute the extractor function - local output, error = extractor_function() + -- Execute the extractor function + local output, error = extractor_function() - -- If there is no output - if not output then - -- + -- If there is no output + if not output then + -- - -- Clean up the extracted files - clean_up() + -- Clean up the extracted files + clean_up() - -- Return the result of the extractor function - return { - successful = false, - error = tostring(error), - } - end + -- Return the result of the extractor function + return { + successful = false, + error = tostring(error), + } + end - -- If the output status code is 0, - -- which means the command was successful, - -- return the result of the extractor function - if output.status.code == 0 then - return { - successful = true, - output = output.stdout, - } - end + -- If the output status code is 0, + -- which means the command was successful, + -- return the result of the extractor function + if output.status.code == 0 then + return { + successful = true, + output = output.stdout, + } + end - -- Set the error message to the standard error - error_message = output.stderr + -- Set the error message to the standard error + error_message = output.stderr - -- If the command failed for a reason other - -- than the archive being encrypted, - -- or if the current try count - -- is the same as the total number of tries - if - not ( - output.status.code == 2 - and error_message:lower():find("wrong password") - ) or tries == total_number_of_tries - then - -- + -- If the command failed for a reason other + -- than the archive being encrypted, + -- or if the current try count + -- is the same as the total number of tries + if + not ( + output.status.code == 2 + and error_message:lower():find("wrong password") + ) or tries == total_number_of_tries + then + -- - -- Clean up the extracted files - clean_up() + -- Clean up the extracted files + clean_up() - -- Return the extractor function result - return { - successful = false, - error = error_message, - } - end + -- Return the extractor function result + return { + successful = false, + error = error_message, + } + end - -- Otherwise, get the prompt for the password - local password_prompt = tries == 0 and initial_password_prompt - or wrong_password_prompt + -- Otherwise, get the prompt for the password + local password_prompt = tries == 0 and initial_password_prompt + or wrong_password_prompt - -- Initialise the width of the input element - local input_width = DEFAULT_INPUT_OPTIONS.position.w + -- Initialise the width of the input element + local input_width = DEFAULT_INPUT_OPTIONS.position.w - -- If the length of the password prompt is larger - -- than the default input with, set the input width - -- to the length of the password prompt + 1 - if #password_prompt > input_width then - input_width = #password_prompt + 1 - end + -- If the length of the password prompt is larger + -- than the default input with, set the input width + -- to the length of the password prompt + 1 + if #password_prompt > input_width then + input_width = #password_prompt + 1 + end - -- Get the new position object - -- for the new input element - ---@type Position - local new_position = - merge_tables(DEFAULT_INPUT_OPTIONS.position, { w = input_width }) + -- Get the new position object + -- for the new input element + ---@type Position + local new_position = + merge_tables(DEFAULT_INPUT_OPTIONS.position, { w = input_width }) - -- Ask the user for the password - local user_input, event = - ---@diagnostic disable-next-line: missing-fields - get_user_input(password_prompt, { position = new_position }) + -- Ask the user for the password + local user_input, event = + ---@diagnostic disable-next-line: missing-fields + get_user_input(password_prompt, { position = new_position }) - -- If the user has confirmed the input, - -- and the user input is not nil, - -- set the password to the user's input - if event == 1 and user_input ~= nil then - self.password = user_input + -- If the user has confirmed the input, + -- and the user input is not nil, + -- set the password to the user's input + if event == 1 and user_input ~= nil then + self.password = user_input - -- Otherwise - else - -- + -- Otherwise + else + -- - -- Call the clean up function - clean_up() + -- Call the clean up function + clean_up() - -- Return the result of the extractor command - return { - successful = false, - cancelled = true, - error = error_message, - } - end - end + -- Return the result of the extractor command + return { + successful = false, + cancelled = true, + error = error_message, + } + end + end - -- If all the tries have been exhausted, - -- call the clean up function - clean_up() + -- If all the tries have been exhausted, + -- call the clean up function + clean_up() - -- Return the result of the extractor command - return { - successful = false, - error = error_message, - } + -- Return the result of the extractor command + return { + successful = false, + error = error_message, + } end -- Function to list the archive items with the command ---@type ExtractorListItemsCommand function SevenZip:list_items_command() - -- + -- - -- Initialise the arguments for the command - local arguments = { + -- Initialise the arguments for the command + local arguments = { - -- List the items in the archive - "l", + -- List the items in the archive + "l", - -- Use UTF-8 encoding for console input and output - "-sccUTF-8", + -- Use UTF-8 encoding for console input and output + "-sccUTF-8", - -- Pass the password to the command - "-p" .. self.password, + -- Pass the password to the command + "-p" .. self.password, - -- Remove the headers (undocumented switch) - "-ba", + -- Remove the headers (undocumented switch) + "-ba", - -- The archive path - self.archive_path, - } + -- The archive path + self.archive_path, + } - -- Return the result of the command to list the items in the archive - return Command(self.command) - :args(arguments) - :stdout(Command.PIPED) - :stderr(Command.PIPED) - :output() + -- Return the result of the command to list the items in the archive + return Command(self.command) + :args(arguments) + :stdout(Command.PIPED) + :stderr(Command.PIPED) + :output() end -- Function to get the items in the archive ---@type ExtractorGetItems function SevenZip:get_items() - -- + -- - -- Initialise the list of files in the archive - ---@type string[] - local files = {} + -- Initialise the list of files in the archive + ---@type string[] + local files = {} - -- Initialise the list of directories - ---@type string[] - local directories = {} + -- Initialise the list of directories + ---@type string[] + local directories = {} - -- Call the function to retry the extractor command - -- with the list items in the archive function - local extractor_result = self:retry_extractor( - function() return self:list_items_command() end - ) + -- Call the function to retry the extractor command + -- with the list items in the archive function + local extractor_result = self:retry_extractor( + function() return self:list_items_command() end + ) - -- Get the output - local output = extractor_result.output + -- Get the output + local output = extractor_result.output - -- Get the error - local error = extractor_result.error + -- Get the error + local error = extractor_result.error - -- If the extractor command was not successful, - -- or the output was nil, - -- then return nil the error message, - -- and nil as the correct password - if not extractor_result.successful or not output then - return files, directories, error - end + -- If the extractor command was not successful, + -- or the output was nil, + -- then return nil the error message, + -- and nil as the correct password + if not extractor_result.successful or not output then + return files, directories, error + end - -- Otherwise, split the output at the newline character - local output_lines = string_split(output, "\n") + -- Otherwise, split the output at the newline character + local output_lines = string_split(output, "\n") - -- The pattern to get the information from an archive item - ---@type string - local archive_item_info_pattern = "%s+([%.%a]+)%s+(%d+)%s+(%d+)%s+(.+)$" + -- The pattern to get the information from an archive item + ---@type string + local archive_item_info_pattern = "%s+([%.%a]+)%s+(%d+)%s+(%d+)%s+(.+)$" - -- Iterate over the lines of the output - for _, line in ipairs(output_lines) do - -- + -- Iterate over the lines of the output + for _, line in ipairs(output_lines) do + -- - -- Get the information about the archive item from the line. - -- The information is in the format: - -- Attributes, Size, Compressed Size, File Path - local attributes, _, _, file_path = - line:match(archive_item_info_pattern) + -- Get the information about the archive item from the line. + -- The information is in the format: + -- Attributes, Size, Compressed Size, File Path + local attributes, _, _, file_path = + line:match(archive_item_info_pattern) - -- If the file path doesn't exist, then continue the loop - if not file_path then goto continue end + -- If the file path doesn't exist, then continue the loop + if not file_path then goto continue end - -- If the attributes of the item starts with a "D", - -- which means the item is a directory - if attributes and attributes:find("^D") then - -- + -- If the attributes of the item starts with a "D", + -- which means the item is a directory + if attributes and attributes:find("^D") then + -- - -- Add the directory to the list of directories - table.insert(directories, file_path) + -- Add the directory to the list of directories + table.insert(directories, file_path) - -- Continue the loop - goto continue - end + -- Continue the loop + goto continue + end - -- Otherwise, add the file path to the list of archive items - table.insert(files, file_path) + -- Otherwise, add the file path to the list of archive items + table.insert(files, file_path) - -- The continue label to continue the loop - ::continue:: - end + -- The continue label to continue the loop + ::continue:: + end - -- Return the list of files, the list of directories, - -- the error message, and the password - return files, directories, error + -- Return the list of files, the list of directories, + -- the error message, and the password + return files, directories, error end -- Function to extract an archive using the command @@ -1618,213 +1597,213 @@ end ---@return CommandOutput|nil output The output of the command ---@return Error|nil error The error if any function SevenZip:extract_command(extract_files_only, extract_behaviour) - -- + -- - -- Initialise the extract files only flag to false if it's not given - extract_files_only = extract_files_only or false + -- Initialise the extract files only flag to false if it's not given + extract_files_only = extract_files_only or false - -- Initialise the extract behaviour to rename if it's not given - extract_behaviour = - self.extract_behaviour_map[extract_behaviour or ExtractBehaviour.Rename] + -- Initialise the extract behaviour to rename if it's not given + extract_behaviour = + self.extract_behaviour_map[extract_behaviour or ExtractBehaviour.Rename] - -- Initialise the extraction mode to use. - -- By default, it extracts the archive with - -- full paths, which keeps the archive structure. - local extraction_mode = "x" + -- Initialise the extraction mode to use. + -- By default, it extracts the archive with + -- full paths, which keeps the archive structure. + local extraction_mode = "x" - -- If the extract files only flag is passed - if extract_files_only then - -- + -- If the extract files only flag is passed + if extract_files_only then + -- - -- Use the regular extract, - -- without the full paths, which will move - -- all files in the archive into the current directory - -- and ignore the archive folder structure. - extraction_mode = "e" - end + -- Use the regular extract, + -- without the full paths, which will move + -- all files in the archive into the current directory + -- and ignore the archive folder structure. + extraction_mode = "e" + end - -- Initialise the arguments for the command - local arguments = { + -- Initialise the arguments for the command + local arguments = { - -- The extraction mode - extraction_mode, + -- The extraction mode + extraction_mode, - -- Assume yes to all prompts - "-y", + -- Assume yes to all prompts + "-y", - -- Use UTF-8 encoding for console input and output - "-sccUTF-8", + -- Use UTF-8 encoding for console input and output + "-sccUTF-8", - -- Configure the extraction behaviour - extract_behaviour, + -- Configure the extraction behaviour + extract_behaviour, - -- Pass the password to the command - "-p" .. self.password, + -- Pass the password to the command + "-p" .. self.password, - -- The archive file to extract - self.archive_path, + -- The archive file to extract + self.archive_path, - -- The destination directory path - "-o" .. self.destination_path, - } + -- The destination directory path + "-o" .. self.destination_path, + } - -- Return the command to extract the archive - return Command(self.command) - :args(arguments) - :stdout(Command.PIPED) - :stderr(Command.PIPED) - :output() + -- Return the command to extract the archive + return Command(self.command) + :args(arguments) + :stdout(Command.PIPED) + :stderr(Command.PIPED) + :output() end -- Function to extract the archive ---@type ExtractorExtract function SevenZip:extract(has_only_one_file) - -- + -- - -- Extract the archive with the extractor command - local result = self:retry_extractor( - function() return self:extract_command(has_only_one_file) end, - true - ) + -- Extract the archive with the extractor command + local result = self:retry_extractor( + function() return self:extract_command(has_only_one_file) end, + true + ) - -- Return the extractor result - return result + -- Return the extractor result + return result end -- Function to list the archive items with the command ---@type ExtractorListItemsCommand function Tar:list_items_command() - -- + -- - -- Initialise the arguments for the command - local arguments = { + -- Initialise the arguments for the command + local arguments = { - -- List the items in the archive - "-t", + -- List the items in the archive + "-t", - -- Pass the file - "-f", + -- Pass the file + "-f", - -- The archive file path - self.archive_path, - } + -- The archive file path + self.archive_path, + } - -- Return the result of the command - return Command(self.command) - :args(arguments) - :stdout(Command.PIPED) - :stderr(Command.PIPED) - :output() + -- Return the result of the command + return Command(self.command) + :args(arguments) + :stdout(Command.PIPED) + :stderr(Command.PIPED) + :output() end -- Function to get the items in the archive ---@type ExtractorGetItems function Tar:get_items() - -- + -- - -- Call the function to get the list of items in the archive - local output, error = self:list_items_command() + -- Call the function to get the list of items in the archive + local output, error = self:list_items_command() - -- Initialise the list of files - ---@type string[] - local files = {} + -- Initialise the list of files + ---@type string[] + local files = {} - -- Initialise the list of directories - ---@type string[] - local directories = {} + -- Initialise the list of directories + ---@type string[] + local directories = {} - -- If there is no output, return the empty lists and the error - if not output then return files, directories, tostring(error) end + -- If there is no output, return the empty lists and the error + if not output then return files, directories, tostring(error) end - -- Otherwise, split the output into lines and iterate over it - for _, line in ipairs(string_split(output.stdout, "\n")) do - -- + -- Otherwise, split the output into lines and iterate over it + for _, line in ipairs(string_split(output.stdout, "\n")) do + -- - -- If the line ends with a slash, it's a directory - if line:sub(-1) == "/" then - -- + -- If the line ends with a slash, it's a directory + if line:sub(-1) == "/" then + -- - -- Add the directory without the trailing slash - -- to the list of directories - table.insert(directories, line:sub(1, -2)) + -- Add the directory without the trailing slash + -- to the list of directories + table.insert(directories, line:sub(1, -2)) - -- Continue the loop - goto continue - end + -- Continue the loop + goto continue + end - -- Otherwise, the item is a file, so add it to the list of files - table.insert(files, line) + -- Otherwise, the item is a file, so add it to the list of files + table.insert(files, line) - -- The label to continue the loop - ::continue:: - end + -- The label to continue the loop + ::continue:: + end - -- Return the list of files and directories and the error - return files, directories, output.stderr + -- Return the list of files and directories and the error + return files, directories, output.stderr end -- Function to extract an archive using the command ---@param extract_behaviour ExtractBehaviour|nil The extract behaviour to use function Tar:extract_command(extract_behaviour) - -- + -- - -- Initialise the extract behaviour to rename if it is not given - extract_behaviour = - self.extract_behaviour_map[extract_behaviour or ExtractBehaviour.Rename] + -- Initialise the extract behaviour to rename if it is not given + extract_behaviour = + self.extract_behaviour_map[extract_behaviour or ExtractBehaviour.Rename] - -- Initialise the arguments for the command - local arguments = { + -- Initialise the arguments for the command + local arguments = { - -- Extract the archive - "-x", + -- Extract the archive + "-x", - -- Verbose - "-v", + -- Verbose + "-v", - -- The extract behaviour flag - extract_behaviour, + -- The extract behaviour flag + extract_behaviour, - -- Specify the destination directory - "-C", + -- Specify the destination directory + "-C", - -- The destination directory path - self.destination_path, - } + -- The destination directory path + self.destination_path, + } - -- If keeping permissions is wanted, add the -p flag - if self.config.preserve_file_permissions then - table.insert(arguments, "-p") - end + -- If keeping permissions is wanted, add the -p flag + if self.config.preserve_file_permissions then + table.insert(arguments, "-p") + end - -- Add the -f flag and the archive path to the arguments - table.insert(arguments, "-f") - table.insert(arguments, self.archive_path) + -- Add the -f flag and the archive path to the arguments + table.insert(arguments, "-f") + table.insert(arguments, self.archive_path) - -- Create the destination path first. - -- - -- This is required because tar does not - -- automatically create the directory - -- pointed to by the -C flag. - -- Instead, tar just tries to change - -- the working directory to the directory - -- pointed to by the -C flag, which can - -- fail if the directory does not exist. - -- - -- GNU tar has a --one-top-level=[DIR] option, - -- which will automatically create the directory - -- given, but macOS tar does not have this option. - -- - -- The error here is ignored because if there - -- is an error creating the directory, - -- then the extractor will fail anyway. - fs.create("dir_all", Url(self.destination_path)) + -- Create the destination path first. + -- + -- This is required because tar does not + -- automatically create the directory + -- pointed to by the -C flag. + -- Instead, tar just tries to change + -- the working directory to the directory + -- pointed to by the -C flag, which can + -- fail if the directory does not exist. + -- + -- GNU tar has a --one-top-level=[DIR] option, + -- which will automatically create the directory + -- given, but macOS tar does not have this option. + -- + -- The error here is ignored because if there + -- is an error creating the directory, + -- then the extractor will fail anyway. + fs.create("dir_all", Url(self.destination_path)) - -- Return the output of the command - return Command(self.command) - :args(arguments) - :stdout(Command.PIPED) - :stderr(Command.PIPED) - :output() + -- Return the output of the command + return Command(self.command) + :args(arguments) + :stdout(Command.PIPED) + :stderr(Command.PIPED) + :output() end -- Function to extract the archive. @@ -1834,35 +1813,35 @@ end -- extract compressed tarballs. ---@type ExtractorExtract function Tar:extract(_) - -- + -- - -- Call the command to extract the archive - local output, error = self:extract_command() + -- Call the command to extract the archive + local output, error = self:extract_command() - -- If there is no output, return the result - if not output then - return { - successful = false, - error = tostring(error), - } - end + -- If there is no output, return the result + if not output then + return { + successful = false, + error = tostring(error), + } + end - -- Otherwise, if the status code is not 0, - -- which means the extraction was not successful, - -- return the result - if output.status.code ~= 0 then - return { - successful = false, - output = output.stdout, - error = output.stderr, - } - end + -- Otherwise, if the status code is not 0, + -- which means the extraction was not successful, + -- return the result + if output.status.code ~= 0 then + return { + successful = false, + output = output.stdout, + error = output.stderr, + } + end - -- Otherwise, return the successful result - return { - successful = true, - output = output.stdout, - } + -- Otherwise, return the successful result + return { + successful = true, + output = output.stdout, + } end -- Functions for the commands @@ -1874,84 +1853,84 @@ end ---@return ExtractionResult result The results of getting the extractor ---@return Extractor|nil extractor The extractor for the file type local function get_extractor(archive_path, destination_path, config) - -- + -- - -- Get the mime type of the archive file - local mime_type = get_mime_type(archive_path) + -- Get the mime type of the archive file + local mime_type = get_mime_type(archive_path) - -- Get the extractor for the mime type - local extractor = ARCHIVE_MIME_TYPE_TO_EXTRACTOR_MAP[mime_type] + -- Get the extractor for the mime type + local extractor = ARCHIVE_MIME_TYPE_TO_EXTRACTOR_MAP[mime_type] - -- If there is no extractor, - -- return that it is not successful, - -- but that it has been cancelled - -- as the mime type is not an archive - if not extractor then - return { - successful = false, - cancelled = true, - } - end + -- If there is no extractor, + -- return that it is not successful, + -- but that it has been cancelled + -- as the mime type is not an archive + if not extractor then + return { + successful = false, + cancelled = true, + } + end - -- Instantiate an instance of the extractor - local extractor_instance = - extractor:new(archive_path, destination_path, config) + -- Instantiate an instance of the extractor + local extractor_instance = + extractor:new(archive_path, destination_path, config) - -- While the extractor instance failed to be created - while not extractor_instance do - -- + -- While the extractor instance failed to be created + while not extractor_instance do + -- - -- If the extractor instance is the default extractor, - -- then return an error telling the user to install the - -- default extractor - if extractor.name == DefaultExtractor.name then - return { - successful = false, - error = table.concat({ - string.format( - "%s is not installed,", - DefaultExtractor.name - ), - "please install it before using the 'extract' command", - }, " "), - } - end + -- If the extractor instance is the default extractor, + -- then return an error telling the user to install the + -- default extractor + if extractor.name == DefaultExtractor.name then + return { + successful = false, + error = table.concat({ + string.format( + "%s is not installed,", + DefaultExtractor.name + ), + "please install it before using the 'extract' command", + }, " "), + } + end - -- Try instantiating the default extractor - extractor_instance = - DefaultExtractor:new(archive_path, destination_path, config) - end + -- Try instantiating the default extractor + extractor_instance = + DefaultExtractor:new(archive_path, destination_path, config) + end - -- If the user wants to preserve file permissions, - -- and the target extractor for the mime type supports - -- preserving file permissions, but the extractor - -- instantiated does not, show a warning to the user - if - config.preserve_file_permissions - and extractor.supports_file_permissions - and not extractor_instance.supports_file_permissions - then - -- + -- If the user wants to preserve file permissions, + -- and the target extractor for the mime type supports + -- preserving file permissions, but the extractor + -- instantiated does not, show a warning to the user + if + config.preserve_file_permissions + and extractor.supports_file_permissions + and not extractor_instance.supports_file_permissions + then + -- - -- The warning to show the user - local warning = table.concat({ - string.format( - "%s is not installed, defaulting to %s.", - extractor.name, - extractor_instance.name - ), - string.format( - "However, %s does not support preserving file permissions.", - extractor_instance.name - ), - }, "\n") + -- The warning to show the user + local warning = table.concat({ + string.format( + "%s is not installed, defaulting to %s.", + extractor.name, + extractor_instance.name + ), + string.format( + "However, %s does not support preserving file permissions.", + extractor_instance.name + ), + }, "\n") - -- Show the warning to the user - show_warning(warning) - end + -- Show the warning to the user + show_warning(warning) + end - -- Return the extractor instance - return { successful = true }, extractor_instance + -- Return the extractor instance + return { successful = true }, extractor_instance end -- Function to move the extracted items out of the temporary directory @@ -1959,144 +1938,144 @@ end ---@param destination_url Url The url of the destination ---@return ExtractionResult result The result of the move local function move_extracted_items(archive_url, destination_url) - -- + -- - -- The function to clean up the destination directory - -- and return the extractor result in the event of an error - ---@param error string The error message to return - ---@param empty_dir_only boolean|nil Whether to remove the empty dir only - ---@return ExtractionResult - local function fail(error, empty_dir_only) - -- + -- The function to clean up the destination directory + -- and return the extractor result in the event of an error + ---@param error string The error message to return + ---@param empty_dir_only boolean|nil Whether to remove the empty dir only + ---@return ExtractionResult + local function fail(error, empty_dir_only) + -- - -- Clean up the destination path - fs.remove(empty_dir_only and "dir" or "dir_all", destination_url) + -- Clean up the destination path + fs.remove(empty_dir_only and "dir" or "dir_all", destination_url) - -- Return the extractor result - ---@type ExtractionResult - return { - successful = false, - error = error, - } - end + -- Return the extractor result + ---@type ExtractionResult + return { + successful = false, + error = error, + } + end - -- Get the extracted items in the destination. - -- There is a limit of 2 as we just need to - -- know if the destination contains only - -- a single item or not. - local extracted_items = fs.read_dir(destination_url, { limit = 2 }) + -- Get the extracted items in the destination. + -- There is a limit of 2 as we just need to + -- know if the destination contains only + -- a single item or not. + local extracted_items = fs.read_dir(destination_url, { limit = 2 }) - -- If the extracted items doesn't exist, - -- clean up and return the error - if not extracted_items then - return fail( - string.format( - "Failed to read the destination directory: %s", - tostring(destination_url) - ) - ) - end + -- If the extracted items doesn't exist, + -- clean up and return the error + if not extracted_items then + return fail( + string.format( + "Failed to read the destination directory: %s", + tostring(destination_url) + ) + ) + end - -- If there are no extracted items, - -- clean up and return the error - if #extracted_items == 0 then - return fail("No files extracted from the archive", true) - end + -- If there are no extracted items, + -- clean up and return the error + if #extracted_items == 0 then + return fail("No files extracted from the archive", true) + end - -- Get the parent directory of the destination - local parent_directory_url = destination_url:parent() + -- Get the parent directory of the destination + local parent_directory_url = destination_url:parent() - -- If the parent directory doesn't exist, - -- clean up and return the error - if not parent_directory_url then - return fail("Destination path has no parent directory") - end + -- If the parent directory doesn't exist, + -- clean up and return the error + if not parent_directory_url then + return fail("Destination path has no parent directory") + end - -- Get the name of the archive without the extension - local archive_name = archive_url:stem() + -- Get the name of the archive without the extension + local archive_name = archive_url:stem() - -- If the name of the archive doesn't exist, - -- clean up and return the error - if not archive_name then - return fail("Archive has no name without its extension") - end + -- If the name of the archive doesn't exist, + -- clean up and return the error + if not archive_name then + return fail("Archive has no name without its extension") + end - -- Get the first extracted item - local first_extracted_item = table.unpack(extracted_items) + -- Get the first extracted item + local first_extracted_item = table.unpack(extracted_items) - -- Initialise the variable to indicate whether the archive has only one item - local only_one_item = false + -- Initialise the variable to indicate whether the archive has only one item + local only_one_item = false - -- Initialise the target directory url to move the extracted items to, - -- which is the parent directory of the archive - -- joined with the file name of the archive without the extension - local target_url = parent_directory_url:join(archive_name) + -- Initialise the target directory url to move the extracted items to, + -- which is the parent directory of the archive + -- joined with the file name of the archive without the extension + local target_url = parent_directory_url:join(archive_name) - -- If there is only one item in the archive - if #extracted_items == 1 then - -- + -- If there is only one item in the archive + if #extracted_items == 1 then + -- - -- Set the only one item variable to true - only_one_item = true + -- Set the only one item variable to true + only_one_item = true - -- Get the name of the first extracted item - local first_extracted_item_name = first_extracted_item.url:name() + -- Get the name of the first extracted item + local first_extracted_item_name = first_extracted_item.url:name() - -- If the first extracted item has no name, - -- then clean up and return the error - if not first_extracted_item_name then - return fail("The only extracted item has no name") - end + -- If the first extracted item has no name, + -- then clean up and return the error + if not first_extracted_item_name then + return fail("The only extracted item has no name") + end - -- Otherwise, set the target url to the parent directory - -- of the destination joined with the file name of the extracted item - target_url = parent_directory_url:join(first_extracted_item_name) - end + -- Otherwise, set the target url to the parent directory + -- of the destination joined with the file name of the extracted item + target_url = parent_directory_url:join(first_extracted_item_name) + end - -- Get a unique name for the target url - local unique_target_url = fs.unique_name(target_url) + -- Get a unique name for the target url + local unique_target_url = fs.unique_name(target_url) - -- If the unique target url is nil, - -- clean up and return the error - if not unique_target_url then - return fail( - "Failed to get a unique name to move the extracted items to" - ) - end + -- If the unique target url is nil, + -- clean up and return the error + if not unique_target_url then + return fail( + "Failed to get a unique name to move the extracted items to" + ) + end - -- Set the target path to the string of the target url - local target_path = tostring(unique_target_url) + -- Set the target path to the string of the target url + local target_path = tostring(unique_target_url) - -- Initialise the move successful variable and the error message - local error_message, move_successful = nil, false + -- Initialise the move successful variable and the error message + local error_message, move_successful = nil, false - -- If there is only one item in the archive - if only_one_item then - -- + -- If there is only one item in the archive + if only_one_item then + -- - -- Move the item to the target path - move_successful, error_message = - os.rename(tostring(first_extracted_item.url), target_path) + -- Move the item to the target path + move_successful, error_message = + os.rename(tostring(first_extracted_item.url), target_path) - -- Otherwise - else - -- + -- Otherwise + else + -- - -- Rename the destination directory itself to the target path - move_successful, error_message = - os.rename(tostring(destination_url), target_path) - end + -- Rename the destination directory itself to the target path + move_successful, error_message = + os.rename(tostring(destination_url), target_path) + end - -- Clean up the destination directory - fs.remove(move_successful and "dir" or "dir_all", destination_url) + -- Clean up the destination directory + fs.remove(move_successful and "dir" or "dir_all", destination_url) - -- Return the extractor result with the target path as the - -- path to the extracted items - return { - successful = move_successful, - error = error_message, - extracted_items_path = target_path, - } + -- Return the extractor result with the target path as the + -- path to the extracted items + return { + successful = move_successful, + error = error_message, + extracted_items_path = target_path, + } end -- Function to recursively extract archives @@ -2106,551 +2085,551 @@ end ---@param destination_path string|nil The destination path to extract to ---@return ExtractionResult extraction_result The extraction results local function recursively_extract_archive( - archive_path, - args, - config, - destination_path + archive_path, + args, + config, + destination_path ) - -- + -- - -- Get whether the destination path is given - local destination_path_given = destination_path ~= nil + -- Get whether the destination path is given + local destination_path_given = destination_path ~= nil - -- Initialise the destination path to the archive path if it is not given - local destination = destination_path or archive_path + -- Initialise the destination path to the archive path if it is not given + local destination = destination_path or archive_path - -- Get the temporary directory url - local temporary_directory_url = - get_temporary_directory_url(destination, destination_path_given) + -- Get the temporary directory url + local temporary_directory_url = + get_temporary_directory_url(destination, destination_path_given) - -- If the temporary directory can't be created - -- then return the result - if not temporary_directory_url then - return { - successful = false, - error = "Failed to create a temporary directory", - archive_path = archive_path, - destination_path = destination_path, - } - end + -- If the temporary directory can't be created + -- then return the result + if not temporary_directory_url then + return { + successful = false, + error = "Failed to create a temporary directory", + archive_path = archive_path, + destination_path = destination_path, + } + end - -- Get an extractor for the archive - local get_extractor_result, extractor = - get_extractor(archive_path, tostring(temporary_directory_url), config) + -- Get an extractor for the archive + local get_extractor_result, extractor = + get_extractor(archive_path, tostring(temporary_directory_url), config) - -- If there is no extractor, return the result - if not extractor then - return merge_tables(get_extractor_result, { - archive_path = archive_path, - destination_path = destination_path, - }) - end + -- If there is no extractor, return the result + if not extractor then + return merge_tables(get_extractor_result, { + archive_path = archive_path, + destination_path = destination_path, + }) + end - -- Function to add additional information to the extraction result - -- The additional information are: - -- - The archive path - -- - The destination path - -- - The name of the extractor - ---@param result ExtractionResult The result to add the paths to - ---@return ExtractionResult modified_result The result with the paths added - local function add_additional_info(result) - return merge_tables(result, { - archive_path = archive_path, - destination_path = destination_path, - extractor_name = extractor.name, - }) - end + -- Function to add additional information to the extraction result + -- The additional information are: + -- - The archive path + -- - The destination path + -- - The name of the extractor + ---@param result ExtractionResult The result to add the paths to + ---@return ExtractionResult modified_result The result with the paths added + local function add_additional_info(result) + return merge_tables(result, { + archive_path = archive_path, + destination_path = destination_path, + extractor_name = extractor.name, + }) + end - -- Get the list of archive files and directories, - -- the error message and the password - local archive_files, archive_directories, error = extractor:get_items() + -- Get the list of archive files and directories, + -- the error message and the password + local archive_files, archive_directories, error = extractor:get_items() - -- If there are no are no archive files and directories - if #archive_files == 0 and #archive_directories == 0 then - -- + -- If there are no are no archive files and directories + if #archive_files == 0 and #archive_directories == 0 then + -- - -- The extraction result - ---@type ExtractionResult - local extraction_result = { - successful = false, - error = error or "Archive is empty", - } + -- The extraction result + ---@type ExtractionResult + local extraction_result = { + successful = false, + error = error or "Archive is empty", + } - -- Return the extraction result - return add_additional_info(extraction_result) - end + -- Return the extraction result + return add_additional_info(extraction_result) + end - -- Get if the archive has only one file - local archive_has_only_one_file = #archive_files == 1 - and #archive_directories == 0 + -- Get if the archive has only one file + local archive_has_only_one_file = #archive_files == 1 + and #archive_directories == 0 - -- Extract the given archive - local extraction_result = extractor:extract(archive_has_only_one_file) + -- Extract the given archive + local extraction_result = extractor:extract(archive_has_only_one_file) - -- If the extraction result is not successful, return it - if not extraction_result.successful then - return add_additional_info(extraction_result) - end + -- If the extraction result is not successful, return it + if not extraction_result.successful then + return add_additional_info(extraction_result) + end - -- Get the result of moving the extracted items - local move_result = - move_extracted_items(Url(archive_path), temporary_directory_url) + -- Get the result of moving the extracted items + local move_result = + move_extracted_items(Url(archive_path), temporary_directory_url) - -- Get the extracted items path - local extracted_items_path = move_result.extracted_items_path + -- Get the extracted items path + local extracted_items_path = move_result.extracted_items_path - -- If moving the extracted items isn't successful, - -- or if the extracted items path is nil, - -- or if the user does not want to extract archives recursively, - -- return the move results - if - not move_result.successful - or not extracted_items_path - or not config.recursively_extract_archives - then - return add_additional_info(move_result) - end + -- If moving the extracted items isn't successful, + -- or if the extracted items path is nil, + -- or if the user does not want to extract archives recursively, + -- return the move results + if + not move_result.successful + or not extracted_items_path + or not config.recursively_extract_archives + then + return add_additional_info(move_result) + end - -- Get the url of the extracted items path - ---@type Url - local extracted_items_url = Url(extracted_items_path) + -- Get the url of the extracted items path + ---@type Url + local extracted_items_url = Url(extracted_items_path) - -- Initialise the base url for the extracted items - local base_url = extracted_items_url + -- Initialise the base url for the extracted items + local base_url = extracted_items_url - -- Get the parent directory of the extracted items path - local parent_directory_url = extracted_items_url:parent() + -- Get the parent directory of the extracted items path + local parent_directory_url = extracted_items_url:parent() - -- If the parent directory doesn't exist - if not parent_directory_url then - -- + -- If the parent directory doesn't exist + if not parent_directory_url then + -- - -- Modify the move result with a custom error - ---@type ExtractionResult - local modified_move_result = merge_tables(move_result, { - error = "Archive has no parent directory", - archive_path = archive_path, - destination_path = destination_path, - }) + -- Modify the move result with a custom error + ---@type ExtractionResult + local modified_move_result = merge_tables(move_result, { + error = "Archive has no parent directory", + archive_path = archive_path, + destination_path = destination_path, + }) - -- Return the modified move result - return modified_move_result - end + -- Return the modified move result + return modified_move_result + end - -- If the archive has only one file - if archive_has_only_one_file then - -- + -- If the archive has only one file + if archive_has_only_one_file then + -- - -- Set the base url to the parent directory of the extracted items path - base_url = parent_directory_url - end + -- Set the base url to the parent directory of the extracted items path + base_url = parent_directory_url + end - -- Iterate over the archive files - for _, file in ipairs(archive_files) do - -- + -- Iterate over the archive files + for _, file in ipairs(archive_files) do + -- - -- Get the file extension of the file - local file_extension = file:match(file_extension_pattern) + -- Get the file extension of the file + local file_extension = file:match(file_extension_pattern) - -- If the file extension is not found, then skip the file - if not file_extension then goto continue end + -- If the file extension is not found, then skip the file + if not file_extension then goto continue end - -- If the file extension is not an archive file extension, skip the file - if not is_archive_file_extension(file_extension) then goto continue end + -- If the file extension is not an archive file extension, skip the file + if not is_archive_file_extension(file_extension) then goto continue end - -- Otherwise, get the full url to the archive - local full_archive_url = base_url:join(file) + -- Otherwise, get the full url to the archive + local full_archive_url = base_url:join(file) - -- Get the full path to the archive - local full_archive_path = tostring(full_archive_url) + -- Get the full path to the archive + local full_archive_path = tostring(full_archive_url) - -- Recursively extract the archive - emit_plugin_command( - "extract", - merge_tables(args, { - archive_path = ya.quote(full_archive_path), - remove = true, - }) - ) + -- Recursively extract the archive + emit_plugin_command( + "extract", + merge_tables(args, { + archive_path = ya.quote(full_archive_path), + remove = true, + }) + ) - -- The label the continue the loop - ::continue:: - end + -- The label the continue the loop + ::continue:: + end - -- Return the move result - return add_additional_info(move_result) + -- Return the move result + return add_additional_info(move_result) end -- Function to show an extraction error ---@param extraction_result ExtractionResult The extraction result ---@return nil local function show_extraction_error(extraction_result) - -- + -- - -- The line for the error - local error_line = string.format("Error: %s", extraction_result.error) + -- The line for the error + local error_line = string.format("Error: %s", extraction_result.error) - -- If the extractor name exists - if extraction_result.extractor_name then - -- + -- If the extractor name exists + if extraction_result.extractor_name then + -- - -- Add the extractor's name to the error - error_line = string.format( - "%s error: %s", - extraction_result.extractor_name, - extraction_result.error - ) - end + -- Add the extractor's name to the error + error_line = string.format( + "%s error: %s", + extraction_result.extractor_name, + extraction_result.error + ) + end - -- Show the extraction error - return show_error(table.concat({ - string.format( - "Failed to extract archive at: %s", - extraction_result.archive_path - ), - string.format("Destination: %s", extraction_result.destination_path), - error_line, - }, "\n")) + -- Show the extraction error + return show_error(table.concat({ + string.format( + "Failed to extract archive at: %s", + extraction_result.archive_path + ), + string.format("Destination: %s", extraction_result.destination_path), + error_line, + }, "\n")) end -- Function to handle the open command ---@type CommandFunction local function handle_open(args, config) - -- + -- - -- Call the function to get the item group - local item_group = get_item_group() + -- Call the function to get the item group + local item_group = get_item_group() - -- If no item group is returned, exit the function - if not item_group then return end + -- If no item group is returned, exit the function + if not item_group then return end - -- If the item group is the selected items, - -- then execute the command and exit the function - if item_group == ItemGroup.Selected then - -- + -- If the item group is the selected items, + -- then execute the command and exit the function + if item_group == ItemGroup.Selected then + -- - -- Emit the command and exit the function - return ya.manager_emit("open", args) - end + -- Emit the command and exit the function + return ya.manager_emit("open", args) + end - -- If the hovered item is a directory - if hovered_item_is_dir() then - -- + -- If the hovered item is a directory + if hovered_item_is_dir() then + -- - -- If smart enter is wanted, - -- calls the function to enter the directory - -- and exit the function - if config.smart_enter or table_pop(args, "smart", false) then - return emit_plugin_command("enter", args) - end + -- If smart enter is wanted, + -- calls the function to enter the directory + -- and exit the function + if config.smart_enter or table_pop(args, "smart", false) then + return emit_plugin_command("enter", args) + end - -- Otherwise, just exit the function - return - end + -- Otherwise, just exit the function + return + end - -- Otherwise, if the hovered item is not an archive, - -- or entering archives isn't wanted, - -- or the interactive flag is passed - if - not hovered_item_is_archive() - or not config.enter_archives - or args.interactive - then - -- + -- Otherwise, if the hovered item is not an archive, + -- or entering archives isn't wanted, + -- or the interactive flag is passed + if + not hovered_item_is_archive() + or not config.enter_archives + or args.interactive + then + -- - -- Simply emit the open command, - -- opening only the hovered item - -- as the item group is the hovered item, - -- and exit the function - return ya.manager_emit("open", merge_tables(args, { hovered = true })) - end + -- Simply emit the open command, + -- opening only the hovered item + -- as the item group is the hovered item, + -- and exit the function + return ya.manager_emit("open", merge_tables(args, { hovered = true })) + end - -- Otherwise, the hovered item is an archive - -- and entering archives is wanted, - -- so get the path of the hovered item - local archive_path = get_path_of_hovered_item() + -- Otherwise, the hovered item is an archive + -- and entering archives is wanted, + -- so get the path of the hovered item + local archive_path = get_path_of_hovered_item() - -- If the archive path somehow doesn't exist, then exit the function - if not archive_path then return end + -- If the archive path somehow doesn't exist, then exit the function + if not archive_path then return end - -- Get the parent directory of the hovered item - ---@type Url - local parent_directory_url = Url(archive_path):parent() + -- Get the parent directory of the hovered item + ---@type Url + local parent_directory_url = Url(archive_path):parent() - -- If the parent directory doesn't exist, then exit the function - if not parent_directory_url then return end + -- If the parent directory doesn't exist, then exit the function + if not parent_directory_url then return end - -- Emit the command to extract the archive - -- and reveal the extracted items - emit_plugin_command( - "extract", - merge_tables(args, { - archive_path = ya.quote(archive_path), - reveal = true, - parent_dir = ya.quote(tostring(parent_directory_url)), - }) - ) + -- Emit the command to extract the archive + -- and reveal the extracted items + emit_plugin_command( + "extract", + merge_tables(args, { + archive_path = ya.quote(archive_path), + reveal = true, + parent_dir = ya.quote(tostring(parent_directory_url)), + }) + ) end -- Function to get the archive paths for the extract command ---@param args Arguments The arguments passed to the plugin ---@return string|string[]|nil archive_paths The archive paths local function get_archive_paths(args) - -- + -- - -- Get the archive path from the arguments given - local archive_path = table_pop(args, "archive_path") + -- Get the archive path from the arguments given + local archive_path = table_pop(args, "archive_path") - -- If the archive path is given, return it immediately - if archive_path then return archive_path end + -- If the archive path is given, return it immediately + if archive_path then return archive_path end - -- Otherwise, get the item group - local item_group = get_item_group() + -- Otherwise, get the item group + local item_group = get_item_group() - -- If there is no item group - if not item_group then return end + -- If there is no item group + if not item_group then return end - -- If the item group is the hovered item - if item_group == ItemGroup.Hovered then - -- + -- If the item group is the hovered item + if item_group == ItemGroup.Hovered then + -- - -- Get the hovered item path - local hovered_item_path = get_path_of_hovered_item(true) + -- Get the hovered item path + local hovered_item_path = get_path_of_hovered_item(true) - -- If the hovered item path is nil, exit the function - if not hovered_item_path then return end + -- If the hovered item path is nil, exit the function + if not hovered_item_path then return end - -- Otherwise, return the hovered item path - return hovered_item_path - end + -- Otherwise, return the hovered item path + return hovered_item_path + end - -- Otherwise, if the item group is the selected items - if item_group == ItemGroup.Selected then - -- + -- Otherwise, if the item group is the selected items + if item_group == ItemGroup.Selected then + -- - -- Get the list of selected items - local selected_items = get_paths_of_selected_items(true) + -- Get the list of selected items + local selected_items = get_paths_of_selected_items(true) - -- If there are no selected items, exit the function - if not selected_items then return end + -- If there are no selected items, exit the function + if not selected_items then return end - -- Otherwise, return the list of selected items - return selected_items - end + -- Otherwise, return the list of selected items + return selected_items + end end -- Function to handle the extract command ---@type CommandFunction local function handle_extract(args, config) - -- + -- - -- Get the archive paths - local archive_paths = get_archive_paths(args) + -- Get the archive paths + local archive_paths = get_archive_paths(args) - -- Get the destination path from the arguments given - ---@type string - local destination_path = table_pop(args, "destination_path") + -- Get the destination path from the arguments given + ---@type string + local destination_path = table_pop(args, "destination_path") - -- If there are no archive paths, exit the function - if not archive_paths then return end + -- If there are no archive paths, exit the function + if not archive_paths then return end - -- If the archive path is a list - if type(archive_paths) == "table" then - -- + -- If the archive path is a list + if type(archive_paths) == "table" then + -- - -- Iterate over the archive paths - -- and call the extract command on them - for _, archive_path in ipairs(archive_paths) do - emit_plugin_command( - "extract", - merge_tables(args, { - archive_path = ya.quote(archive_path), - }) - ) - end + -- Iterate over the archive paths + -- and call the extract command on them + for _, archive_path in ipairs(archive_paths) do + emit_plugin_command( + "extract", + merge_tables(args, { + archive_path = ya.quote(archive_path), + }) + ) + end - -- Exit the function - return - end + -- Exit the function + return + end - -- Otherwise the archive path is a string - ---@type string - local archive_path = archive_paths + -- Otherwise the archive path is a string + ---@type string + local archive_path = archive_paths - -- Call the function to recursively extract the archive - local extraction_result = recursively_extract_archive( - archive_path, - args, - config, - destination_path - ) + -- Call the function to recursively extract the archive + local extraction_result = recursively_extract_archive( + archive_path, + args, + config, + destination_path + ) - -- If the extraction is cancelled, then just exit the function - if extraction_result.cancelled then return end + -- If the extraction is cancelled, then just exit the function + if extraction_result.cancelled then return end - -- Get the extracted items path - local extracted_items_path = extraction_result.extracted_items_path + -- Get the extracted items path + local extracted_items_path = extraction_result.extracted_items_path - -- If the extraction is not successful, notify the user - if not extraction_result.successful or not extracted_items_path then - return show_extraction_error(extraction_result) - end + -- If the extraction is not successful, notify the user + if not extraction_result.successful or not extracted_items_path then + return show_extraction_error(extraction_result) + end - -- Get the url of the archive - local archive_url = Url(archive_path) + -- Get the url of the archive + local archive_url = Url(archive_path) - -- If the remove flag is passed, - -- then remove the archive after extraction - if table_pop(args, "remove", false) then fs.remove("file", archive_url) end + -- If the remove flag is passed, + -- then remove the archive after extraction + if table_pop(args, "remove", false) then fs.remove("file", archive_url) end - -- If the reveal flag is passed - if table_pop(args, "reveal", false) then - -- + -- If the reveal flag is passed + if table_pop(args, "reveal", false) then + -- - -- Get the url of the extracted items - ---@type Url - local extracted_items_url = Url(extracted_items_path) + -- Get the url of the extracted items + ---@type Url + local extracted_items_url = Url(extracted_items_path) - -- Get the parent directory of the extracted items - local parent_directory_url = extracted_items_url:parent() + -- Get the parent directory of the extracted items + local parent_directory_url = extracted_items_url:parent() - -- If the parent directory doesn't exist, then exit the function - if not parent_directory_url then return end + -- If the parent directory doesn't exist, then exit the function + if not parent_directory_url then return end - -- Get the given parent directory - local given_parent_directory = table_pop(args, "parent_dir") + -- Get the given parent directory + local given_parent_directory = table_pop(args, "parent_dir") - -- If there is a parent directory given but the parent directory - -- of the extracted items isn't the same as the given one, - -- exit the function - if - given_parent_directory - and given_parent_directory ~= tostring(parent_directory_url) - then - return - end + -- If there is a parent directory given but the parent directory + -- of the extracted items isn't the same as the given one, + -- exit the function + if + given_parent_directory + and given_parent_directory ~= tostring(parent_directory_url) + then + return + end - -- Get the cha of the extracted item - local extracted_items_cha = fs.cha(extracted_items_url, false) + -- Get the cha of the extracted item + local extracted_items_cha = fs.cha(extracted_items_url, false) - -- If the cha of the extracted item doesn't exist, - -- exit the function - if not extracted_items_cha then return end + -- If the cha of the extracted item doesn't exist, + -- exit the function + if not extracted_items_cha then return end - -- If the extracted item is not a directory - if not extracted_items_cha.is_dir then - -- + -- If the extracted item is not a directory + if not extracted_items_cha.is_dir then + -- - -- Reveal the item and exit the function - return ya.manager_emit("reveal", { extracted_items_url }) - end + -- Reveal the item and exit the function + return ya.manager_emit("reveal", { extracted_items_url }) + end - -- Otherwise, change the directory to the extracted item. - -- Note that extracted_items_url is destroyed here. - ya.manager_emit("cd", { extracted_items_url }) + -- Otherwise, change the directory to the extracted item. + -- Note that extracted_items_url is destroyed here. + ya.manager_emit("cd", { extracted_items_url }) - -- If the user wants to skip single subdirectories on enter, - -- and the no skip flag is not passed - if - config.skip_single_subdirectory_on_enter - and not table_pop(args, "no_skip", false) - then - -- + -- If the user wants to skip single subdirectories on enter, + -- and the no skip flag is not passed + if + config.skip_single_subdirectory_on_enter + and not table_pop(args, "no_skip", false) + then + -- - -- Call the function to skip child directories - skip_single_child_directories(extracted_items_path) - end - end + -- Call the function to skip child directories + skip_single_child_directories(extracted_items_path) + end + end end -- Function to handle the enter command ---@type CommandFunction local function handle_enter(args, config) - -- + -- - -- If the hovered item is not a directory - if not hovered_item_is_dir() then - -- + -- If the hovered item is not a directory + if not hovered_item_is_dir() then + -- - -- If smart enter is wanted, - -- call the function for the open command - -- and exit the function - if config.smart_enter or table_pop(args, "smart", false) then - return emit_plugin_command("open", args) - end + -- If smart enter is wanted, + -- call the function for the open command + -- and exit the function + if config.smart_enter or table_pop(args, "smart", false) then + return emit_plugin_command("open", args) + end - -- Otherwise, just exit the function - return - end + -- Otherwise, just exit the function + return + end - -- Otherwise, always emit the enter command, - ya.manager_emit("enter", args) + -- Otherwise, always emit the enter command, + ya.manager_emit("enter", args) - -- If the user doesn't want to skip single subdirectories on enter, - -- or one of the arguments passed is no skip, - -- then exit the function - if - not config.skip_single_subdirectory_on_enter - or table_pop(args, "no_skip", false) - then - return - end + -- If the user doesn't want to skip single subdirectories on enter, + -- or one of the arguments passed is no skip, + -- then exit the function + if + not config.skip_single_subdirectory_on_enter + or table_pop(args, "no_skip", false) + then + return + end - -- Otherwise, call the function to skip child directories - -- with only a single directory inside - skip_single_child_directories(get_current_directory()) + -- Otherwise, call the function to skip child directories + -- with only a single directory inside + skip_single_child_directories(get_current_directory()) end -- Function to handle the leave command ---@type CommandFunction local function handle_leave(args, config) - -- + -- - -- Always emit the leave command - ya.manager_emit("leave", args) + -- Always emit the leave command + ya.manager_emit("leave", args) - -- If the user doesn't want to skip single subdirectories on leave, - -- or one of the arguments passed is no skip, - -- then exit the function - if - not config.skip_single_subdirectory_on_leave - or table_pop(args, "no_skip", false) - then - return - end + -- If the user doesn't want to skip single subdirectories on leave, + -- or one of the arguments passed is no skip, + -- then exit the function + if + not config.skip_single_subdirectory_on_leave + or table_pop(args, "no_skip", false) + then + return + end - -- Otherwise, initialise the directory to the current directory - local directory = get_current_directory() + -- Otherwise, initialise the directory to the current directory + local directory = get_current_directory() - -- Get the tab preferences - local tab_preferences = get_tab_preferences() + -- Get the tab preferences + local tab_preferences = get_tab_preferences() - -- Start an infinite loop - while true do - -- + -- Start an infinite loop + while true do + -- - -- Get all the items in the current directory - local directory_items = - get_directory_items(directory, tab_preferences.show_hidden) + -- Get all the items in the current directory + local directory_items = + get_directory_items(directory, tab_preferences.show_hidden) - -- If the number of directory items is not 1, - -- then break out of the loop. - if #directory_items ~= 1 then break end + -- If the number of directory items is not 1, + -- then break out of the loop. + if #directory_items ~= 1 then break end - -- Get the parent directory of the current directory - ---@type Url|nil - local parent_directory = Url(directory):parent() + -- Get the parent directory of the current directory + ---@type Url|nil + local parent_directory = Url(directory):parent() - -- If the parent directory is nil, - -- break the loop - if not parent_directory then break end + -- If the parent directory is nil, + -- break the loop + if not parent_directory then break end - -- Otherwise, set the new directory to the parent directory - directory = tostring(parent_directory) - end + -- Otherwise, set the new directory to the parent directory + directory = tostring(parent_directory) + end - -- Emit the change directory command to change to the directory variable - ya.manager_emit("cd", { directory }) + -- Emit the change directory command to change to the directory variable + ya.manager_emit("cd", { directory }) end -- Function to handle a Yazi command @@ -2658,28 +2637,28 @@ end ---@param args Arguments The arguments passed to the plugin ---@return nil local function handle_yazi_command(command, args) - -- + -- - -- Call the function to get the item group - local item_group = get_item_group() + -- Call the function to get the item group + local item_group = get_item_group() - -- If no item group is returned, exit the function - if not item_group then return end + -- If no item group is returned, exit the function + if not item_group then return end - -- If the item group is the selected items - if item_group == ItemGroup.Selected then - -- + -- If the item group is the selected items + if item_group == ItemGroup.Selected then + -- - -- Emit the command to operate on the selected items - ya.manager_emit(command, args) + -- Emit the command to operate on the selected items + ya.manager_emit(command, args) - -- If the item group is the hovered item - elseif item_group == ItemGroup.Hovered then - -- + -- If the item group is the hovered item + elseif item_group == ItemGroup.Hovered then + -- - -- Emit the command with the hovered option - ya.manager_emit(command, merge_tables(args, { hovered = true })) - end + -- Emit the command with the hovered option + ya.manager_emit(command, merge_tables(args, { hovered = true })) + end end -- Function to enter or open the created file @@ -2689,45 +2668,45 @@ end ---@param config Configuration The configuration object ---@return nil local function enter_or_open_created_item(item_url, is_directory, args, config) - -- + -- - -- If the item is a directory - if is_directory then - -- + -- If the item is a directory + if is_directory then + -- - -- If user does not want to enter the directory - -- after creating it, exit the function - if - not ( - config.enter_directory_after_creation - or table_pop(args, "enter", false) - ) - then - return - end + -- If user does not want to enter the directory + -- after creating it, exit the function + if + not ( + config.enter_directory_after_creation + or table_pop(args, "enter", false) + ) + then + return + end - -- Otherwise, call the function change to the created directory - return ya.manager_emit("cd", { item_url }) - end + -- Otherwise, call the function change to the created directory + return ya.manager_emit("cd", { item_url }) + end - -- Otherwise, the item is a file + -- Otherwise, the item is a file - -- If the user does not want to open the file - -- after creating it, exit the function - if - not (config.open_file_after_creation or table_pop(args, "open", false)) - then - return - end + -- If the user does not want to open the file + -- after creating it, exit the function + if + not (config.open_file_after_creation or table_pop(args, "open", false)) + then + return + end - -- Otherwise, call the function to reveal the created file - ya.manager_emit("reveal", { item_url }) + -- Otherwise, call the function to reveal the created file + ya.manager_emit("reveal", { item_url }) - -- Wait for Yazi to finish loading - wait_until_yazi_is_loaded() + -- Wait for Yazi to finish loading + wait_until_yazi_is_loaded() - -- Call the function to open the file - return ya.manager_emit("open", { hovered = true }) + -- Call the function to open the file + return ya.manager_emit("open", { hovered = true }) end -- Function to execute the create command @@ -2736,141 +2715,138 @@ end ---@param config Configuration The configuration object ---@return nil local function execute_create(item_url, is_directory, args, config) - -- + -- - -- Get the parent directory of the file to create - local parent_directory_url = item_url:parent() + -- Get the parent directory of the file to create + local parent_directory_url = item_url:parent() - -- If the parent directory doesn't exist, - -- then show an error and exit the function - if not parent_directory_url then - return show_error( - "Parent directory of the item to create doesn't exist" - ) - end + -- If the parent directory doesn't exist, + -- then show an error and exit the function + if not parent_directory_url then + return show_error( + "Parent directory of the item to create doesn't exist" + ) + end - -- If the item to create is a directory - if is_directory then - -- + -- If the item to create is a directory + if is_directory then + -- - -- Call the function to create the directory - local successful, error_message = fs.create("dir_all", item_url) + -- Call the function to create the directory + local successful, error_message = fs.create("dir_all", item_url) - -- If the function is not successful, - -- show the error message and exit the function - if not successful then return show_error(tostring(error_message)) end + -- If the function is not successful, + -- show the error message and exit the function + if not successful then return show_error(tostring(error_message)) end - -- Otherwise, the item to create is a file - else - -- + -- Otherwise, the item to create is a file + else + -- - -- Otherwise, create the parent directory if it doesn't exist - if not fs.cha(parent_directory_url, false) then - -- + -- Otherwise, create the parent directory if it doesn't exist + if not fs.cha(parent_directory_url, false) then + -- - -- Call the function to create the parent directory - local successful, error_message = - fs.create("dir_all", parent_directory_url) + -- Call the function to create the parent directory + local successful, error_message = + fs.create("dir_all", parent_directory_url) - -- If the function is not successful, - -- show the error message and exit the function - if not successful then - return show_error(tostring(error_message)) - end - end + -- If the function is not successful, + -- show the error message and exit the function + if not successful then + return show_error(tostring(error_message)) + end + end - -- Otherwise, create the file - local successful, error_message = fs.write(item_url, "") + -- Otherwise, create the file + local successful, error_message = fs.write(item_url, "") - -- If the function is not successful, - -- show the error message and exit the function - if not successful then return show_error(tostring(error_message)) end - end + -- If the function is not successful, + -- show the error message and exit the function + if not successful then return show_error(tostring(error_message)) end + end - -- Call the function to enter or open the created item - enter_or_open_created_item(item_url, is_directory, args, config) + -- Call the function to enter or open the created item + enter_or_open_created_item(item_url, is_directory, args, config) end -- Function to handle the create command ---@type CommandFunction local function handle_create(args, config) - -- + -- - -- Get the directory flag - local dir_flag = table_pop(args, "dir", false) + -- Get the directory flag + local dir_flag = table_pop(args, "dir", false) - -- Get the user's input for the item to create - local user_input, event = - get_user_input(dir_flag and "Create (dir):" or "Create:") + -- Get the user's input for the item to create + local user_input, event = + get_user_input(dir_flag and "Create (dir):" or "Create:") - -- If the user input is nil, - -- or if the user did not confirm the input, - -- exit the function - if not user_input or event ~= 1 then return end + -- If the user input is nil, + -- or if the user did not confirm the input, + -- exit the function + if not user_input or event ~= 1 then return end - -- Get the current working directory as a url - ---@type Url - local current_working_directory = Url(get_current_directory()) + -- Get the current working directory as a url + ---@type Url + local current_working_directory = Url(get_current_directory()) - -- Get whether the url ends with a path delimiter - local ends_with_path_delimiter = user_input:find("[/\\]$") + -- Get whether the url ends with a path delimiter + local ends_with_path_delimiter = user_input:find("[/\\]$") - -- Get the whether the given item is a directory or not based - -- on the default conditions for a directory - local is_directory = ends_with_path_delimiter or dir_flag + -- Get the whether the given item is a directory or not based + -- on the default conditions for a directory + local is_directory = ends_with_path_delimiter or dir_flag - -- Get the url from the user's input - ---@type Url - local item_url = Url(user_input) + -- Get the url from the user's input + ---@type Url + local item_url = Url(user_input) - -- If the user does not want to use the default Yazi create behaviour - if - not ( - config.use_default_create_behaviour - or table_pop(args, "default_behaviour", false) - ) - then - -- + -- If the user does not want to use the default Yazi create behaviour + if + not ( + config.use_default_create_behaviour + or table_pop(args, "default_behaviour", false) + ) + then + -- - -- Get the file extension from the user's input - local file_extension = user_input:match(file_extension_pattern) + -- Get the file extension from the user's input + local file_extension = user_input:match(file_extension_pattern) - -- Set the is directory variable to the is directory condition - -- or if the file extension exists - is_directory = is_directory or not file_extension - end + -- Set the is directory variable to the is directory condition + -- or if the file extension exists + is_directory = is_directory or not file_extension + end - -- Get the full url of the item to create - local full_url = current_working_directory:join(item_url) + -- Get the full url of the item to create + local full_url = current_working_directory:join(item_url) - -- If the path to the item to create already exists, - -- and the user did not pass the force flag - if fs.cha(full_url, false) and not table_pop(args, "force", false) then - -- + -- If the path to the item to create already exists, + -- and the user did not pass the force flag + if fs.cha(full_url, false) and not table_pop(args, "force", false) then + -- - -- Get the user's confirmation for - -- whether they want to overwrite the item - local user_confirmation = get_user_confirmation( + -- Get the user's confirmation for + -- whether they want to overwrite the item + local user_confirmation = get_user_confirmation( + "Overwrite file?", + ui.Text({ + ui.Line("Will overwrite the following file:") + :align(ui.Line.CENTER), + ui.Line(string.rep("─", DEFAULT_CONFIRM_OPTIONS.pos.w - 2)) + :align(ui.Line.LEFT), + ui.Line(tostring(full_url)):align(ui.Line.LEFT), + }):wrap(ui.Text.WRAP_TRIM) + ) - -- TODO: Remove the line below - "The item already exists, overwrite? (y/N)", - "Overwrite file?", - ui.Text({ - ui.Line("Will overwrite the following file:") - :align(ui.Line.CENTER), - ui.Line(string.rep("─", DEFAULT_CONFIRM_OPTIONS.pos.w - 2)) - :align(ui.Line.LEFT), - ui.Line(tostring(full_url)):align(ui.Line.LEFT), - }):wrap(ui.Text.WRAP_TRIM) - ) + -- If the user did not confirm the overwrite, + -- then exit the function + if not user_confirmation then return end + end - -- If the user did not confirm the overwrite, - -- then exit the function - if not user_confirmation then return end - end - - -- Call the function to execute the create command - return execute_create(full_url, is_directory, args, config) + -- Call the function to execute the create command + return execute_create(full_url, is_directory, args, config) end -- Function to remove the F flag from the less command @@ -2878,33 +2854,33 @@ end ---@return string command The command with the F flag removed ---@return boolean f_flag_found Whether the F flag was found local function remove_f_flag_from_less_command(command) - -- + -- - -- Initialise the variable to store if the F flag is found - local f_flag_found = false + -- Initialise the variable to store if the F flag is found + local f_flag_found = false - -- Initialise the variable to store the replacement count - local replacement_count = 0 + -- Initialise the variable to store the replacement count + local replacement_count = 0 - -- Remove the F flag when it is passed at the start - -- of the flags given to the less command - command, replacement_count = command:gsub("(%f[%a]less%f[%A].*)%-F", "%1") + -- Remove the F flag when it is passed at the start + -- of the flags given to the less command + command, replacement_count = command:gsub("(%f[%a]less%f[%A].*)%-F", "%1") - -- If the replacement count is not 0, - -- set the f_flag_found variable to true - if replacement_count ~= 0 then f_flag_found = true end + -- If the replacement count is not 0, + -- set the f_flag_found variable to true + if replacement_count ~= 0 then f_flag_found = true end - -- Remove the F flag when it is passed in the middle - -- or end of the flags given to the less command command - command, replacement_count = - command:gsub("(%f[%a]less%f[%A].*%-)(%a*)F(%a*)", "%1%2%3") + -- Remove the F flag when it is passed in the middle + -- or end of the flags given to the less command command + command, replacement_count = + command:gsub("(%f[%a]less%f[%A].*%-)(%a*)F(%a*)", "%1%2%3") - -- If the replacement count is not 0, - -- set the f_flag_found variable to true - if replacement_count ~= 0 then f_flag_found = true end + -- If the replacement count is not 0, + -- set the f_flag_found variable to true + if replacement_count ~= 0 then f_flag_found = true end - -- Return the command and whether or not the F flag was found - return command, f_flag_found + -- Return the command and whether or not the F flag was found + return command, f_flag_found end -- Function to fix a command containing less. @@ -2913,621 +2889,682 @@ end ---@param command string The shell command containing the less command ---@return string command The fixed shell command local function fix_shell_command_containing_less(command) - -- + -- - -- Remove the F flag from the given command - local fixed_command = remove_f_flag_from_less_command(command) + -- Remove the F flag from the given command + local fixed_command = remove_f_flag_from_less_command(command) - -- Get the LESS environment variable - local less_environment_variable = os.getenv("LESS") + -- Get the LESS environment variable + local less_environment_variable = os.getenv("LESS") - -- If the LESS environment variable is not set, - -- then return the given command with the F flag removed - if not less_environment_variable then return fixed_command end + -- If the LESS environment variable is not set, + -- then return the given command with the F flag removed + if not less_environment_variable then return fixed_command end - -- Otherwise, remove the F flag from the LESS environment variable - -- and check if the F flag was found - local less_command_with_modified_env_variables, f_flag_found = - remove_f_flag_from_less_command("less " .. less_environment_variable) + -- Otherwise, remove the F flag from the LESS environment variable + -- and check if the F flag was found + local less_command_with_modified_env_variables, f_flag_found = + remove_f_flag_from_less_command("less " .. less_environment_variable) - -- If the F flag isn't found, - -- then return the given command with the F flag removed - if not f_flag_found then return fixed_command end + -- If the F flag isn't found, + -- then return the given command with the F flag removed + if not f_flag_found then return fixed_command end - -- Add the less environment variable flags to the less command - fixed_command = fixed_command:gsub( - "%f[%a]less%f[%A]", - escape_replacement_string(less_command_with_modified_env_variables) - ) + -- Add the less environment variable flags to the less command + fixed_command = fixed_command:gsub( + "%f[%a]less%f[%A]", + escape_replacement_string(less_command_with_modified_env_variables) + ) - -- Unset the LESS environment variable before calling the command - fixed_command = "unset LESS; " .. fixed_command + -- Unset the LESS environment variable before calling the command + fixed_command = "unset LESS; " .. fixed_command - -- Return the fixed command - return fixed_command + -- Return the fixed command + return fixed_command end -- Function to fix the bat default pager command ---@param command string The command containing the bat default pager command ---@return string command The fixed bat command -local function fix_bat_default_pager_shell_command(command) - -- +local function fix_shell_command_containing_bat(command) + -- - -- Initialise the default pager command for bat without the F flag - local bat_default_pager_command_without_f_flag = "less -RX" + -- The pattern to match the pager argument for the bat command + local bat_pager_pattern = "(%-%-pager)%s+(%S+)" - -- Get the modified command and the replacement count - -- when replacing the less command when it is quoted - local modified_command, replacement_count = command:gsub( - "(" - .. bat_command_with_pager_pattern - .. "['\"]+%s*" - .. ")" - .. "less" - .. "(%s*['\"]+)", - "%1" .. bat_default_pager_command_without_f_flag .. "%2" - ) + -- Get the pager argument for the bat command + local _, pager_argument = command:match(bat_pager_pattern) - -- If the replacement count is not 0, - -- then return the modified command - if replacement_count ~= 0 then return modified_command end + -- If there is a pager argument + -- + -- We don't need to do much if the pager argument already exists, + -- as we can rely on the function that fixes the less command to + -- remove the -F flag that is executed after this function is called. + -- + -- There's only work to be done if the pager argument isn't quoted, + -- as we need to quote it so the function that fixes the less command + -- can execute cleanly without causing shell syntax errors. + -- + -- The reason why we don't quote the less command in the function + -- to fix the less command is to not deal with using backslashes + -- to escape the quotes, which can get really messy and really confusing, + -- so we just naively replace the less command with the fixed version + -- without caring about whether the less command is passed as an + -- argument, or is called as a shell command. + if pager_argument then + -- - -- Otherwise, get the modified command and the replacement count - -- when replacing the less command when it is unquoted - modified_command, replacement_count = command:gsub( - "(" .. bat_command_with_pager_pattern .. ")" .. "less", - '%1"' .. bat_default_pager_command_without_f_flag .. '"' - ) + -- If the pager argument is quoted, return the command immediately + if pager_argument:find("['\"].+['\"]") then + return command + end - -- If the replacement count is not 0, - -- then return the modified command - if replacement_count ~= 0 then return modified_command end + -- Otherwise, quote the pager argument with single quotes + -- + -- It should be fine to quote with single quotes + -- as the user passing the argument probably isn't + -- using a shell variable, as they would have quoted + -- the shell variable in double quotes instead of + -- omitting the quotes. + pager_argument = string.format("'%s'", pager_argument) - -- Otherwise, return the given command - return command + -- Replace the pager argument with the quoted version + local modified_command = + command:gsub(bat_pager_pattern, "%1 " .. pager_argument) + + -- Return the modified command + return modified_command + end + + -- If there is no pager argument, + -- initialise the default pager command for bat without the F flag + local bat_default_pager_command_without_f_flag = "less -RX" + + -- Replace the bat command with the command to use the + -- bat default pager command without the F flag + local modified_command = command:gsub( + bat_command_pattern, + string.format( + "bat --pager '%s'", + bat_default_pager_command_without_f_flag + ), + 1 + ) + + -- Return the modified command + return modified_command end -- Function to fix the shell commands given to work properly with Yazi ---@param command string A shell command ---@return string command The fixed shell command local function fix_shell_command(command) - -- + -- - -- If the given command includes the less command - if command:find("%f[%a]less%f[%A]") ~= nil then - -- + -- If the given command contains the bat command + if command:find(bat_command_pattern) ~= nil then + -- - -- Fix the command containing less - command = fix_shell_command_containing_less(command) - end + -- Calls the command to fix the bat command + command = fix_shell_command_containing_bat(command) + end - -- If the given command contains the bat command with the pager - -- option passed - if command:find(bat_command_with_pager_pattern) ~= nil then - -- + -- If the given command includes the less command + if command:find("%f[%a]less%f[%A]") ~= nil then + -- - -- Calls the command to fix the bat command with the default pager - command = fix_bat_default_pager_shell_command(command) - end + -- Fix the command containing less + command = fix_shell_command_containing_less(command) + end - -- Return the modified command - return command + -- Return the modified command + return command end -- Function to handle a shell command ---@type CommandFunction local function handle_shell(args, _) - -- + -- - -- Get the first item of the arguments given - -- and set it to the command variable - local command = table.remove(args, 1) + -- Get the first item of the arguments given + -- and set it to the command variable + local command = table.remove(args, 1) - -- Get the type of the command variable - local command_type = type(command) + -- Get the type of the command variable + local command_type = type(command) - -- If the command isn't a string, - -- show an error message and exit the function - if command_type ~= "string" then - return show_error( - string.format( - "Shell command given is not a string, " - .. "instead it is a '%s', " - .. "with value '%s'", - command_type, - tostring(command) - ) - ) - end + -- If the command isn't a string, + -- show an error message and exit the function + if command_type ~= "string" then + return show_error( + string.format( + "Shell command given is not a string, " + .. "instead it is a '%s', " + .. "with value '%s'", + command_type, + tostring(command) + ) + ) + end - -- Fix the given command - command = fix_shell_command(command) + -- Fix the given command + command = fix_shell_command(command) - -- Call the function to get the item group - local item_group = get_item_group() + -- Call the function to get the item group + local item_group = get_item_group() - -- If no item group is returned, exit the function - if not item_group then return end + -- If no item group is returned, exit the function + if not item_group then return end - -- Get whether the exit if directory flag is passed - local exit_if_dir = table_pop(args, "exit_if_dir", false) + -- Get whether the exit if directory flag is passed + local exit_if_dir = table_pop(args, "exit_if_dir", false) - -- If the item group is the selected items - if item_group == ItemGroup.Selected then - -- + -- If the item group is the selected items + if item_group == ItemGroup.Selected then + -- - -- Get the paths of the selected items - local selected_items = get_paths_of_selected_items(true) + -- Get the paths of the selected items + local selected_items = get_paths_of_selected_items(true) - -- If there are no selected items, exit the function - if not selected_items then return end + -- If there are no selected items, exit the function + if not selected_items then return end - -- If the exit if directory flag is passed - if exit_if_dir then - -- + -- If the exit if directory flag is passed + if exit_if_dir then + -- - -- Initialise the number of files - local number_of_files = 0 + -- Initialise the number of files + local number_of_files = 0 - -- Iterate over all of the selected items - for _, item in pairs(selected_items) do - -- + -- Iterate over all of the selected items + for _, item in pairs(selected_items) do + -- - -- Get the cha object of the item - local item_cha = fs.cha(Url(item), false) + -- Get the cha object of the item + local item_cha = fs.cha(Url(item), false) - -- If the item isn't a directory - if not (item_cha or {}).is_dir then - -- + -- If the item isn't a directory + if not (item_cha or {}).is_dir then + -- - -- Increment the number of files - number_of_files = number_of_files + 1 - end - end + -- Increment the number of files + number_of_files = number_of_files + 1 + end + end - -- If the number of files is 0, then exit the function - if number_of_files == 0 then return end - end + -- If the number of files is 0, then exit the function + if number_of_files == 0 then return end + end - -- Replace the shell variable in the command - -- with the quoted paths of the selected items - command = command:gsub( - shell_variable_pattern, - escape_replacement_string(table.concat(selected_items, " ")) - ) + -- Replace the shell variable in the command + -- with the quoted paths of the selected items + command = command:gsub( + shell_variable_pattern, + escape_replacement_string(table.concat(selected_items, " ")) + ) - -- If the item group is the hovered item - elseif item_group == ItemGroup.Hovered then - -- + -- If the item group is the hovered item + elseif item_group == ItemGroup.Hovered then + -- - -- Get the hovered item path - local hovered_item_path = get_path_of_hovered_item(true) + -- Get the hovered item path + local hovered_item_path = get_path_of_hovered_item(true) - -- If the hovered item path is nil, exit the function - if not hovered_item_path then return end + -- If the hovered item path is nil, exit the function + if not hovered_item_path then return end - -- If the exit if directory flag is passed, - -- and the hovered item is a directory, - -- then exit the function - if exit_if_dir and hovered_item_is_dir() then return end + -- If the exit if directory flag is passed, + -- and the hovered item is a directory, + -- then exit the function + if exit_if_dir and hovered_item_is_dir() then return end - -- Replace the shell variable in the command - -- with the quoted path of the hovered item - command = command:gsub( - shell_variable_pattern, - escape_replacement_string(hovered_item_path) - ) + -- Replace the shell variable in the command + -- with the quoted path of the hovered item + command = command:gsub( + shell_variable_pattern, + escape_replacement_string(hovered_item_path) + ) - -- Otherwise, exit the function - else - return - end + -- Otherwise, exit the function + else + return + end - -- Merge the command back into the arguments given - args = merge_tables({ command }, args) + -- Merge the command back into the arguments given + args = merge_tables({ command }, args) - -- Emit the command to operate on the hovered item - ya.manager_emit("shell", args) + -- Emit the command to operate on the hovered item + ya.manager_emit("shell", args) end -- Function to handle the paste command ---@type CommandFunction local function handle_paste(args, config) - -- + -- - -- If the hovered item is not a directory or smart paste is not wanted - if - not hovered_item_is_dir() - or not (config.smart_paste or table_pop(args, "smart", false)) - then - -- + -- If the hovered item is not a directory or smart paste is not wanted + if + not hovered_item_is_dir() + or not (config.smart_paste or table_pop(args, "smart", false)) + then + -- - -- Just paste the items inside the current directory - -- and exit the function - return ya.manager_emit("paste", args) - end + -- Just paste the items inside the current directory + -- and exit the function + return ya.manager_emit("paste", args) + end - -- Otherwise, enter the directory - ya.manager_emit("enter", {}) + -- Otherwise, enter the directory + ya.manager_emit("enter", {}) - -- Paste the items inside the directory - ya.manager_emit("paste", args) + -- Paste the items inside the directory + ya.manager_emit("paste", args) - -- Leave the directory - ya.manager_emit("leave", {}) + -- Leave the directory + ya.manager_emit("leave", {}) end -- Function to execute the tab create command ---@type fun( ---- args: Arguments, -- The arguments passed to the plugin +--- args: Arguments, -- The arguments passed to the plugin ---): nil local execute_tab_create = ya.sync(function(state, args) - -- + -- - -- Get the hovered item - local hovered_item = cx.active.current.hovered + -- Get the hovered item + local hovered_item = cx.active.current.hovered - -- If the hovered item is nil, - -- or if the hovered item is not a directory, - -- or if the user doesn't want to smartly - -- create a tab in the hovered directory - if - not hovered_item - or not hovered_item.cha.is_dir - or not ( - state.config.smart_tab_create - or table_pop(args, "smart", false) - ) - then - -- + -- If the hovered item is nil, + -- or if the hovered item is not a directory, + -- or if the user doesn't want to smartly + -- create a tab in the hovered directory + if + not hovered_item + or not hovered_item.cha.is_dir + or not ( + state.config.smart_tab_create or table_pop(args, "smart", false) + ) + then + -- - -- Emit the command to create a new tab with the arguments - -- and exit the function - return ya.manager_emit("tab_create", args) - end + -- Emit the command to create a new tab with the arguments + -- and exit the function + return ya.manager_emit("tab_create", args) + end - -- Otherwise, emit the command to create a new tab - -- with the hovered item's url - ya.manager_emit("tab_create", { hovered_item.url }) + -- Otherwise, emit the command to create a new tab + -- with the hovered item's url + ya.manager_emit("tab_create", { hovered_item.url }) end) -- Function to handle the tab create command ---@type CommandFunction local function handle_tab_create(args) - -- + -- - -- Call the function to execute the tab create command - execute_tab_create(args) + -- Call the function to execute the tab create command + execute_tab_create(args) end -- Function to execute the tab switch command ---@type fun( ---- args: Arguments, -- The arguments passed to the plugin +--- args: Arguments, -- The arguments passed to the plugin ---): nil local execute_tab_switch = ya.sync(function(state, args) - -- + -- - -- Get the tab index - local tab_index = args[1] + -- Get the tab index + local tab_index = args[1] - -- If no tab index is given, exit the function - if not tab_index then return end + -- If no tab index is given, exit the function + if not tab_index then return end - -- If the user doesn't want to create tabs - -- when switching to a new tab, - -- or the tab index is not given, - -- then just call the tab switch command - -- and exit the function - if - not (state.config.smart_tab_switch or table_pop(args, "smart", false)) - then - return ya.manager_emit("tab_switch", args) - end + -- If the user doesn't want to create tabs + -- when switching to a new tab, + -- or the tab index is not given, + -- then just call the tab switch command + -- and exit the function + if + not (state.config.smart_tab_switch or table_pop(args, "smart", false)) + then + return ya.manager_emit("tab_switch", args) + end - -- Get the current tab - local current_tab = cx.active.current + -- Get the current tab + local current_tab = cx.active.current - -- Get the number of tabs currently open - local number_of_open_tabs = #cx.tabs + -- Get the number of tabs currently open + local number_of_open_tabs = #cx.tabs - -- Iterate from the number of current open tabs - -- to the given tab number - for _ = number_of_open_tabs, tab_index do - -- + -- Iterate from the number of current open tabs + -- to the given tab number + for _ = number_of_open_tabs, tab_index do + -- - -- Call the tab create command - ya.manager_emit("tab_create", { current_tab.cwd }) + -- Call the tab create command + ya.manager_emit("tab_create", { current_tab.cwd }) - -- If there is a hovered item - if current_tab.hovered then - -- + -- If there is a hovered item + if current_tab.hovered then + -- - -- Reveal the hovered item - ya.manager_emit("reveal", { current_tab.hovered.url }) - end - end + -- Reveal the hovered item + ya.manager_emit("reveal", { current_tab.hovered.url }) + end + end - -- Switch to the given tab index - ya.manager_emit("tab_switch", args) + -- Switch to the given tab index + ya.manager_emit("tab_switch", args) end) -- Function to handle the tab switch command ---@type CommandFunction local function handle_tab_switch(args) - -- + -- - -- Call the function to execute the tab switch command - execute_tab_switch(args) + -- Call the function to execute the tab switch command + execute_tab_switch(args) +end + +-- Function to execute the quit command +---@type CommandFunction +local function handle_quit(args, config) + -- + + -- Get the number of tabs + local number_of_tabs = get_number_of_tabs() + + -- If the user doesn't want the confirm on quit functionality, + -- or if the number of tabs is 1 or less, + -- then emit the quit command + if not (config.confirm_on_quit or args.confirm) or number_of_tabs <= 1 then + return ya.manager_emit("quit", args) + end + + -- Otherwise, get the user's confirmation for quitting + local user_confirmation = get_user_confirmation( + "Quit?", + ui.Text({ + "There are multiple tabs open.", + "Are you sure you want to quit?", + }):wrap(ui.Text.WRAP_TRIM) + ) + + -- If the user didn't confirm, then exit the function + if not user_confirmation then return end + + -- Otherwise, emit the quit command + ya.manager_emit("quit", args) end -- Function to do the wraparound for the arrow command ---@type fun( ---- args: Arguments, -- The arguments passed to the plugin +--- args: Arguments, -- The arguments passed to the plugin ---): nil local wraparound_arrow = ya.sync(function(_, args) - -- + -- - -- Get the current tab - local current_tab = cx.active.current + -- Get the current tab + local current_tab = cx.active.current - -- Get the step from the arguments given - local step = table.remove(args, 1) + -- Get the step from the arguments given + local step = table.remove(args, 1) - -- Get the number of files in the current tab - local number_of_files = #current_tab.files + -- Get the number of files in the current tab + local number_of_files = #current_tab.files - -- If there are no files in the current tab, exit the function - if number_of_files == 0 then return end + -- If there are no files in the current tab, exit the function + if number_of_files == 0 then return end - -- Get the new cursor index, - -- which is the current cursor position plus the step given - -- to the arrow function, modulus the number of files in - -- the current tab - local new_cursor_index = (current_tab.cursor + step) % number_of_files + -- Get the new cursor index, + -- which is the current cursor position plus the step given + -- to the arrow function, modulus the number of files in + -- the current tab + local new_cursor_index = (current_tab.cursor + step) % number_of_files - -- Emit the arrow function with the new cursor index minus - -- the current cursor index to determine how to move the cursor - ya.manager_emit( - "arrow", - merge_tables(args, { new_cursor_index - current_tab.cursor }) - ) + -- Emit the arrow function with the new cursor index minus + -- the current cursor index to determine how to move the cursor + ya.manager_emit( + "arrow", + merge_tables(args, { new_cursor_index - current_tab.cursor }) + ) end) -- Function to handle the arrow command ---@type CommandFunction local function handle_arrow(args, config) - -- + -- - -- If wraparound file navigation isn't wanted, - -- then execute the arrow command - if not config.wraparound_file_navigation then - ya.manager_emit("arrow", args) + -- If wraparound file navigation isn't wanted, + -- then execute the arrow command + if not config.wraparound_file_navigation then + ya.manager_emit("arrow", args) - -- Otherwise, call the wraparound arrow function - else - wraparound_arrow(args) - end + -- Otherwise, call the wraparound arrow function + else + wraparound_arrow(args) + end end -- Function to get the directory items in the parent directory ---@type fun( ---- directories_only: boolean, -- Whether to only get directories +--- directories_only: boolean, -- Whether to only get directories ---): string[] directory_items The list of paths to the directory items local get_parent_directory_items = ya.sync(function(_, directories_only) - -- + -- - -- Initialise the list of directory items - local directory_items = {} + -- Initialise the list of directory items + local directory_items = {} - -- Get the parent directory - local parent_directory = cx.active.parent + -- Get the parent directory + local parent_directory = cx.active.parent - -- If the parent directory doesn't exist, - -- return the empty list of directory items - if not parent_directory then return directory_items end + -- If the parent directory doesn't exist, + -- return the empty list of directory items + if not parent_directory then return directory_items end - -- Otherwise, iterate over the items in the parent directory - for _, item in ipairs(parent_directory.files) do - -- + -- Otherwise, iterate over the items in the parent directory + for _, item in ipairs(parent_directory.files) do + -- - -- If the directories only flag is passed, - -- and the item is not a directory, - -- then skip the item - if directories_only and not item.cha.is_dir then goto continue end + -- If the directories only flag is passed, + -- and the item is not a directory, + -- then skip the item + if directories_only and not item.cha.is_dir then goto continue end - -- Otherwise, add the item to the list of directory items - table.insert(directory_items, item) + -- Otherwise, add the item to the list of directory items + table.insert(directory_items, item) - -- The continue label to skip the item - ::continue:: - end + -- The continue label to skip the item + ::continue:: + end - -- Return the list of directory items - return directory_items + -- Return the list of directory items + return directory_items end) -- Function to execute the parent arrow command ---@type fun( ---- args: Arguments, -- The arguments passed to the plugin +--- args: Arguments, -- The arguments passed to the plugin ---): nil local execute_parent_arrow = ya.sync(function(state, args) - -- + -- - -- Gets the parent directory - local parent_directory = cx.active.parent + -- Gets the parent directory + local parent_directory = cx.active.parent - -- If the parent directory doesn't exist, - -- then exit the function - if not parent_directory then return end + -- If the parent directory doesn't exist, + -- then exit the function + if not parent_directory then return end - -- Get the offset from the arguments given - local offset = table.remove(args, 1) + -- Get the offset from the arguments given + local offset = table.remove(args, 1) - -- Get the type of the offset - local offset_type = type(offset) + -- Get the type of the offset + local offset_type = type(offset) - -- If the offset is not a number, - -- then show an error that the offset is not a number - -- and exit the function - if offset_type ~= "number" then - return show_error( - string.format( - "The given offset is not of the type 'number', " - .. "instead it is a '%s', " - .. "with value '%s'", - offset_type, - tostring(offset) - ) - ) - end + -- If the offset is not a number, + -- then show an error that the offset is not a number + -- and exit the function + if offset_type ~= "number" then + return show_error( + string.format( + "The given offset is not of the type 'number', " + .. "instead it is a '%s', " + .. "with value '%s'", + offset_type, + tostring(offset) + ) + ) + end - -- Get the number of items in the parent directory - local number_of_items = #parent_directory.files + -- Get the number of items in the parent directory + local number_of_items = #parent_directory.files - -- Initialise the new cursor index - -- to the current cursor index - local new_cursor_index = parent_directory.cursor + -- Initialise the new cursor index + -- to the current cursor index + local new_cursor_index = parent_directory.cursor - -- Get whether the user wants to sort directories first - local sort_directories_first = cx.active.pref.sort_dir_first + -- Get whether the user wants to sort directories first + local sort_directories_first = cx.active.pref.sort_dir_first - -- If wraparound file navigation is wanted - if state.config.wraparound_file_navigation then - -- + -- If wraparound file navigation is wanted + if state.config.wraparound_file_navigation then + -- - -- If the user sorts their directories first - if sort_directories_first then - -- + -- If the user sorts their directories first + if sort_directories_first then + -- - -- Get the directories in the parent directory - local directories = get_parent_directory_items(true) + -- Get the directories in the parent directory + local directories = get_parent_directory_items(true) - -- Get the number of directories in the parent directory - local number_of_directories = #directories + -- Get the number of directories in the parent directory + local number_of_directories = #directories - -- If the number of directories is 0, then exit the function - if number_of_directories == 0 then return end + -- If the number of directories is 0, then exit the function + if number_of_directories == 0 then return end - -- Get the new cursor index by adding the offset, - -- and modding the whole thing by the number of directories - new_cursor_index = (parent_directory.cursor + offset) - % number_of_directories + -- Get the new cursor index by adding the offset, + -- and modding the whole thing by the number of directories + new_cursor_index = (parent_directory.cursor + offset) + % number_of_directories - -- Otherwise, if the user doesn't sort their directories first - else - -- + -- Otherwise, if the user doesn't sort their directories first + else + -- - -- Get the new cursor index by adding the offset, - -- and modding the whole thing by the number of - -- items in the parent directory - new_cursor_index = (parent_directory.cursor + offset) - % number_of_items - end + -- Get the new cursor index by adding the offset, + -- and modding the whole thing by the number of + -- items in the parent directory + new_cursor_index = (parent_directory.cursor + offset) + % number_of_items + end - -- Otherwise, get the new cursor index normally - -- by adding the offset to the cursor index - else - new_cursor_index = parent_directory.cursor + offset - end + -- Otherwise, get the new cursor index normally + -- by adding the offset to the cursor index + else + new_cursor_index = parent_directory.cursor + offset + end - -- Increment the cursor index by 1. - -- The cursor index needs to be increased by 1 - -- as the cursor index is 0-based, while Lua - -- tables are 1-based. - new_cursor_index = new_cursor_index + 1 + -- Increment the cursor index by 1. + -- The cursor index needs to be increased by 1 + -- as the cursor index is 0-based, while Lua + -- tables are 1-based. + new_cursor_index = new_cursor_index + 1 - -- Get the starting index of the loop - local start_index = new_cursor_index + -- Get the starting index of the loop + local start_index = new_cursor_index - -- Get the ending index of the loop. - -- - -- If the offset given is negative, set the end index to 1, - -- as the loop will iterate backwards. - -- Otherwise, if the step given is positive, - -- set the end index to the number of items in the - -- parent directory. - local end_index = offset < 0 and 1 or number_of_items + -- Get the ending index of the loop. + -- + -- If the offset given is negative, set the end index to 1, + -- as the loop will iterate backwards. + -- Otherwise, if the step given is positive, + -- set the end index to the number of items in the + -- parent directory. + local end_index = offset < 0 and 1 or number_of_items - -- Get the step for the loop. - -- - -- If the offset given is negative, set the step to -1, - -- as the loop will iterate backwards. - -- Otherwise, if the step given is positive, set - -- the step to 1 to iterate forwards. - local step = offset < 0 and -1 or 1 + -- Get the step for the loop. + -- + -- If the offset given is negative, set the step to -1, + -- as the loop will iterate backwards. + -- Otherwise, if the step given is positive, set + -- the step to 1 to iterate forwards. + local step = offset < 0 and -1 or 1 - -- Iterate over the parent directory items - for i = start_index, end_index, step do - -- + -- Iterate over the parent directory items + for i = start_index, end_index, step do + -- - -- Get the directory item - local directory_item = parent_directory.files[i] + -- Get the directory item + local directory_item = parent_directory.files[i] - -- If the directory item exists and is a directory - if directory_item and directory_item.cha.is_dir then - -- + -- If the directory item exists and is a directory + if directory_item and directory_item.cha.is_dir then + -- - -- Emit the command to change directory to - -- the directory item and exit the function - return ya.manager_emit("cd", { directory_item.url }) - end - end + -- Emit the command to change directory to + -- the directory item and exit the function + return ya.manager_emit("cd", { directory_item.url }) + end + end end) -- Function to handle the parent arrow command ---@type CommandFunction local function handle_parent_arrow(args) - -- + -- - -- Call the function to execute the parent arrow command - -- with the arguments given - execute_parent_arrow(args) + -- Call the function to execute the parent arrow command + -- with the arguments given + execute_parent_arrow(args) end -- Function to handle the editor command ---@type CommandFunction local function handle_editor(args, config) - -- + -- - -- Get the editor environment variable - local editor = os.getenv("EDITOR") + -- Get the editor environment variable + local editor = os.getenv("EDITOR") - -- If the editor not set, exit the function - if not editor then return end + -- If the editor not set, exit the function + if not editor then return end - -- Call the handle shell function - -- with the editor command - handle_shell( - merge_tables({ - editor .. " $@", - block = true, - exit_if_dir = true, - }, args), - config - ) + -- Call the handle shell function + -- with the editor command + handle_shell( + merge_tables({ + editor .. " $@", + block = true, + exit_if_dir = true, + }, args), + config + ) end -- Function to handle the pager command ---@type CommandFunction local function handle_pager(args, config) - -- + -- - -- Get the pager environment variable - local pager = os.getenv("PAGER") + -- Get the pager environment variable + local pager = os.getenv("PAGER") - -- If the pager is not set, exit the function - if not pager then return end + -- If the pager is not set, exit the function + if not pager then return end - -- Call the handle shell function - -- with the pager command - handle_shell( - merge_tables({ - pager .. " $@", - block = true, - exit_if_dir = true, - }, args), - config - ) + -- Call the handle shell function + -- with the pager command + handle_shell( + merge_tables({ + pager .. " $@", + block = true, + exit_if_dir = true, + }, args), + config + ) end -- Function to run the commands given @@ -3536,39 +3573,40 @@ end ---@param config Configuration The configuration object ---@return nil local function run_command_func(command, args, config) - -- + -- - -- The command table - ---@type CommandTable - local command_table = { - [Commands.Open] = handle_open, - [Commands.Extract] = handle_extract, - [Commands.Enter] = handle_enter, - [Commands.Leave] = handle_leave, - [Commands.Rename] = function(_) handle_yazi_command("rename", args) end, - [Commands.Remove] = function(_) handle_yazi_command("remove", args) end, - [Commands.Create] = handle_create, - [Commands.Shell] = handle_shell, - [Commands.Paste] = handle_paste, - [Commands.TabCreate] = handle_tab_create, - [Commands.TabSwitch] = handle_tab_switch, - [Commands.Arrow] = handle_arrow, - [Commands.ParentArrow] = handle_parent_arrow, - [Commands.Editor] = handle_editor, - [Commands.Pager] = handle_pager, - } + -- The command table + ---@type CommandTable + local command_table = { + [Commands.Open] = handle_open, + [Commands.Extract] = handle_extract, + [Commands.Enter] = handle_enter, + [Commands.Leave] = handle_leave, + [Commands.Rename] = function(_) handle_yazi_command("rename", args) end, + [Commands.Remove] = function(_) handle_yazi_command("remove", args) end, + [Commands.Create] = handle_create, + [Commands.Shell] = handle_shell, + [Commands.Paste] = handle_paste, + [Commands.TabCreate] = handle_tab_create, + [Commands.TabSwitch] = handle_tab_switch, + [Commands.Quit] = handle_quit, + [Commands.Arrow] = handle_arrow, + [Commands.ParentArrow] = handle_parent_arrow, + [Commands.Editor] = handle_editor, + [Commands.Pager] = handle_pager, + } - -- Get the function for the command - ---@type CommandFunction|nil - local command_func = command_table[command] + -- Get the function for the command + ---@type CommandFunction|nil + local command_func = command_table[command] - -- If the function isn't found, notify the user and exit the function - if not command_func then - return show_error("Unknown command: " .. command) - end + -- If the function isn't found, notify the user and exit the function + if not command_func then + return show_error("Unknown command: " .. command) + end - -- Otherwise, call the function for the command - command_func(args, config) + -- Otherwise, call the function for the command + command_func(args, config) end -- The setup function to setup the plugin @@ -3576,10 +3614,10 @@ end ---@param opts Configuration|nil The options given to the plugin ---@return nil local function setup(_, opts) - -- + -- - -- Initialise the plugin - initialise_plugin(opts) + -- Initialise the plugin + initialise_plugin(opts) end -- Function to be called to use the plugin @@ -3587,33 +3625,33 @@ end ---@param job { args: Arguments } The job object given by Yazi ---@return nil local function entry(_, job) - -- + -- - -- Get the arguments to the plugin - ---@type Arguments - local args = parse_number_arguments(job.args) + -- Get the arguments to the plugin + ---@type Arguments + local args = parse_number_arguments(job.args) - -- Get the command passed to the plugin - local command = table.remove(args, 1) + -- Get the command passed to the plugin + local command = table.remove(args, 1) - -- If the command isn't given, exit the function - if not command then return end + -- If the command isn't given, exit the function + if not command then return end - -- Get the configuration object - local config = get_config() + -- Get the configuration object + local config = get_config() - -- If the configuration hasn't been initialised yet, - -- then initialise the plugin with the default configuration, - -- as it hasn't been initialised either - if not config then config = initialise_plugin() end + -- If the configuration hasn't been initialised yet, + -- then initialise the plugin with the default configuration, + -- as it hasn't been initialised either + if not config then config = initialise_plugin() end - -- Call the function to handle the commands - run_command_func(command, args, config) + -- Call the function to handle the commands + run_command_func(command, args, config) end -- Returns the table required for Yazi to run the plugin ---@return { setup: fun(): nil, entry: fun(): nil } return { - setup = setup, - entry = entry, + setup = setup, + entry = entry, } diff --git a/config/yazi/plugins/chmod.yazi/main.lua b/config/yazi/plugins/chmod.yazi/main.lua index 183c31e2..d3a5a672 100644 --- a/config/yazi/plugins/chmod.yazi/main.lua +++ b/config/yazi/plugins/chmod.yazi/main.lua @@ -1,3 +1,5 @@ +--- @since 25.2.7 + local selected_or_hovered = ya.sync(function() local tab, paths = cx.active, {} for _, u in pairs(tab.selected) do diff --git a/config/yazi/plugins/full-border.yazi/main.lua b/config/yazi/plugins/full-border.yazi/main.lua index 0de33b1d..058371ad 100644 --- a/config/yazi/plugins/full-border.yazi/main.lua +++ b/config/yazi/plugins/full-border.yazi/main.lua @@ -1,3 +1,5 @@ +--- @since 25.2.7 + local function setup(_, opts) local type = opts and opts.type or ui.Border.ROUNDED local old_build = Tab.build diff --git a/config/yazi/plugins/git.yazi/README.md b/config/yazi/plugins/git.yazi/README.md index f17d7b39..10542306 100644 --- a/config/yazi/plugins/git.yazi/README.md +++ b/config/yazi/plugins/git.yazi/README.md @@ -1,7 +1,7 @@ # git.yazi > [!NOTE] -> Yazi v0.4.1 or later is required for this plugin to work. +> Yazi v25.2.7 or later is required for this plugin to work. Show the status of Git file changes as linemode in the file list. diff --git a/config/yazi/plugins/git.yazi/main.lua b/config/yazi/plugins/git.yazi/main.lua index d629d7d6..edd54fc0 100644 --- a/config/yazi/plugins/git.yazi/main.lua +++ b/config/yazi/plugins/git.yazi/main.lua @@ -1,3 +1,5 @@ +--- @since 25.2.7 + local WIN = ya.target_family() == "windows" local PATS = { { "[MT]", 6 }, -- Modified @@ -159,9 +161,6 @@ local function fetch(_, job) local repo = root(cwd) if not repo then remove(tostring(cwd)) - if not ya.__250127 then -- TODO: remove this - return 1 - end return true end @@ -178,10 +177,6 @@ local function fetch(_, job) :stdout(Command.PIPED) :output() if not output then - if not ya.__250127 then -- TODO: remove this - ya.err("Cannot spawn git command, error: " .. err) - return 0 - end return true, Err("Cannot spawn `git` command, error: %s", err) end @@ -208,9 +203,6 @@ local function fetch(_, job) end add(tostring(cwd), repo, changed) - if not ya.__250127 then -- TODO: remove this - return 3 - end return false end diff --git a/config/yazi/plugins/glow.yazi/README.md b/config/yazi/plugins/glow.yazi/README.md index c67f7cc7..ba66ae77 100644 --- a/config/yazi/plugins/glow.yazi/README.md +++ b/config/yazi/plugins/glow.yazi/README.md @@ -19,7 +19,7 @@ Make sure you have [glow](https://github.com/charmbracelet/glow) installed, and ## Feature -+ You can modify line wrap in `init.lua`, the current value is 55. ++ You can modify line wrap in `main.lua`, the current value is 55. + You can press `ctrl+e` to scroll up and `ctrl+y` to scroll down the readme file in preview panel in yazi: (add this to `keymap.toml`) ```toml prepend_keymap = [ diff --git a/config/yazi/plugins/glow.yazi/main.lua b/config/yazi/plugins/glow.yazi/main.lua index cc813e9d..09080154 100644 --- a/config/yazi/plugins/glow.yazi/main.lua +++ b/config/yazi/plugins/glow.yazi/main.lua @@ -1,7 +1,7 @@ local M = {} function M:peek(job) - -- Set a fixed width of 55 characters for the preview + -- Set a fixed width of 50 characters for the preview local preview_width = 55 local child = Command("glow") @@ -55,20 +55,8 @@ function M:seek(job) if not h or h.url ~= job.file.url then return end - - local scroll_amount = 1 - local scroll_offset = job.units - - if job.key == "ctrl-e" then - scroll_offset = scroll_amount - elseif job.key == "ctrl-y" then - scroll_offset = -scroll_amount - else - scroll_offset = job.units - end - ya.manager_emit('peek', { - math.max(0, cx.active.preview.skip + scroll_offset), + math.max(0, cx.active.preview.skip + job.units), only_if = job.file.url, }) end diff --git a/config/yazi/plugins/hexyl.yazi/main.lua b/config/yazi/plugins/hexyl.yazi/main.lua index 6fe0990e..3b6a51cf 100644 --- a/config/yazi/plugins/hexyl.yazi/main.lua +++ b/config/yazi/plugins/hexyl.yazi/main.lua @@ -1,57 +1,76 @@ local M = {} function M:peek(job) - local child - local l = self.file.cha.len - if l == 0 then - child = Command("hexyl") - :args({ - tostring(job.file.url), - }) - :stdout(Command.PIPED) - :stderr(Command.PIPED) - :spawn() - else - child = Command("hexyl") - :args({ - "--border", - "none", - "--terminal-width", - tostring(job.area.w), - tostring(job.file.url), - }) - :stdout(Command.PIPED) - :stderr(Command.PIPED) - :spawn() - end + local child + local l = job.file.cha.len + if l == 0 then + child = Command("hexyl") + :args({ + tostring(job.file.url), + }) + :stdout(Command.PIPED) + :stderr(Command.PIPED) + :spawn() + else + child = Command("hexyl") + :args({ + "--border", "none", + "--terminal-width", tostring(job.area.w), + tostring(job.file.url), + }) + :stdout(Command.PIPED) + :stderr(Command.PIPED) + :spawn() + end - local limit = job.area.h - local i, lines = 0, "" - repeat - local next, event = child:read_line() - if event == 1 then - ya.err(tostring(event)) - elseif event ~= 0 then - break - end + if not child then + return require("code").peek(job) + end - i = i + 1 - if i > job.skip then - lines = lines .. next - end - until i >= job.skip + limit + local limit = job.area.h + local i, lines = 0, "" + repeat + local line, event = child:read_line() + if event == 1 then + ya.err(tostring(event)) + break + elseif event ~= 0 then + break + end - child:start_kill() - if job.skip > 0 and i < job.skip + limit then - ya.manager_emit("peek", { math.max(0, i - limit), only_if = job.file.url, upper_bound = true }) - else - lines = lines:gsub("\t", string.rep(" ", PREVIEW.tab_size)) - ya.preview_widgets(job, { ui.Text.parse(lines):area(job.area) }) - end + i = i + 1 + if i > job.skip then + lines = lines .. line + end + until i >= job.skip + limit + + child:start_kill() + if job.skip > 0 and i < job.skip + limit then + ya.manager_emit("peek", { + math.max(0, i - limit), + only_if = job.file.url, + upper_bound = true, + }) + else + lines = lines:gsub("\t", string.rep(" ", PREVIEW.tab_size)) + ya.preview_widgets(job, { ui.Text.parse(lines):area(job.area) }) + end end -function M:seek(units) - require("code").seek(job, units) +function M:seek(job) + -- Get the currently hovered file from your UI context. + local h = cx.active.current.hovered + if not h or h.url ~= job.file.url then + return + end + + local scroll_offset = job.units + + -- Emit a new peek event with the updated skip value. + ya.manager_emit("peek", { + math.max(0, cx.active.preview.skip + scroll_offset), + only_if = job.file.url, + }) end return M diff --git a/config/yazi/plugins/hide-preview.yazi/README.md b/config/yazi/plugins/hide-preview.yazi/README.md index f307e667..f6ecb7a0 100644 --- a/config/yazi/plugins/hide-preview.yazi/README.md +++ b/config/yazi/plugins/hide-preview.yazi/README.md @@ -1,5 +1,8 @@ # hide-preview.yazi +> [!WARNING] +> This plugin has been deprecated. Please use the new [toggle-pane.yazi](../toggle-pane.yazi) instead. + Switch the preview pane between hidden and shown. https://github.com/yazi-rs/plugins/assets/17523360/c4f0b5c4-ff9f-4be8-ba73-4d8e7902e383 diff --git a/config/yazi/plugins/hide-preview.yazi/main.lua b/config/yazi/plugins/hide-preview.yazi/main.lua index fcf4094f..7b9a2bd6 100644 --- a/config/yazi/plugins/hide-preview.yazi/main.lua +++ b/config/yazi/plugins/hide-preview.yazi/main.lua @@ -1,6 +1,14 @@ +--- @since 25.2.7 --- @sync entry local function entry(st) + ya.notify { + title = "Deprecated plugin", + content = "The `hide-preview` plugin is deprecated, please use the new `toggle-pane` plugin instead: https://github.com/yazi-rs/plugins/tree/main/toggle-pane.yazi", + timeout = 10, + level = "warn", + } + if st.old then Tab.layout, st.old = st.old, nil else diff --git a/config/yazi/plugins/max-preview.yazi/README.md b/config/yazi/plugins/max-preview.yazi/README.md index 73b90585..4de80df0 100644 --- a/config/yazi/plugins/max-preview.yazi/README.md +++ b/config/yazi/plugins/max-preview.yazi/README.md @@ -1,5 +1,8 @@ # max-preview.yazi +> [!WARNING] +> This plugin has been deprecated. Please use the new [toggle-pane.yazi](../toggle-pane.yazi) instead. + Maximize or restore the preview pane. https://github.com/yazi-rs/plugins/assets/17523360/8976308e-ebfe-4e9e-babe-153eb1f87d61 diff --git a/config/yazi/plugins/max-preview.yazi/main.lua b/config/yazi/plugins/max-preview.yazi/main.lua index 64edfed6..025ffe4d 100644 --- a/config/yazi/plugins/max-preview.yazi/main.lua +++ b/config/yazi/plugins/max-preview.yazi/main.lua @@ -1,6 +1,14 @@ +--- @since 25.2.7 --- @sync entry local function entry(st) + ya.notify { + title = "Deprecated plugin", + content = "The `max-preview` plugin is deprecated, please use the new `toggle-pane` plugin instead: https://github.com/yazi-rs/plugins/tree/main/toggle-pane.yazi", + timeout = 10, + level = "warn", + } + if st.old then Tab.layout, st.old = st.old, nil else diff --git a/config/yazi/plugins/ouch.yazi/README.md b/config/yazi/plugins/ouch.yazi/README.md index 570b52b0..fd2ee089 100644 --- a/config/yazi/plugins/ouch.yazi/README.md +++ b/config/yazi/plugins/ouch.yazi/README.md @@ -10,7 +10,7 @@ ## Installation -If you use latest Yazi from main branch +If you use Yazi from latest main branch ```bash # Linux/macOS git clone https://github.com/ndtoan96/ouch.yazi.git ~/.config/yazi/plugins/ouch.yazi @@ -22,16 +22,16 @@ git clone https://github.com/ndtoan96/ouch.yazi.git %AppData%\yazi\config\plugin git clone https://github.com/ndtoan96/ouch.yazi.git "$($env:APPDATA)\yazi\config\plugins\ouch.yazi" ``` -If you use Yazi <= 0.3.3 +If you use Yazi < 0.4.3 ```bash # Linux/macOS -git clone --branch v0.2.1 --single-branch https://github.com/ndtoan96/ouch.yazi.git ~/.config/yazi/plugins/ouch.yazi +git clone --branch v0.4.0 --single-branch https://github.com/ndtoan96/ouch.yazi.git ~/.config/yazi/plugins/ouch.yazi # Windows with cmd -git clone --branch v0.2.1 --single-branch https://github.com/ndtoan96/ouch.yazi.git %AppData%\yazi\config\plugins\ouch.yazi +git clone --branch v0.4.0 --single-branch https://github.com/ndtoan96/ouch.yazi.git %AppData%\yazi\config\plugins\ouch.yazi # Windows with powershell -git clone --branch v0.2.1 --single-branch https://github.com/ndtoan96/ouch.yazi.git "$($env:APPDATA)\yazi\config\plugins\ouch.yazi" +git clone --branch v0.4.0 --single-branch https://github.com/ndtoan96/ouch.yazi.git "$($env:APPDATA)\yazi\config\plugins\ouch.yazi" ``` Make sure you have [ouch](https://github.com/ouch-org/ouch) installed and in your `PATH`. diff --git a/config/yazi/plugins/relative-motions.yazi/README.md b/config/yazi/plugins/relative-motions.yazi/README.md index b39c8a2a..35a2386b 100644 --- a/config/yazi/plugins/relative-motions.yazi/README.md +++ b/config/yazi/plugins/relative-motions.yazi/README.md @@ -6,26 +6,14 @@ https://github.com/dedukun/relative-motions.yazi/assets/25795432/04fb186a-5efe-4 ## Requirements -- [Yazi](https://github.com/sxyazi/yazi) v0.3.0+ +- [Yazi](https://github.com/sxyazi/yazi) v25.2.7+ ## Installation -If you use the latest Yazi from main branch - ```sh ya pack -a dedukun/relative-motions ``` -If you are using <= v0.3.3 - -```sh -# Linux/macOS -git clone --branch 0.3.3 https://github.com/dedukun/relative-motions.yazi.git ~/.config/yazi/plugins/relative-motions.yazi - -# Windows -git clone --branch 0.3.3 https://github.com/dedukun/relative-motions.yazi.git %AppData%\yazi\config\plugins\relative-motions.yazi -``` - ## Configuration If you want to use the numbers directly to start a motion add this to your `keymap.toml`: @@ -35,47 +23,47 @@ If you want to use the numbers directly to start a motion add this to your `keym ```toml [[manager.prepend_keymap]] on = [ "1" ] -run = "plugin relative-motions --args=1" +run = "plugin relative-motions 1" desc = "Move in relative steps" [[manager.prepend_keymap]] on = [ "2" ] -run = "plugin relative-motions --args=2" +run = "plugin relative-motions 2" desc = "Move in relative steps" [[manager.prepend_keymap]] on = [ "3" ] -run = "plugin relative-motions --args=3" +run = "plugin relative-motions 3" desc = "Move in relative steps" [[manager.prepend_keymap]] on = [ "4" ] -run = "plugin relative-motions --args=4" +run = "plugin relative-motions 4" desc = "Move in relative steps" [[manager.prepend_keymap]] on = [ "5" ] -run = "plugin relative-motions --args=5" +run = "plugin relative-motions 5" desc = "Move in relative steps" [[manager.prepend_keymap]] on = [ "6" ] -run = "plugin relative-motions --args=6" +run = "plugin relative-motions 6" desc = "Move in relative steps" [[manager.prepend_keymap]] on = [ "7" ] -run = "plugin relative-motions --args=7" +run = "plugin relative-motions 7" desc = "Move in relative steps" [[manager.prepend_keymap]] on = [ "8" ] -run = "plugin relative-motions --args=8" +run = "plugin relative-motions 8" desc = "Move in relative steps" [[manager.prepend_keymap]] on = [ "9" ] -run = "plugin relative-motions --args=9" +run = "plugin relative-motions 9" desc = "Move in relative steps" ``` @@ -94,18 +82,19 @@ desc = "Trigger a new relative motion" Additionally there are a couple of initial configurations that can be given to the plugin's `setup` function: -| Configuration | Values | Default | Description | -| -------------- | ----------------------------------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------- | -| `show_numbers` | `relative`, `absolute`, `relative_absolute` or `none` | `none` | Shows relative or absolute numbers before the file icon | -| `show_motion` | `true` or `false` | `false` | Shows current motion in Status bar | -| `only_motions` | `true` or `false` | `false` | If true, only the motion movements will be enabled, i.e., the commands for delete, cut, yank and visual selection will be disabled | +| Configuration | Values | Default | Description | +| -------------- | ----------------------------------------------------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------- | +| `show_numbers` | `relative`, `absolute`, `relative_absolute` or `none` | `none` | Shows relative or absolute numbers before the file icon | +| `show_motion` | `true` or `false` | `false` | Shows current motion in Status bar | +| `only_motions` | `true` or `false` | `false` | If true, only the motion movements will be enabled, i.e., the commands for delete, cut, yank and visual selection will be disabled | +| `enter_mode` | `cache`, `first` or `cache_or_first` | `cache_or_first` | The method to enter folders | If you want, for example, to enable relative numbers as well as to show the motion in the status bar, add the following to Yazi's `init.lua`, i.e. `~/.config/yazi/init.lua`: ```lua -- ~/.config/yazi/init.lua -require("relative-motions"):setup({ show_numbers="relative", show_motion = true }) +require("relative-motions"):setup({ show_numbers="relative", show_motion = true, enter_mode ="first" }) ``` > [!NOTE] @@ -119,13 +108,15 @@ require("relative-motions"):setup({ show_numbers="relative", show_motion = true This plugin adds the some basic vim motions like `3k`, `12j`, `10gg`, etc. The following table show all the available motions: -| Command | Description | -| -------------- | ------------------- | -| `j`/`` | Move `n` lines down | -| `k`/`` | Move `n` lines up | -| `gj`/`g` | Go `n` lines down | -| `gk`/`g` | Go `n` lines up | -| `gg` | Go to line | +| Command | Description | +| -------------- | --------------------- | +| `j`/`` | Move `n` lines down | +| `k`/`` | Move `n` lines up | +| `h`/`` | Move `n` folders back | +| `l`/`` | Enter `n` folders | +| `gj`/`g` | Go `n` lines down | +| `gk`/`g` | Go `n` lines up | +| `gg` | Go to line | Furthermore, the following operations were also added: diff --git a/config/yazi/plugins/relative-motions.yazi/main.lua b/config/yazi/plugins/relative-motions.yazi/main.lua index be938098..a2764595 100644 --- a/config/yazi/plugins/relative-motions.yazi/main.lua +++ b/config/yazi/plugins/relative-motions.yazi/main.lua @@ -1,3 +1,4 @@ +--- @since 25.2.7 -- stylua: ignore local MOTIONS_AND_OP_KEYS = { { on = "0" }, { on = "1" }, { on = "2" }, { on = "3" }, { on = "4" }, @@ -8,7 +9,7 @@ local MOTIONS_AND_OP_KEYS = { { on = "t" }, { on = "L" }, { on = "H" }, { on = "w" }, { on = "W" }, { on = "<" }, { on = ">" }, { on = "~" }, -- movement - { on = "g" }, { on = "j" }, { on = "k" }, { on = "" }, { on = "" } + { on = "g" }, { on = "j" }, { on = "k" }, { on = "h" }, { on = "l" }, { on = "" }, { on = "" }, { on = "" }, { on = "" } } -- stylua: ignore @@ -16,7 +17,7 @@ local MOTION_KEYS = { { on = "0" }, { on = "1" }, { on = "2" }, { on = "3" }, { on = "4" }, { on = "5" }, { on = "6" }, { on = "7" }, { on = "8" }, { on = "9" }, -- movement - { on = "g" }, { on = "j" }, { on = "k" } + { on = "g" }, { on = "j" }, { on = "k" }, { on = "h" }, { on = "l" }, { on = "" }, { on = "" }, { on = "" }, { on = "" } } -- stylua: ignore @@ -30,6 +31,10 @@ local SHOW_NUMBERS_ABSOLUTE = 0 local SHOW_NUMBERS_RELATIVE = 1 local SHOW_NUMBERS_RELATIVE_ABSOLUTE = 2 +local ENTER_MODE_FIRST = 0 +local ENTER_MODE_CACHE = 1 +local ENTER_MODE_CACHE_OR_FIRST = 2 + ----------------------------------------------- ----------------- R E N D E R ----------------- ----------------------------------------------- @@ -149,6 +154,10 @@ local function normal_direction(dir) return "j" elseif dir == "" then return "k" + elseif dir == "" then + return "h" + elseif dir == "" then + return "l" end return dir end @@ -205,6 +214,29 @@ end local get_active_tab = ya.sync(function(_) return cx.tabs.idx end) +local get_cache_or_first_dir = ya.sync(function(state) + if state._enter_mode == ENTER_MODE_CACHE then + return nil + elseif state._enter_mode == ENTER_MODE_CACHE_OR_FIRST then + local hovered_file = cx.active.current.hovered + + if hovered_file ~= nil and hovered_file.cha.is_dir then + return cx.active.current.cursor + end + end + + local files = cx.active.current.files + local index = 1 + + for i = 1, #files do + if files[i].cha.is_dir then + index = i + break + end + end + + return index - 1 +end) ----------------------------------------------- ---------- E N T R Y / S E T U P ---------- ----------------------------------------------- @@ -231,7 +263,7 @@ return { if cmd == "g" then if direction == "g" then - ya.manager_emit("arrow", { -99999999 }) + ya.manager_emit("arrow", { "top" }) ya.manager_emit("arrow", { lines - 1 }) render_clear() return @@ -254,6 +286,19 @@ return { ya.manager_emit("arrow", { lines }) elseif cmd == "k" then ya.manager_emit("arrow", { -lines }) + elseif cmd == "h" then + for _ = 1, lines do + ya.manager_emit("leave", {}) + end + elseif cmd == "l" then + for _ = 1, lines do + ya.manager_emit("enter", {}) + local file_idx = get_cache_or_first_dir() + if file_idx then + ya.manager_emit("arrow", { "top" }) + ya.manager_emit("arrow", { file_idx }) + end + end elseif is_tab_command(cmd) then if cmd == "t" then for _ = 1, lines do @@ -315,6 +360,16 @@ return { render_motion_setup() end + if args["enter_mode"] == "cache" then + state._enter_mode = ENTER_MODE_CACHE + elseif args["enter_mode"] == "first" then + state._enter_mode = ENTER_MODE_FIRST + elseif args["enter_mode"] == "cache_or_first" then + state._enter_mode = ENTER_MODE_CACHE_OR_FIRST + else + state._enter_mode = ENTER_MODE_CACHE_OR_FIRST + end + if args["show_numbers"] == "absolute" then render_numbers(SHOW_NUMBERS_ABSOLUTE) elseif args["show_numbers"] == "relative" then diff --git a/config/yazi/plugins/torrent-preview.yazi/README.md b/config/yazi/plugins/torrent-preview.yazi/README.md index aa5ed0f8..8381ee09 100644 --- a/config/yazi/plugins/torrent-preview.yazi/README.md +++ b/config/yazi/plugins/torrent-preview.yazi/README.md @@ -1,6 +1,6 @@ # torrent-preview.yazi -[Yazi](https://github.com/sxyazi/yazi) plugin to preview `application/x-bittorrent` files +[Yazi](https://github.com/sxyazi/yazi) plugin to preview `application/bittorrent` files ![show case](https://github.com/kirasok/torrent-preview.yazi/assets/75790517/6f215e6d-bb19-46f4-b606-9241594028ff) @@ -13,6 +13,14 @@ ### Linux/MacOS +Using the [Yazi Package Manager](https://yazi-rs.github.io/docs/cli/#package-manager): + +```sh +ya pack -a kirasok/torrent-preview +``` + +Or manually: + ```sh git clone https://github.com/kirasok/torrent-preview.yazi.git ~/.config/yazi/plugins/torrent-preview.yazi ``` @@ -23,6 +31,10 @@ Add this to your `yazi.toml`: ```toml [[plugin.prepend_previewers]] -mime = "application/x-bittorrent" +mime = "application/bittorrent" run = "torrent-preview" ``` + +> [!NOTE] +> Yazi after `v0.4` removes `x-` prefix from subtype, so even if `file -i` outputs `application/x-bittorrent`, you should use `application/bittorrent` ([relevant issue](https://github.com/kirasok/torrent-preview.yazi/issues/2)) + diff --git a/config/yazi/yazi.toml b/config/yazi/yazi.toml index 4894591c..bfeff9ba 100644 --- a/config/yazi/yazi.toml +++ b/config/yazi/yazi.toml @@ -93,7 +93,7 @@ previewers = [ {mime = "application/json", run = "code"}, # Image {mime = "image/vnd.djvu", run = "noop"}, - {mime = "image/*", run = "image"}, + # {mime = "image/*", run = "image"}, # Video # {mime = "video/*", run = "video"}, # PDF diff --git a/dotter b/dotter old mode 100755 new mode 100644 diff --git a/dotter.arm b/dotter.arm old mode 100755 new mode 100644 diff --git a/dotter.exe b/dotter.exe old mode 100755 new mode 100644 diff --git a/local/bin/tmux-sessionizer b/local/bin/tmux-sessionizer index f98be204..53cfc552 100755 --- a/local/bin/tmux-sessionizer +++ b/local/bin/tmux-sessionizer @@ -3,7 +3,7 @@ if [[ $# -eq 1 ]]; then selected=$1 else - selected=$(fd --min-depth 1 --max-depth 1 --type d . ~/repos/Rust/ ~/repos/examples/ ~/repos/ ~/neorg/Work/ ~/Nextcloud/repos/ ~/Nextcloud/Documents/LaTeX/ ~/Nextcloud/Documents/Typst/ ~/Nextcloud/repos/university/** ~/Nextcloud/repos/university/**/**/ ~/repos/yoda-bot/ ~/repos/Codnity/ ~/repos/Codnity/emisela/ ~/repos/Codnity/dio/ ~/repos/Codnity/zaao/ ~/repos/Codnity/talentflow/ | sk --height 16) + selected=$(fd --min-depth 1 --max-depth 1 --type d . ~/repos/Rust/ ~/repos/examples/ ~/repos/ ~/neorg/Work/ ~/Nextcloud/repos/ ~/Nextcloud/Documents/LaTeX/ ~/Nextcloud/Documents/Typst/ ~/Nextcloud/repos/university/** ~/Nextcloud/repos/university/**/**/ ~/repos/yoda-bot/ ~/repos/Codnity/ ~/repos/Codnity/emisela/ ~/repos/Codnity/dio/ ~/repos/Codnity/zaao/ ~/repos/Codnity/talentflow/ ~/Obsidian | sk --height 16) fi if [[ -z $selected ]]; then