diff --git a/dots-extra/fedora/hypr/hyprland/execs.conf b/dots-extra/fedora/hypr/hyprland/execs.conf index ba334c012..eb2af4bc4 100644 --- a/dots-extra/fedora/hypr/hyprland/execs.conf +++ b/dots-extra/fedora/hypr/hyprland/execs.conf @@ -10,7 +10,6 @@ exec-once = gnome-keyring-daemon --start --components=secrets exec-once = hypridle exec-once = dbus-update-activation-environment --all exec-once = sleep 1 && dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP # Some fix idk -exec-once = hyprpm reload # Audio exec-once = easyeffects --gapplication-service diff --git a/dots/.config/fish/auto-Hypr.fish b/dots/.config/fish/auto-Hypr.fish index 711f31dbe..893451c44 100644 --- a/dots/.config/fish/auto-Hypr.fish +++ b/dots/.config/fish/auto-Hypr.fish @@ -1,5 +1,5 @@ # Auto start Hyprland on tty1 if test -z "$DISPLAY" ;and test "$XDG_VTNR" -eq 1 mkdir -p ~/.cache - exec Hyprland > ~/.cache/hyprland.log 2>&1 + exec start-hyprland > ~/.cache/hyprland.log 2>&1 end diff --git a/dots/.config/fish/config.fish b/dots/.config/fish/config.fish index 72476660f..0d26fe1dc 100755 --- a/dots/.config/fish/config.fish +++ b/dots/.config/fish/config.fish @@ -18,9 +18,11 @@ if status is-interactive # Commands to run in interactive sessions can go here end # Aliases - alias pamcan pacman + alias clear "printf '\033[2J\033[3J\033[1;1H'" # fix: kitty doesn't clear properly + alias celar "printf '\033[2J\033[3J\033[1;1H'" + alias claer "printf '\033[2J\033[3J\033[1;1H'" alias ls 'eza --icons' - alias clear "printf '\033[2J\033[3J\033[1;1H'" + alias pamcan pacman alias q 'qs -c ii' end diff --git a/dots/.config/hypr/custom/rules.conf b/dots/.config/hypr/custom/rules.conf index b253bc8d1..9b48404e6 100644 --- a/dots/.config/hypr/custom/rules.conf +++ b/dots/.config/hypr/custom/rules.conf @@ -5,7 +5,7 @@ # ######## Window rules ######## # Uncomment to apply global transparency to all windows: -# windowrulev2 = opacity 0.89 override 0.89 override, class:.* +# windowrule = opacity 0.89 override 0.89 override, match:class .* # Disable blur for all xwayland apps -# windowrulev2 = noblur, xwayland:1 \ No newline at end of file +# windowrule = no_blur on, match:xwayland 1 diff --git a/dots/.config/hypr/hyprland.conf b/dots/.config/hypr/hyprland.conf index e40c6c14a..632d3700c 100644 --- a/dots/.config/hypr/hyprland.conf +++ b/dots/.config/hypr/hyprland.conf @@ -5,8 +5,11 @@ $qsConfig = ii exec = hyprctl dispatch submap global # DO NOT REMOVE THIS OR YOU WON'T BE ABLE TO USE ANY KEYBIND submap = global # This is required for catchall to work -# Defaults +# Environment variables source=hyprland/env.conf +source=custom/env.conf + +# Defaults source=hyprland/execs.conf source=hyprland/general.conf source=hyprland/rules.conf @@ -14,7 +17,6 @@ source=hyprland/colors.conf source=hyprland/keybinds.conf # Custom -source=custom/env.conf source=custom/execs.conf source=custom/general.conf source=custom/rules.conf diff --git a/dots/.config/hypr/hyprland/colors.conf b/dots/.config/hypr/hyprland/colors.conf index 5954859e8..28863e35d 100644 --- a/dots/.config/hypr/hyprland/colors.conf +++ b/dots/.config/hypr/hyprland/colors.conf @@ -31,4 +31,4 @@ plugin { } } -windowrulev2 = bordercolor rgba(FFB2BCAA) rgba(FFB2BC77),pinned:1 \ No newline at end of file +windowrule = border_color rgba(FFB2BCAA) rgba(FFB2BC77), match:pin 1 diff --git a/dots/.config/hypr/hyprland/env.conf b/dots/.config/hypr/hyprland/env.conf index affd1ca82..0c2103407 100644 --- a/dots/.config/hypr/hyprland/env.conf +++ b/dots/.config/hypr/hyprland/env.conf @@ -5,7 +5,7 @@ env = ELECTRON_OZONE_PLATFORM_HINT,auto env = XDG_DATA_DIRS,$HOME/.local/share/flatpak/exports/share:/var/lib/flatpak/exports/share:/usr/local/share:/usr/share # ############ Themes ############# -env = QT_QPA_PLATFORM, wayland +env = QT_QPA_PLATFORM, wayland;xcb env = QT_QPA_PLATFORMTHEME, kde env = XDG_MENU_PREFIX, plasma- diff --git a/dots/.config/hypr/hyprland/execs.conf b/dots/.config/hypr/hyprland/execs.conf index 2cad1308e..1f4b0e913 100644 --- a/dots/.config/hypr/hyprland/execs.conf +++ b/dots/.config/hypr/hyprland/execs.conf @@ -8,7 +8,6 @@ exec-once = gnome-keyring-daemon --start --components=secrets exec-once = hypridle exec-once = dbus-update-activation-environment --all exec-once = sleep 1 && dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP # Some fix idk -exec-once = hyprpm reload # Audio exec-once = easyeffects --hide-window --service-mode diff --git a/dots/.config/hypr/hyprland/general.conf b/dots/.config/hypr/hyprland/general.conf index 3f5fa2cf8..071515e4c 100644 --- a/dots/.config/hypr/hyprland/general.conf +++ b/dots/.config/hypr/hyprland/general.conf @@ -4,8 +4,8 @@ monitor=,preferred,auto,1 gesture = 3, swipe, move, gesture = 3, pinch, float gesture = 4, horizontal, workspace -gesture = 4, up, dispatcher, global, quickshell:overviewToggle -gesture = 4, down, dispatcher, global, quickshell:overviewClose +gesture = 4, up, dispatcher, global, quickshell:overviewWorkspacesToggle +gesture = 4, down, dispatcher, global, quickshell:overviewWorkspacesClose gestures { workspace_swipe_distance = 700 workspace_swipe_cancel_ratio = 0.2 @@ -22,7 +22,7 @@ general { gaps_workspaces = 50 border_size = 1 - col.active_border = rgba(0DB7D4FF) + col.active_border = rgba(0DB7D455) col.inactive_border = rgba(31313600) resize_on_border = true @@ -31,10 +31,10 @@ general { allow_tearing = true # This just allows the `immediate` window rule to work snap { - enabled = true - window_gap = 4 - monitor_gap = 5 - respect_gaps = true + enabled = true + window_gap = 4 + monitor_gap = 5 + respect_gaps = true } } @@ -46,6 +46,9 @@ dwindle { } decoration { + # 2 = circle, higher = squircle, 4 = very obvious squircle + # Clear squircles look really off; we use only extra .4 here to make the rounding feel more continuous + rounding_power = 2 rounding = 18 blur { @@ -69,16 +72,16 @@ decoration { shadow { enabled = true ignore_window = true - range = 30 - offset = 0 2 - render_power = 4 - color = rgba(00000010) + range = 50 + offset = 0 4 + render_power = 10 + color = rgba(00000027) } # Dim dim_inactive = true - dim_strength = 0.025 - dim_special = 0.07 + dim_strength = 0.05 + dim_special = 0.2 } animations { @@ -112,6 +115,8 @@ animations { ## specialWorkspace animation = specialWorkspaceIn, 1, 2.8, emphasizedDecel, slidevert animation = specialWorkspaceOut, 1, 1.2, emphasizedAccel, slidevert + # zoom + animation = zoomFactor, 1, 3, standardDecel } input { @@ -142,7 +147,7 @@ misc { animate_mouse_windowdragging = false enable_swallow = false swallow_regex = (foot|kitty|allacritty|Alacritty) - new_window_takes_over_fullscreen = 2 + on_focus_under_fullscreen = 2 allow_session_lock_restore = true session_lock_xray = true initial_workspace_tracking = false @@ -157,19 +162,7 @@ binds { cursor { zoom_factor = 1 zoom_rigid = false + zoom_disable_aa = true hotspot_padding = 1 } -# Overview -plugin { - hyprexpo { - columns = 3 - gap_size = 5 - bg_col = rgb(000000) - workspace_method = first 1 # [center/first] [workspace] e.g. first 1 or center m+1 - - enable_gesture = false # laptop touchpad, 4 fingers - gesture_distance = 300 # how far is the "max" - gesture_positive = false - } -} diff --git a/dots/.config/hypr/hyprland/keybinds.conf b/dots/.config/hypr/hyprland/keybinds.conf index 6032ab65a..6085e7de4 100644 --- a/dots/.config/hypr/hyprland/keybinds.conf +++ b/dots/.config/hypr/hyprland/keybinds.conf @@ -53,7 +53,7 @@ bindd = Ctrl+Super, T, Toggle wallpaper selector, global, quickshell:wallpaperSe bindd = Ctrl+Super+Alt, T, Select random wallpaper, global, quickshell:wallpaperSelectorRandom # Random wallpaper bindd = Ctrl+Super, T, Change wallpaper, exec, qs -c $qsConfig ipc call TEST_ALIVE || ~/.config/quickshell/$qsConfig/scripts/colors/switchwall.sh # [hidden] Change wallpaper (fallback) bind = Ctrl+Super, R, exec, killall ags agsv1 gjs ydotool qs quickshell; qs -c $qsConfig & # Restart widgets -bind = Super+Alt, W, global, quickshell:panelFamilyCycle # Cycle panel family +bind = Ctrl+Super, P, global, quickshell:panelFamilyCycle # Cycle panel family ##! Utilities # Screenshot, Record, OCR, Color picker, Clipboard history @@ -109,8 +109,8 @@ bind = Super+Shift+Alt, Q, exec, hyprctl kill # Forcefully zap a window # Window split ratio #/# binde = Super, ;/',, # Adjust split ratio -binde = Super, Semicolon, splitratio, -0.1 # [hidden] -binde = Super, Apostrophe, splitratio, +0.1 # [hidden] +binde = Super, Semicolon, layoutmsg, splitratio -0.1 # [hidden] +binde = Super, Apostrophe, layoutmsg, splitratio +0.1 # [hidden] # Positioning mode bind = Super+Alt, Space, togglefloating, # Float/Tile bind = Super, D, fullscreen, 1 # Maximize @@ -230,10 +230,8 @@ bindd = Ctrl+Shift+Alt+Super, Delete, Shutdown, exec, systemctl poweroff || logi ##! Screen # Zoom -binde = Super, Minus, exec, qs -c $qsConfig ipc call zoom zoomOut # Zoom out -binde = Super, Equal, exec, qs -c $qsConfig ipc call zoom zoomIn # Zoom in -binde = Super, Minus, exec, qs -c $qsConfig ipc call TEST_ALIVE || ~/.config/hypr/hyprland/scripts/zoom.sh decrease 0.1 # [hidden] Zoom out -binde = Super, Equal, exec, qs -c $qsConfig ipc call TEST_ALIVE || ~/.config/hypr/hyprland/scripts/zoom.sh increase 0.1 # [hidden] Zoom in +binde = Super, Minus, exec, ~/.config/hypr/hyprland/scripts/zoom.sh decrease 0.3 # Zoom out +binde = Super, Equal, exec, ~/.config/hypr/hyprland/scripts/zoom.sh increase 0.3 # Zoom in # Zoom with keypad binde = Super, code:82, exec, qs -c $qsConfig ipc call zoom zoomOut # [hidden] Zoom out binde = Super, code:86, exec, qs -c $qsConfig ipc call zoom zoomIn # [hidden] Zoom in @@ -253,11 +251,11 @@ bindl= ,XF86AudioPause, exec, playerctl play-pause # [hidden] ##! Apps bind = Super, Return, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "${TERMINAL}" "kitty -1" "foot" "alacritty" "wezterm" "konsole" "kgx" "uxterm" "xterm" # Terminal -bind = Super, T, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "${TERMINAL}" "kitty -1" "foot" "alacritty" "wezterm" "konsole" "kgx" "uxterm" "xterm" # [hidden] (terminal) (alt) +bind = Super, T, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "${TERMINAL}" "kitty -1" "foot" "alacritty" "wezterm" "konsole" "kgx" "uxterm" "xterm" # [hidden] (terminal) (alt) bind = Ctrl+Alt, T, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "${TERMINAL}" "kitty -1" "foot" "alacritty" "wezterm" "konsole" "kgx" "uxterm" "xterm" # [hidden] (terminal) (for Ubuntu people) bind = Super, E, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "dolphin" "nautilus" "nemo" "thunar" "${TERMINAL}" "kitty -1 fish -c yazi" # File manager bind = Super, W, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "google-chrome-stable" "zen-browser" "firefox" "brave" "chromium" "microsoft-edge-stable" "opera" "librewolf" # Browser -bind = Super, C, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "code" "codium" "cursor" "zed" "zedit" "zeditor" "kate" "gnome-text-editor" "emacs" "command -v nvim && kitty -1 nvim" "command -v micro && kitty -1 micro" # Code editor +bind = Super, C, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "antigravity" "code" "codium" "cursor" "zed" "zedit" "zeditor" "kate" "gnome-text-editor" "emacs" "command -v nvim && kitty -1 nvim" "command -v micro && kitty -1 micro" # Code editor bind = Ctrl+Super+Shift+Alt, W, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "wps" "onlyoffice-desktopeditors" "libreoffice" # Office software bind = Super, X, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "kate" "gnome-text-editor" "emacs" # Text editor bind = Ctrl+Super, V, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "pavucontrol-qt" "pavucontrol" # Volume mixer diff --git a/dots/.config/hypr/hyprland/rules.conf b/dots/.config/hypr/hyprland/rules.conf index 3a3a01ef9..4c618847d 100644 --- a/dots/.config/hypr/hyprland/rules.conf +++ b/dots/.config/hypr/hyprland/rules.conf @@ -1,165 +1,168 @@ # ######## Window rules ######## # Disable blur for xwayland context menus -windowrulev2 = noblur,class:^()$,title:^()$ +windowrule = match:class ^()$, match:title ^()$, no_blur on # Disable blur for every window -windowrulev2 = noblur, class:.* +windowrule = match:class .*, no_blur on # Floating -windowrulev2 = center, title:^(Open File)(.*)$ -windowrulev2 = float, title:^(Open File)(.*)$ -windowrulev2 = center, title:^(Select a File)(.*)$ -windowrulev2 = float, title:^(Select a File)(.*)$ -windowrulev2 = center, title:^(Choose wallpaper)(.*)$ -windowrulev2 = float, title:^(Choose wallpaper)(.*)$ -windowrulev2 = size 60% 65%, title:^(Choose wallpaper)(.*)$ -windowrulev2 = center, title:^(Open Folder)(.*)$ -windowrulev2 = float, title:^(Open Folder)(.*)$ -windowrulev2 = center, title:^(Save As)(.*)$ -windowrulev2 = float, title:^(Save As)(.*)$ -windowrulev2 = center, title:^(Library)(.*)$ -windowrulev2 = float, title:^(Library)(.*)$ -windowrulev2 = center, title:^(File Upload)(.*)$ -windowrulev2 = float, title:^(File Upload)(.*)$ -windowrulev2 = center, title:^(.*)(wants to save)$ -windowrulev2 = float, title:^(.*)(wants to save)$ -windowrulev2 = center, title:^(.*)(wants to open)$ -windowrulev2 = float, title:^(.*)(wants to open)$ -windowrulev2 = float, class:^(blueberry\.py)$ -windowrulev2 = float, class:^(guifetch)$ # FlafyDev/guifetch -windowrulev2 = float, class:^(pavucontrol)$ -windowrulev2 = size 45%, class:^(pavucontrol)$ -windowrulev2 = center, class:^(pavucontrol)$ -windowrulev2 = float, class:^(org.pulseaudio.pavucontrol)$ -windowrulev2 = size 45%, class:^(org.pulseaudio.pavucontrol)$ -windowrulev2 = center, class:^(org.pulseaudio.pavucontrol)$ -windowrulev2 = float, class:^(nm-connection-editor)$ -windowrulev2 = size 45%, class:^(nm-connection-editor)$ -windowrulev2 = center, class:^(nm-connection-editor)$ -windowrulev2 = float, class:.*plasmawindowed.* -windowrulev2 = float, class:kcm_.* -windowrulev2 = float, class:.*bluedevilwizard -windowrulev2 = float, title:.*Welcome -windowrulev2 = float, title:^(illogical-impulse Settings)$ -windowrulev2 = float, title:.*Shell conflicts.* -windowrulev2 = float, class:org.freedesktop.impl.portal.desktop.kde -windowrulev2 = size 60% 65%, class:org.freedesktop.impl.portal.desktop.kde -windowrulev2 = float, class:^(Zotero)$ -windowrulev2 = size 45%, class:^(Zotero)$ +windowrule = match:title ^(Open File)(.*)$, center on +windowrule = match:title ^(Open File)(.*)$, float on +windowrule = match:title ^(Select a File)(.*)$, center on +windowrule = match:title ^(Select a File)(.*)$, float on +windowrule = match:title ^(Choose wallpaper)(.*)$, center on +windowrule = match:title ^(Choose wallpaper)(.*)$, float on +windowrule = match:title ^(Choose wallpaper)(.*)$, size (monitor_w*.60) (monitor_h*.65) +windowrule = match:title ^(Open Folder)(.*)$, center on +windowrule = match:title ^(Open Folder)(.*)$, float on +windowrule = match:title ^(Save As)(.*)$, center on +windowrule = match:title ^(Save As)(.*)$, float on +windowrule = match:title ^(Library)(.*)$, center on +windowrule = match:title ^(Library)(.*)$, float on +windowrule = match:title ^(File Upload)(.*)$, center on +windowrule = match:title ^(File Upload)(.*)$, float on +windowrule = match:title ^(.*)(wants to save)$, center on +windowrule = match:title ^(.*)(wants to save)$, float on +windowrule = match:title ^(.*)(wants to open)$, center on +windowrule = match:title ^(.*)(wants to open)$, float on +windowrule = match:class ^(blueberry\.py)$, float on +windowrule = match:class ^(guifetch)$ , float on # FlafyDev/guifetch +windowrule = match:class ^(pavucontrol)$, float on +windowrule = match:class ^(pavucontrol)$, size (monitor_w*.45) (monitor_h*.45) +windowrule = match:class ^(pavucontrol)$, center on +windowrule = match:class ^(org.pulseaudio.pavucontrol)$, float on +windowrule = match:class ^(org.pulseaudio.pavucontrol)$, size (monitor_w*.45) (monitor_h*.45) +windowrule = match:class ^(org.pulseaudio.pavucontrol)$, center on +windowrule = match:class ^(nm-connection-editor)$, float on +windowrule = match:class ^(nm-connection-editor)$, size (monitor_w*.45) (monitor_h*.45) +windowrule = match:class ^(nm-connection-editor)$, center on +windowrule = match:class .*plasmawindowed.*, float on +windowrule = match:class kcm_.*, float on +windowrule = match:class .*bluedevilwizard, float on +windowrule = match:title .*Welcome, float on +windowrule = match:title ^(illogical-impulse Settings)$, float on +windowrule = match:title .*Shell conflicts.*, float on +windowrule = match:class org.freedesktop.impl.portal.desktop.kde, float on +windowrule = match:class org.freedesktop.impl.portal.desktop.kde, size (monitor_w*.60) (monitor_h*.65) +windowrule = match:class ^(Zotero)$, float on +windowrule = match:class ^(Zotero)$, size (monitor_w*.45) (monitor_h*.45) # Move # kde-material-you-colors spawns a window when changing dark/light theme. This is to make sure it doesn't interfere at all. -windowrulev2 = float, class:^(plasma-changeicons)$ -windowrulev2 = noinitialfocus, class:^(plasma-changeicons)$ -windowrulev2 = move 999999 999999, class:^(plasma-changeicons)$ +windowrule = match:class ^(plasma-changeicons)$, float on +windowrule = match:class ^(plasma-changeicons)$, no_initial_focus on +windowrule = match:class ^(plasma-changeicons)$, move 999999 999999 # stupid dolphin copy -windowrulev2 = move 40 80, title:^(Copying — Dolphin)$ +windowrule = match:title ^(Copying — Dolphin)$, move 40 80 # Tiling -windowrulev2 = tile, class:^dev\.warp\.Warp$ +windowrule = match:class ^dev\.warp\.Warp$, tile on # Picture-in-Picture -windowrulev2 = float, title:^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$ -windowrulev2 = keepaspectratio, title:^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$ -windowrulev2 = move 73% 72%, title:^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$ -windowrulev2 = size 25%, title:^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$ -windowrulev2 = float, title:^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$ -windowrulev2 = pin, title:^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$ +windowrule = match:title ^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$, float on +windowrule = match:title ^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$, keep_aspect_ratio on +windowrule = match:title ^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$, move (monitor_w*.73) (monitor_h*.72) +windowrule = match:title ^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$, size (monitor_w*.25) (monitor_h*.25) +windowrule = match:title ^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$, float on +windowrule = match:title ^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$, pin on # --- Tearing --- -windowrulev2 = immediate, title:.*\.exe -windowrulev2 = immediate, title:.*minecraft.* -windowrulev2 = immediate, class:^(steam_app).* +windowrule = match:title .*\.exe, immediate on +windowrule = match:title .*minecraft.*, immediate on +windowrule = match:class ^(steam_app).*, immediate on # Fix Jetbrain IDEs focus/rerendering problem -windowrulev2=noinitialfocus,class:^jetbrains-.*$,floating:1,title:^$|^\s$|^win\d+$ +windowrule = match:class ^jetbrains-.*$, match:float 1, match:title ^$|^\s$|^win\d+$, no_initial_focus on # No shadow for tiled windows (matches windows that are not floating). -windowrulev2 = noshadow, floating:0 +windowrule = match:float 0, no_shadow on # ######## Workspace rules ######## workspace = special:special, gapsout:30 # ######## Layer rules ######## -layerrule = xray 1, .* -# layerrule = noanim, .* -layerrule = noanim, walker -layerrule = noanim, selection -layerrule = noanim, overview -layerrule = noanim, anyrun -layerrule = noanim, indicator.* -layerrule = noanim, osk -layerrule = noanim, hyprpicker +layerrule = match:namespace .*, xray on +# layerrule = match:namespace .*, no_anim on +layerrule = match:namespace walker, no_anim on +layerrule = match:namespace selection, no_anim on +layerrule = match:namespace overview, no_anim on +layerrule = match:namespace anyrun, no_anim on +layerrule = match:namespace indicator.*, no_anim on +layerrule = match:namespace osk, no_anim on +layerrule = match:namespace hyprpicker, no_anim on -layerrule = noanim, noanim -layerrule = blur, gtk-layer-shell -layerrule = ignorezero, gtk-layer-shell -layerrule = blur, launcher -layerrule = ignorealpha 0.5, launcher -layerrule = blur, notifications -layerrule = ignorealpha 0.69, notifications -layerrule = blur, logout_dialog # wlogout +layerrule = match:namespace noanim, no_anim on +layerrule = match:namespace gtk-layer-shell, blur on +layerrule = match:namespace gtk-layer-shell, ignore_alpha 0 +layerrule = match:namespace launcher, blur on +layerrule = match:namespace launcher, ignore_alpha 0.5 +layerrule = match:namespace notifications, blur on +layerrule = match:namespace notifications, ignore_alpha 0.69 +layerrule = match:namespace logout_dialog # wlogout, blur on # ags -layerrule = animation slide left, sideleft.* -layerrule = animation slide right, sideright.* -layerrule = blur, session[0-9]* -layerrule = blur, bar[0-9]* -layerrule = ignorealpha 0.6, bar[0-9]* -layerrule = blur, barcorner.* -layerrule = ignorealpha 0.6, barcorner.* -layerrule = blur, dock[0-9]* -layerrule = ignorealpha 0.6, dock[0-9]* -layerrule = blur, indicator.* -layerrule = ignorealpha 0.6, indicator.* -layerrule = blur, overview[0-9]* -layerrule = ignorealpha 0.6, overview[0-9]* -layerrule = blur, cheatsheet[0-9]* -layerrule = ignorealpha 0.6, cheatsheet[0-9]* -layerrule = blur, sideright[0-9]* -layerrule = ignorealpha 0.6, sideright[0-9]* -layerrule = blur, sideleft[0-9]* -layerrule = ignorealpha 0.6, sideleft[0-9]* -layerrule = blur, indicator.* -layerrule = ignorealpha 0.6, indicator.* -layerrule = blur, osk[0-9]* -layerrule = ignorealpha 0.6, osk[0-9]* +layerrule = match:namespace sideleft.*, animation slide left +layerrule = match:namespace sideright.*, animation slide right +layerrule = match:namespace session[0-9]*, blur on +layerrule = match:namespace bar[0-9]*, blur on +layerrule = match:namespace bar[0-9]*, ignore_alpha 0.6 +layerrule = match:namespace barcorner.*, blur on +layerrule = match:namespace barcorner.*, ignore_alpha 0.6 +layerrule = match:namespace dock[0-9]*, blur on +layerrule = match:namespace dock[0-9]*, ignore_alpha 0.6 +layerrule = match:namespace indicator.*, blur on +layerrule = match:namespace indicator.*, ignore_alpha 0.6 +layerrule = match:namespace overview[0-9]*, blur on +layerrule = match:namespace overview[0-9]*, ignore_alpha 0.6 +layerrule = match:namespace cheatsheet[0-9]*, blur on +layerrule = match:namespace cheatsheet[0-9]*, ignore_alpha 0.6 +layerrule = match:namespace sideright[0-9]*, blur on +layerrule = match:namespace sideright[0-9]*, ignore_alpha 0.6 +layerrule = match:namespace sideleft[0-9]*, blur on +layerrule = match:namespace sideleft[0-9]*, ignore_alpha 0.6 +layerrule = match:namespace indicator.*, blur on +layerrule = match:namespace indicator.*, ignore_alpha 0.6 +layerrule = match:namespace osk[0-9]*, blur on +layerrule = match:namespace osk[0-9]*, ignore_alpha 0.6 # Quickshell -layerrule = blurpopups, quickshell:.* -layerrule = blur, quickshell:.* -layerrule = ignorealpha 0.79, quickshell:.* -layerrule = animation slide, quickshell:bar -layerrule = noanim, quickshell:actionCenter -layerrule = animation slide bottom, quickshell:cheatsheet -layerrule = animation slide bottom, quickshell:dock -layerrule = animation popin 120%, quickshell:screenCorners -layerrule = noanim, quickshell:lockWindowPusher -layerrule = animation fade, quickshell:notificationPopup -layerrule = noanim, quickshell:overlay -layerrule = ignorealpha 1, quickshell:overlay -layerrule = noanim, quickshell:overview -layerrule = animation slide bottom, quickshell:osk -layerrule = noanim, quickshell:polkit -layerrule = xray 0, quickshell:popup # No weird color for bar tooltips (this in theory should suffice) -layerrule = ignorealpha 1, quickshell:popup # No weird color for bar tooltips (but somehow this is necessary) -layerrule = ignorealpha 1, quickshell:mediaControls # Same as above -layerrule = animation slide, quickshell:reloadPopup -layerrule = noanim, quickshell:regionSelector -layerrule = noanim, quickshell:screenshot -layerrule = blur, quickshell:session -layerrule = noanim, quickshell:session -layerrule = ignorealpha 0, quickshell:session -layerrule = animation slide right, quickshell:sidebarRight -layerrule = animation slide left, quickshell:sidebarLeft -layerrule = animation slide, quickshell:verticalBar -layerrule = animation slide top, quickshell:wallpaperSelector -layerrule = noanim, quickshell:wNotificationCenter -layerrule = noanim, quickshell:wOnScreenDisplay -layerrule = noanim, quickshell:wStartMenu -layerrule = ignorealpha 0, quickshell:wTaskView -layerrule = noanim, quickshell:wTaskView +# Quickshell: illogical-impulse +layerrule = match:namespace quickshell:.*, blur_popups on +layerrule = match:namespace quickshell:.*, blur on +layerrule = match:namespace quickshell:.*, ignore_alpha 0.79 +layerrule = match:namespace quickshell:bar, animation slide +layerrule = match:namespace quickshell:actionCenter, no_anim on +layerrule = match:namespace quickshell:cheatsheet, animation slide bottom +layerrule = match:namespace quickshell:dock, animation slide bottom +layerrule = match:namespace quickshell:screenCorners, animation popin 120% +layerrule = match:namespace quickshell:lockWindowPusher, no_anim on +layerrule = match:namespace quickshell:notificationPopup, animation fade +layerrule = match:namespace quickshell:overlay, no_anim on +layerrule = match:namespace quickshell:overlay, ignore_alpha 1 +layerrule = match:namespace quickshell:overview, no_anim on +layerrule = match:namespace quickshell:osk, animation slide bottom +layerrule = match:namespace quickshell:polkit, no_anim on +layerrule = match:namespace quickshell:popup, xray off # No weird color for bar tooltips (this in theory should suffice) +layerrule = match:namespace quickshell:popup, ignore_alpha 1 # No weird color for bar tooltips (but somehow this is necessary) +layerrule = match:namespace quickshell:mediaControls, ignore_alpha 1 # Same as above +layerrule = match:namespace quickshell:reloadPopup, animation slide +layerrule = match:namespace quickshell:regionSelector, no_anim on +layerrule = match:namespace quickshell:screenshot, no_anim on +layerrule = match:namespace quickshell:session, blur on +layerrule = match:namespace quickshell:session, no_anim on +layerrule = match:namespace quickshell:session, ignore_alpha 0 +layerrule = match:namespace quickshell:sidebarRight, animation slide right +layerrule = match:namespace quickshell:sidebarLeft, animation slide left +layerrule = match:namespace quickshell:verticalBar, animation slide +layerrule = match:namespace quickshell:osk, order -1 +# Quickshell: waffles +layerrule = match:namespace quickshell:wallpaperSelector, animation slide top +layerrule = match:namespace quickshell:wNotificationCenter, no_anim on +layerrule = match:namespace quickshell:wOnScreenDisplay, no_anim on +layerrule = match:namespace quickshell:wStartMenu, no_anim on +layerrule = match:namespace quickshell:wTaskView, ignore_alpha 0 +layerrule = match:namespace quickshell:wTaskView, no_anim on # Launchers need to be FAST -layerrule = noanim, gtk4-layer-shell +layerrule = match:namespace gtk4-layer-shell, no_anim on diff --git a/dots/.config/matugen/templates/gtk-3.0/gtk.css b/dots/.config/matugen/templates/gtk-3.0/gtk.css index c0054f864..0d8cb9cbe 100644 --- a/dots/.config/matugen/templates/gtk-3.0/gtk.css +++ b/dots/.config/matugen/templates/gtk-3.0/gtk.css @@ -1,22 +1,38 @@ /* -* GTK Colors -* Generated with Matugen +* GTK colors generated with Matugen +* The source template is here: ~/.config/matugen/templates/gtk-3.0/gtk.css */ +/* Accents */ @define-color accent_color {{colors.primary.default.hex}}; @define-color accent_fg_color {{colors.on_primary.default.hex}}; @define-color accent_bg_color {{colors.primary.default.hex}}; +@define-color destructive_bg_color {{colors.error_container.default.hex}}; +@define-color destructive_fg_color {{colors.on_error_container.default.hex}}; +@define-color destructive_color {{colors.error.default.hex}}; +@define-color success_bg_color #374B3E; +@define-color success_fg_color #D1E9D6; +@define-color success_color #B5CCBA; +/* Base surfaces */ @define-color window_bg_color {{colors.background.default.hex}}; @define-color window_fg_color {{colors.on_background.default.hex}}; -@define-color headerbar_bg_color {{colors.surface_dim.default.hex}}; +@define-color headerbar_bg_color {{colors.surface_container.default.hex}}; +@define-color headerbar_backdrop_color {{colors.surface_container.default.hex}}; @define-color headerbar_fg_color {{colors.on_surface.default.hex}}; -@define-color popover_bg_color {{colors.surface_dim.default.hex}}; -@define-color popover_fg_color {{colors.on_surface.default.hex}}; -@define-color view_bg_color {{colors.surface.default.hex}}; -@define-color view_fg_color {{colors.on_surface.default.hex}}; -@define-color card_bg_color {{colors.surface.default.hex}}; +@define-color card_bg_color {{colors.surface_container.default.hex}}; @define-color card_fg_color {{colors.on_surface.default.hex}}; -@define-color sidebar_bg_color @window_bg_color; -@define-color sidebar_fg_color @window_fg_color; -@define-color sidebar_border_color @window_bg_color; -@define-color sidebar_backdrop_color @window_bg_color; +@define-color sidebar_bg_color {{colors.surface_container.default.hex}}; +@define-color sidebar_fg_color {{colors.on_surface.default.hex}}; +@define-color secondary_sidebar_bg_color {{colors.surface_container_low.default.hex}}; +@define-color secondary_sidebar_fg_color {{colors.on_surface.default.hex}}; +@define-color sidebar_border_color @sidebar_bg_color; +@define-color sidebar_backdrop_color @sidebar_bg_color; +@define-color view_bg_color {{colors.surface_container_lowest.default.hex}}; +@define-color view_fg_color {{colors.on_surface.default.hex}}; +@define-color overview_bg_color {{colors.surface_container_lowest.default.hex}}; +@define-color overview_fg_color {{colors.on_surface.default.hex}}; +/* Popups */ +@define-color popover_bg_color {{colors.surface_container_highest.default.hex}}; +@define-color popover_fg_color {{colors.on_surface.default.hex}}; +@define-color dialog_bg_color {{colors.surface_container_high.default.hex}}; +@define-color dialog_fg_color {{colors.on_surface.default.hex}}; diff --git a/dots/.config/matugen/templates/gtk-4.0/gtk.css b/dots/.config/matugen/templates/gtk-4.0/gtk.css index 80b1d6975..07c8099f5 100644 --- a/dots/.config/matugen/templates/gtk-4.0/gtk.css +++ b/dots/.config/matugen/templates/gtk-4.0/gtk.css @@ -1,44 +1,83 @@ /* -* GTK Colors -* Generated with Matugen +* GTK colors generated with Matugen +* The source template is here: ~/.config/matugen/templates/gtk-4.0/gtk.css */ @media (prefers-color-scheme: light) { + /* Accents */ @define-color accent_color {{colors.primary.light.hex}}; @define-color accent_fg_color {{colors.on_primary.light.hex}}; @define-color accent_bg_color {{colors.primary.light.hex}}; + @define-color destructive_bg_color {{colors.error_container.light.hex}}; + @define-color destructive_fg_color {{colors.on_error_container.light.hex}}; + @define-color destructive_color {{colors.error.light.hex}}; + @define-color success_bg_color #B5CCBA; + @define-color success_fg_color #213528; + @define-color success_color #374B3E; + /* Base surfaces */ @define-color window_bg_color {{colors.background.light.hex}}; @define-color window_fg_color {{colors.on_background.light.hex}}; - @define-color headerbar_bg_color {{colors.surface_dim.light.hex}}; + @define-color headerbar_bg_color {{colors.surface_container.light.hex}}; + @define-color headerbar_backdrop_color {{colors.surface_container.light.hex}}; @define-color headerbar_fg_color {{colors.on_surface.light.hex}}; - @define-color popover_bg_color {{colors.surface_dim.light.hex}}; - @define-color popover_fg_color {{colors.on_surface.light.hex}}; - @define-color view_bg_color {{colors.surface.light.hex}}; - @define-color view_fg_color {{colors.on_surface.light.hex}}; - @define-color card_bg_color {{colors.surface.light.hex}}; + @define-color card_bg_color {{colors.surface_container.light.hex}}; @define-color card_fg_color {{colors.on_surface.light.hex}}; - @define-color sidebar_bg_color @window_bg_color; - @define-color sidebar_fg_color @window_fg_color; - @define-color sidebar_border_color @window_bg_color; - @define-color sidebar_backdrop_color @window_bg_color; + @define-color sidebar_bg_color {{colors.surface_container.light.hex}}; + @define-color sidebar_fg_color {{colors.on_surface.light.hex}}; + @define-color secondary_sidebar_bg_color {{colors.surface_container_low.light.hex}}; + @define-color secondary_sidebar_backdrop_color {{colors.surface_container_low.light.hex}}; + @define-color secondary_sidebar_fg_color {{colors.on_surface.light.hex}}; + @define-color sidebar_border_color @sidebar_bg_color; + @define-color sidebar_backdrop_color @sidebar_bg_color; + @define-color view_bg_color {{colors.surface_container_lowest.light.hex}}; + @define-color view_fg_color {{colors.on_surface.light.hex}}; + @define-color overview_bg_color {{colors.surface_container_lowest.light.hex}}; + @define-color overview_fg_color {{colors.on_surface.light.hex}}; + /* Popups */ + @define-color popover_bg_color {{colors.surface_container_highest.light.hex}}; + @define-color popover_fg_color {{colors.on_surface.light.hex}}; + @define-color dialog_bg_color {{colors.surface_container_high.light.hex}}; + @define-color dialog_fg_color {{colors.on_surface.light.hex}}; + @define-color thumbnail_bg_color {{colors.surface_container_high.light.hex}}; + @define-color thumbnail_fg_color {{colors.on_surface.light.hex}}; } @media (prefers-color-scheme: dark) { + + /* Accents */ @define-color accent_color {{colors.primary.dark.hex}}; @define-color accent_fg_color {{colors.on_primary.dark.hex}}; @define-color accent_bg_color {{colors.primary.dark.hex}}; + @define-color destructive_bg_color {{colors.error_container.dark.hex}}; + @define-color destructive_fg_color {{colors.on_error_container.dark.hex}}; + @define-color destructive_color {{colors.error.dark.hex}}; + @define-color success_bg_color #374B3E; + @define-color success_fg_color #D1E9D6; + @define-color success_color #B5CCBA; + /* Base surfaces */ @define-color window_bg_color {{colors.background.dark.hex}}; @define-color window_fg_color {{colors.on_background.dark.hex}}; - @define-color headerbar_bg_color {{colors.surface_dim.dark.hex}}; + @define-color headerbar_bg_color {{colors.surface_container.dark.hex}}; + @define-color headerbar_backdrop_color {{colors.surface_container.dark.hex}}; @define-color headerbar_fg_color {{colors.on_surface.dark.hex}}; - @define-color popover_bg_color {{colors.surface_dim.dark.hex}}; - @define-color popover_fg_color {{colors.on_surface.dark.hex}}; - @define-color view_bg_color {{colors.surface.dark.hex}}; - @define-color view_fg_color {{colors.on_surface.dark.hex}}; - @define-color card_bg_color {{colors.surface.dark.hex}}; + @define-color card_bg_color {{colors.surface_container.dark.hex}}; @define-color card_fg_color {{colors.on_surface.dark.hex}}; - @define-color sidebar_bg_color @window_bg_color; - @define-color sidebar_fg_color @window_fg_color; - @define-color sidebar_border_color @window_bg_color; - @define-color sidebar_backdrop_color @window_bg_color; + @define-color sidebar_bg_color {{colors.surface_container.dark.hex}}; + @define-color sidebar_fg_color {{colors.on_surface.dark.hex}}; + @define-color secondary_sidebar_bg_color {{colors.surface_container_low.dark.hex}}; + @define-color secondary_sidebar_backdrop_color {{colors.surface_container_low.dark.hex}}; + @define-color secondary_sidebar_fg_color {{colors.on_surface.dark.hex}}; + @define-color sidebar_border_color @sidebar_bg_color; + @define-color sidebar_backdrop_color @sidebar_bg_color; + @define-color view_bg_color {{colors.surface_container_lowest.dark.hex}}; + @define-color view_fg_color {{colors.on_surface.dark.hex}}; + @define-color overview_bg_color {{colors.surface_container_lowest.dark.hex}}; + @define-color overview_fg_color {{colors.on_surface.dark.hex}}; + /* Popups */ + @define-color popover_bg_color {{colors.surface_container_highest.dark.hex}}; + @define-color popover_fg_color {{colors.on_surface.dark.hex}}; + @define-color dialog_bg_color {{colors.surface_container_high.dark.hex}}; + @define-color dialog_fg_color {{colors.on_surface.dark.hex}}; + @define-color thumbnail_bg_color {{colors.surface_container_high.dark.hex}}; + @define-color thumbnail_fg_color {{colors.on_surface.dark.hex}}; } diff --git a/dots/.config/matugen/templates/hyprland/colors.conf b/dots/.config/matugen/templates/hyprland/colors.conf index d0002e9e7..6de98514f 100644 --- a/dots/.config/matugen/templates/hyprland/colors.conf +++ b/dots/.config/matugen/templates/hyprland/colors.conf @@ -1,6 +1,6 @@ general { - col.active_border = rgba({{colors.outline.default.hex_stripped}}AA) - col.inactive_border = rgba({{colors.outline_variant.default.hex_stripped}}AA) + col.active_border = rgba({{colors.outline.default.hex_stripped}}77) + col.inactive_border = rgba({{colors.outline_variant.default.hex_stripped}}55) } misc { @@ -29,4 +29,4 @@ plugin { } } -windowrulev2 = bordercolor rgba({{colors.primary.default.hex_stripped}}AA) rgba({{colors.primary.default.hex_stripped}}77),pinned:1 +windowrule = border_color rgba({{colors.primary.default.hex_stripped}}AA) rgba({{colors.primary.default.hex_stripped}}77), match:pin 1 diff --git a/dots/.config/quickshell/ii/GlobalStates.qml b/dots/.config/quickshell/ii/GlobalStates.qml index 85a0414d6..ba680220b 100644 --- a/dots/.config/quickshell/ii/GlobalStates.qml +++ b/dots/.config/quickshell/ii/GlobalStates.qml @@ -37,14 +37,6 @@ Singleton { } } - property real screenZoom: 1 - onScreenZoomChanged: { - Quickshell.execDetached(["hyprctl", "keyword", "cursor:zoom_factor", root.screenZoom.toString()]); - } - Behavior on screenZoom { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) - } - GlobalShortcut { name: "workspaceNumber" description: "Hold to show workspace numbers, release to show icons" @@ -56,16 +48,4 @@ Singleton { root.superDown = false } } - - IpcHandler { - target: "zoom" - - function zoomIn() { - screenZoom = Math.min(screenZoom + 0.4, 3.0) - } - - function zoomOut() { - screenZoom = Math.max(screenZoom - 0.4, 1) - } - } } \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/calendar-add-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/calendar-add-filled.svg new file mode 100644 index 000000000..9b12e58e6 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/calendar-add-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/calendar-add.svg b/dots/.config/quickshell/ii/assets/icons/fluent/calendar-add.svg new file mode 100644 index 000000000..fe0ff7263 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/calendar-add.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/camera-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/camera-filled.svg new file mode 100644 index 000000000..643964740 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/camera-filled.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/camera.svg b/dots/.config/quickshell/ii/assets/icons/fluent/camera.svg new file mode 100644 index 000000000..40fa6d1f2 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/camera.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/crop-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/crop-filled.svg new file mode 100644 index 000000000..f86a4e42c --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/crop-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/crop.svg b/dots/.config/quickshell/ii/assets/icons/fluent/crop.svg new file mode 100644 index 000000000..1447e7c1f --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/crop.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/image-edit-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/image-edit-filled.svg new file mode 100644 index 000000000..661466195 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/image-edit-filled.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/image-edit.svg b/dots/.config/quickshell/ii/assets/icons/fluent/image-edit.svg new file mode 100644 index 000000000..6f751b3ab --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/image-edit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/scan-text-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/scan-text-filled.svg new file mode 100644 index 000000000..3a8c786b8 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/scan-text-filled.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/scan-text.svg b/dots/.config/quickshell/ii/assets/icons/fluent/scan-text.svg new file mode 100644 index 000000000..beaf70538 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/scan-text.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/search-visual-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/search-visual-filled.svg new file mode 100644 index 000000000..1ab1d25b0 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/search-visual-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/search-visual.svg b/dots/.config/quickshell/ii/assets/icons/fluent/search-visual.svg new file mode 100644 index 000000000..167f228d0 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/search-visual.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/video-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/video-filled.svg new file mode 100644 index 000000000..b3d6843ea --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/video-filled.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/video.svg b/dots/.config/quickshell/ii/assets/icons/fluent/video.svg new file mode 100644 index 000000000..021cbd863 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/video.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/modules/common/Appearance.qml b/dots/.config/quickshell/ii/modules/common/Appearance.qml index 9c5045014..d64253476 100644 --- a/dots/.config/quickshell/ii/modules/common/Appearance.qml +++ b/dots/.config/quickshell/ii/modules/common/Appearance.qml @@ -28,15 +28,11 @@ Singleton { property real autoBackgroundTransparency: { // y = 0.5768x^2 - 0.759x + 0.2896 let x = wallpaperVibrancy let y = 0.5768 * (x * x) - 0.759 * (x) + 0.2896 - return Math.max(0, Math.min(0.22, y)) - } - property real autoContentTransparency: { // y = -10.1734x^2 + 3.4457x + 0.1872 - let x = autoBackgroundTransparency - let y = -10.1734 * (x * x) + 3.4457 * (x) + 0.1872 - return Math.max(0, Math.min(0.6, y)) + return Math.max(0, Math.min(0.22, y)) - 0.12 * (m3colors.darkmode ? 0 : 1) } + property real autoContentTransparency: 0.9 property real backgroundTransparency: Config?.options.appearance.transparency.enable ? Config?.options.appearance.transparency.automatic ? autoBackgroundTransparency : Config?.options.appearance.transparency.backgroundTransparency : 0 - property real contentTransparency: Config?.options.appearance.transparency.enable ? Config?.options.appearance.transparency.automatic ? autoContentTransparency : Config?.options.appearance.transparency.contentTransparency : 0 + property real contentTransparency: Config?.options.appearance.transparency.automatic ? autoContentTransparency : Config?.options.appearance.transparency.contentTransparency m3colors: QtObject { property bool darkmode: true @@ -114,30 +110,41 @@ Singleton { colors: QtObject { property color colSubtext: m3colors.m3outline - property color colLayer0: ColorUtils.mix(ColorUtils.transparentize(m3colors.m3background, root.backgroundTransparency), m3colors.m3primary, Config.options.appearance.extraBackgroundTint ? 0.99 : 1) + // Layer 0 + property color colLayer0Base: ColorUtils.mix(m3colors.m3background, m3colors.m3primary, Config.options.appearance.extraBackgroundTint ? 0.99 : 1) + property color colLayer0: ColorUtils.transparentize(colLayer0Base, root.backgroundTransparency) property color colOnLayer0: m3colors.m3onBackground property color colLayer0Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer0, colOnLayer0, 0.9, root.contentTransparency)) property color colLayer0Active: ColorUtils.transparentize(ColorUtils.mix(colLayer0, colOnLayer0, 0.8, root.contentTransparency)) property color colLayer0Border: ColorUtils.mix(root.m3colors.m3outlineVariant, colLayer0, 0.4) - property color colLayer1: ColorUtils.transparentize(m3colors.m3surfaceContainerLow, root.contentTransparency); + // Layer 1 + property color colLayer1Base: m3colors.m3surfaceContainerLow + property color colLayer1: ColorUtils.solveOverlayColor(colLayer0Base, colLayer1Base, 1 - root.contentTransparency); property color colOnLayer1: m3colors.m3onSurfaceVariant; property color colOnLayer1Inactive: ColorUtils.mix(colOnLayer1, colLayer1, 0.45); - property color colLayer2: ColorUtils.transparentize(m3colors.m3surfaceContainer, root.contentTransparency) - property color colOnLayer2: m3colors.m3onSurface; - property color colOnLayer2Disabled: ColorUtils.mix(colOnLayer2, m3colors.m3background, 0.4); property color colLayer1Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer1, colOnLayer1, 0.92), root.contentTransparency) property color colLayer1Active: ColorUtils.transparentize(ColorUtils.mix(colLayer1, colOnLayer1, 0.85), root.contentTransparency); - property color colLayer2Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer2, colOnLayer2, 0.90), root.contentTransparency) - property color colLayer2Active: ColorUtils.transparentize(ColorUtils.mix(colLayer2, colOnLayer2, 0.80), root.contentTransparency); - property color colLayer2Disabled: ColorUtils.transparentize(ColorUtils.mix(colLayer2, m3colors.m3background, 0.8), root.contentTransparency); - property color colLayer3: ColorUtils.transparentize(m3colors.m3surfaceContainerHigh, root.contentTransparency) + // Layer 2 + property color colLayer2Base: m3colors.m3surfaceContainer + property color colLayer2: ColorUtils.solveOverlayColor(colLayer1Base, colLayer2Base, 1 - root.contentTransparency) + property color colLayer2Hover: ColorUtils.solveOverlayColor(colLayer1Base, ColorUtils.mix(colLayer2Base, colOnLayer2, 0.90), 1 - root.contentTransparency) + property color colLayer2Active: ColorUtils.solveOverlayColor(colLayer1Base, ColorUtils.mix(colLayer2Base, colOnLayer2, 0.80), 1 - root.contentTransparency); + property color colLayer2Disabled: ColorUtils.solveOverlayColor(colLayer1Base, ColorUtils.mix(colLayer2Base, m3colors.m3background, 0.8), 1 - root.contentTransparency); + property color colOnLayer2: m3colors.m3onSurface; + property color colOnLayer2Disabled: ColorUtils.mix(colOnLayer2, m3colors.m3background, 0.4); + // Layer 3 + property color colLayer3Base: m3colors.m3surfaceContainerHigh + property color colLayer3: ColorUtils.solveOverlayColor(colLayer2Base, colLayer3Base, 1 - root.contentTransparency) + property color colLayer3Hover: ColorUtils.solveOverlayColor(colLayer2Base, ColorUtils.mix(colLayer3Base, colOnLayer3, 0.90), 1 - root.contentTransparency) + property color colLayer3Active: ColorUtils.solveOverlayColor(colLayer2Base, ColorUtils.mix(colLayer3Base, colOnLayer3, 0.80), 1 - root.contentTransparency); property color colOnLayer3: m3colors.m3onSurface; - property color colLayer3Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer3, colOnLayer3, 0.90), root.contentTransparency) - property color colLayer3Active: ColorUtils.transparentize(ColorUtils.mix(colLayer3, colOnLayer3, 0.80), root.contentTransparency); - property color colLayer4: ColorUtils.transparentize(m3colors.m3surfaceContainerHighest, root.contentTransparency) + // Layer 4 + property color colLayer4Base: m3colors.m3surfaceContainerHighest + property color colLayer4: ColorUtils.solveOverlayColor(colLayer3Base, colLayer4Base, 1 - root.contentTransparency) + property color colLayer4Hover: ColorUtils.solveOverlayColor(colLayer3Base, ColorUtils.mix(colLayer4Base, colOnLayer4, 0.90), 1 - root.contentTransparency) + property color colLayer4Active: ColorUtils.solveOverlayColor(colLayer3Base, ColorUtils.mix(colLayer4Base, colOnLayer4, 0.80), 1 - root.contentTransparency); property color colOnLayer4: m3colors.m3onSurface; - property color colLayer4Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer4, colOnLayer4, 0.90), root.contentTransparency) - property color colLayer4Active: ColorUtils.transparentize(ColorUtils.mix(colLayer4, colOnLayer4, 0.80), root.contentTransparency); + // Primary property color colPrimary: m3colors.m3primary property color colOnPrimary: m3colors.m3onPrimary property color colPrimaryHover: ColorUtils.mix(colors.colPrimary, colLayer1Hover, 0.87) @@ -146,13 +153,16 @@ Singleton { property color colPrimaryContainerHover: ColorUtils.mix(colors.colPrimaryContainer, colors.colOnPrimaryContainer, 0.9) property color colPrimaryContainerActive: ColorUtils.mix(colors.colPrimaryContainer, colors.colOnPrimaryContainer, 0.8) property color colOnPrimaryContainer: m3colors.m3onPrimaryContainer + // Secondary property color colSecondary: m3colors.m3secondary - property color colOnSecondary: m3colors.m3onSecondary property color colSecondaryHover: ColorUtils.mix(m3colors.m3secondary, colLayer1Hover, 0.85) property color colSecondaryActive: ColorUtils.mix(m3colors.m3secondary, colLayer1Active, 0.4) + property color colOnSecondary: m3colors.m3onSecondary property color colSecondaryContainer: m3colors.m3secondaryContainer property color colSecondaryContainerHover: ColorUtils.mix(m3colors.m3secondaryContainer, m3colors.m3onSecondaryContainer, 0.90) property color colSecondaryContainerActive: ColorUtils.mix(m3colors.m3secondaryContainer, m3colors.m3onSecondaryContainer, 0.54) + property color colOnSecondaryContainer: m3colors.m3onSecondaryContainer + // Tertiary property color colTertiary: m3colors.m3tertiary property color colTertiaryHover: ColorUtils.mix(m3colors.m3tertiary, colLayer1Hover, 0.85) property color colTertiaryActive: ColorUtils.mix(m3colors.m3tertiary, colLayer1Active, 0.4) @@ -161,16 +171,17 @@ Singleton { property color colTertiaryContainerActive: ColorUtils.mix(m3colors.m3tertiaryContainer, colLayer1Active, 0.54) property color colOnTertiary: m3colors.m3onTertiary property color colOnTertiaryContainer: m3colors.m3onTertiaryContainer - property color colOnSecondaryContainer: m3colors.m3onSecondaryContainer - property color colSurfaceContainerLow: ColorUtils.transparentize(m3colors.m3surfaceContainerLow, root.contentTransparency) - property color colSurfaceContainer: ColorUtils.transparentize(m3colors.m3surfaceContainer, root.contentTransparency) + // Surface property color colBackgroundSurfaceContainer: ColorUtils.transparentize(m3colors.m3surfaceContainer, root.backgroundTransparency) - property color colSurfaceContainerHigh: ColorUtils.transparentize(m3colors.m3surfaceContainerHigh, root.contentTransparency) - property color colSurfaceContainerHighest: ColorUtils.transparentize(m3colors.m3surfaceContainerHighest, root.contentTransparency) + property color colSurfaceContainerLow: ColorUtils.solveOverlayColor(m3colors.m3background, m3colors.m3surfaceContainerLow, 1 - root.contentTransparency) + property color colSurfaceContainer: ColorUtils.solveOverlayColor(m3colors.m3surfaceContainerLow, m3colors.m3surfaceContainer, 1 - root.contentTransparency) + property color colSurfaceContainerHigh: ColorUtils.solveOverlayColor(m3colors.m3surfaceContainer, m3colors.m3surfaceContainerHigh, 1 - root.contentTransparency) + property color colSurfaceContainerHighest: ColorUtils.solveOverlayColor(m3colors.m3surfaceContainerHigh, m3colors.m3surfaceContainerHighest, 1 - root.contentTransparency) property color colSurfaceContainerHighestHover: ColorUtils.mix(m3colors.m3surfaceContainerHighest, m3colors.m3onSurface, 0.95) property color colSurfaceContainerHighestActive: ColorUtils.mix(m3colors.m3surfaceContainerHighest, m3colors.m3onSurface, 0.85) property color colOnSurface: m3colors.m3onSurface property color colOnSurfaceVariant: m3colors.m3onSurfaceVariant + // Misc property color colTooltip: m3colors.m3inverseSurface property color colOnTooltip: m3colors.m3inverseOnSurface property color colScrim: ColorUtils.transparentize(m3colors.m3scrim, 0.5) diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 4fed1b6fb..2a9184bb3 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -78,10 +78,7 @@ Singleton { JsonAdapter { id: configOptionsJsonAdapter - property list enabledPanels: [ - "iiBar", "iiBackground", "iiCheatsheet", "iiDock", "iiLock", "iiMediaControls", "iiNotificationPopup", "iiOnScreenDisplay", "iiOnScreenKeyboard", "iiOverlay", "iiOverview", "iiPolkit", "iiRegionSelector", "iiReloadPopup", "iiScreenCorners", "iiSessionScreen", "iiSidebarLeft", "iiSidebarRight", "iiVerticalBar", "iiWallpaperSelector" - ] - property string panelFamily: "ii" // "ii", "w" + property string panelFamily: "ii" // "ii", "waffle" property JsonObject policies: JsonObject { property int ai: 1 // 0: No | 1: Yes | 2: Local @@ -189,7 +186,17 @@ Singleton { property bool useSineCookie: false } property JsonObject digital: JsonObject { + property bool adaptiveAlignment: true + property bool showDate: true property bool animateChange: true + property bool vertical: false + property JsonObject font: JsonObject { + property string family: "Google Sans Flex" + property real weight: 350 + property real width: 100 + property real size: 90 + property real roundness: 0 + } } property JsonObject quote: JsonObject { property bool enable: false @@ -566,6 +573,7 @@ Singleton { } property JsonObject updates: JsonObject { + property bool enableCheck: true property int checkInterval: 120 // minutes property int adviseUpdateThreshold: 75 // packages property int stronglyAdviseUpdateThreshold: 200 // packages diff --git a/dots/.config/quickshell/ii/modules/common/Persistent.qml b/dots/.config/quickshell/ii/modules/common/Persistent.qml index 247884028..8367692e3 100644 --- a/dots/.config/quickshell/ii/modules/common/Persistent.qml +++ b/dots/.config/quickshell/ii/modules/common/Persistent.qml @@ -59,7 +59,7 @@ Singleton { property string hyprlandInstanceSignature: "" property JsonObject ai: JsonObject { - property string model + property string model: "gemini-2.5-flash" property real temperature: 0.5 } diff --git a/dots/.config/quickshell/ii/modules/common/functions/ColorUtils.qml b/dots/.config/quickshell/ii/modules/common/functions/ColorUtils.qml index 165f27754..74305c8fa 100644 --- a/dots/.config/quickshell/ii/modules/common/functions/ColorUtils.qml +++ b/dots/.config/quickshell/ii/modules/common/functions/ColorUtils.qml @@ -135,4 +135,38 @@ Singleton { var c = Qt.color(color); return c.hslLightness < 0.5; } + + /** + * Clamps a value to the inclusive range [0, 1]. + * + * @param {number} x - The value to clamp. + * @returns {number} The clamped value in the range [0, 1]. + */ + function clamp01(x) { + return Math.min(1, Math.max(0, x)); + } + + /** + * Solves for the solid overlay color that, when composited over a base color + * with a given opacity, yields the target color. + * + * The compositing equation is: + * result = overlay * overlayOpacity + base * (1 - overlayOpacity) + * + * This function algebraically inverts that equation per channel. + * + * @param {Qt.rgba} baseColor - The base (background) color. + * @param {Qt.rgba} targetColor - The resulting color after compositing. + * @param {number} overlayOpacity - The overlay opacity (0-1). + * @returns {Qt.rgba} The solved overlay color + */ + function solveOverlayColor(baseColor, targetColor, overlayOpacity) { + let invA = 1.0 - overlayOpacity; + + let r = (targetColor.r - baseColor.r * invA) / overlayOpacity; + let g = (targetColor.g - baseColor.g * invA) / overlayOpacity; + let b = (targetColor.b - baseColor.b * invA) / overlayOpacity; + + return Qt.rgba(clamp01(r), clamp01(g), clamp01(b), overlayOpacity); + } } diff --git a/dots/.config/quickshell/ii/modules/common/panels/lock/LockScreen.qml b/dots/.config/quickshell/ii/modules/common/panels/lock/LockScreen.qml index 9e4b9bd94..5a1b24d7e 100644 --- a/dots/.config/quickshell/ii/modules/common/panels/lock/LockScreen.qml +++ b/dots/.config/quickshell/ii/modules/common/panels/lock/LockScreen.qml @@ -92,7 +92,6 @@ Scope { WlSessionLock { id: lock locked: GlobalStates.screenLocked - surface: root.sessionLockSurface } diff --git a/dots/.config/quickshell/ii/modules/common/utils/ScreenshotAction.qml b/dots/.config/quickshell/ii/modules/common/utils/ScreenshotAction.qml new file mode 100644 index 000000000..831834bd5 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/utils/ScreenshotAction.qml @@ -0,0 +1,81 @@ +pragma ComponentBehavior: Bound +pragma Singleton +import qs.modules.common +import qs.modules.common.utils +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.services +import QtQuick +import QtQuick.Controls +import Qt.labs.synchronizer +import Quickshell + +Singleton { + id: root + + enum Action { + Copy, + Edit, + Search, + CharRecognition, + Record, + RecordWithSound + } + + property string imageSearchEngineBaseUrl: Config.options.search.imageSearch.imageSearchEngineBaseUrl + property string fileUploadApiEndpoint: "https://uguu.se/upload" + + function getCommand(x, y, width, height, screenshotPath, action, saveDir = "") { + // Set command for action + const rx = Math.round(x); + const ry = Math.round(y); + const rw = Math.round(width); + const rh = Math.round(height); + const cropBase = `magick ${StringUtils.shellSingleQuoteEscape(screenshotPath)} ` + + `-crop ${rw}x${rh}+${rx}+${ry}` + const cropToStdout = `${cropBase} -` + const cropInPlace = `${cropBase} '${StringUtils.shellSingleQuoteEscape(screenshotPath)}'` + const cleanup = `rm '${StringUtils.shellSingleQuoteEscape(screenshotPath)}'` + const slurpRegion = `${rx},${ry} ${rw}x${rh}` + const uploadAndGetUrl = (filePath) => { + return `curl -sF files[]=@'${StringUtils.shellSingleQuoteEscape(filePath)}' ${root.fileUploadApiEndpoint} | jq -r '.files[0].url'` + } + const annotationCommand = `${Config.options.regionSelector.annotation.useSatty ? "satty" : "swappy"} -f -`; + switch (action) { + case ScreenshotAction.Action.Copy: + if (saveDir === "") { + // not saving the screenshot, just copy to clipboard + return ["bash", "-c", `${cropToStdout} | wl-copy && ${cleanup}`] + break; + } + return [ + "bash", "-c", + `mkdir -p '${StringUtils.shellSingleQuoteEscape(saveDir)}' && \ + saveFileName="screenshot-$(date '+%Y-%m-%d_%H.%M.%S').png" && \ + savePath="${saveDir}/$saveFileName" && \ + ${cropToStdout} | tee >(wl-copy) > "$savePath" && \ + ${cleanup}` + ] + + break; + case ScreenshotAction.Action.Edit: + return ["bash", "-c", `${cropToStdout} | ${annotationCommand} && ${cleanup}`] + break; + case ScreenshotAction.Action.Search: + return ["bash", "-c", `${cropInPlace} && xdg-open "${root.imageSearchEngineBaseUrl}$(${uploadAndGetUrl(screenshotPath)})" && ${cleanup}`] + break; + case ScreenshotAction.Action.CharRecognition: + return ["bash", "-c", `${cropInPlace} && tesseract '${StringUtils.shellSingleQuoteEscape(screenshotPath)}' stdout -l $(tesseract --list-langs | awk 'NR>1{print $1}' | tr '\\n' '+' | sed 's/\\+$/\\n/') | wl-copy && ${cleanup}`] + break; + case ScreenshotAction.Action.Record: + return ["bash", "-c", `${Directories.recordScriptPath} --region '${slurpRegion}'`] + break; + case ScreenshotAction.Action.RecordWithSound: + return ["bash", "-c", `${Directories.recordScriptPath} --region '${slurpRegion}' --sound`] + break; + default: + console.warn("[Region Selector] Unknown snip action, skipping snip."); + return; + } + } +} diff --git a/dots/.config/quickshell/ii/modules/common/utils/TempScreenshotProcess.qml b/dots/.config/quickshell/ii/modules/common/utils/TempScreenshotProcess.qml new file mode 100644 index 000000000..40c40fb34 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/utils/TempScreenshotProcess.qml @@ -0,0 +1,14 @@ +import QtQuick +import Quickshell +import Quickshell.Io +import qs.modules.common +import qs.modules.common.functions + +Process { + id: screenshotProc + running: true + property string screenshotDir: Directories.screenshotTemp + required property ShellScreen screen + property string screenshotPath: `${screenshotDir}/image-${screen.name}` + command: ["bash", "-c", `mkdir -p '${StringUtils.shellSingleQuoteEscape(screenshotDir)}' && grim -o '${StringUtils.shellSingleQuoteEscape(screen.name)}' '${StringUtils.shellSingleQuoteEscape(screenshotPath)}'`] +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/ConfigSlider.qml b/dots/.config/quickshell/ii/modules/common/widgets/ConfigSlider.qml new file mode 100644 index 000000000..edc63191a --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/ConfigSlider.qml @@ -0,0 +1,47 @@ +import qs.modules.common.widgets +import qs.modules.common +import QtQuick +import QtQuick.Layouts +import qs.services + +RowLayout { + id: root + spacing: 10 + Layout.leftMargin: 8 + Layout.rightMargin: 8 + + property string text: "" + property string buttonIcon: "" + property alias value: slider.value + property alias stopIndicatorValues: slider.stopIndicatorValues + property bool usePercentTooltip: true + property real from: slider.from + property real to: slider.to + property real textWidth: 120 + + RowLayout { + id: row + spacing: 10 + + OptionalMaterialSymbol { + id: iconWidget + icon: root.buttonIcon + iconSize: Appearance.font.pixelSize.larger + } + StyledText { + id: labelWidget + Layout.preferredWidth: root.textWidth + text: root.text + color: Appearance.colors.colOnSecondaryContainer + } + } + + StyledSlider { + id: slider + configuration: StyledSlider.Configuration.XS + usePercentTooltip: root.usePercentTooltip + value: root.value + from: root.from + to: root.to + } +} \ No newline at end of file diff --git a/dots/.config/quickshell/ii/modules/common/widgets/DashedBorder.qml b/dots/.config/quickshell/ii/modules/common/widgets/DashedBorder.qml new file mode 100644 index 000000000..1d992ffab --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/DashedBorder.qml @@ -0,0 +1,28 @@ +import QtQuick +import qs.modules.common +import qs.modules.common.functions + +Canvas { + id: root + property color color: "#ffffff" + property int dashLength: 6 + property int gapLength: 4 + property int borderWidth: 1 + + onDashLengthChanged: requestPaint() + onGapLengthChanged: requestPaint() + onWidthChanged: requestPaint() + onHeightChanged: requestPaint() + onPaint: { + var ctx = getContext("2d"); + ctx.clearRect(0, 0, width, height); + ctx.save(); + ctx.strokeStyle = root.color; + ctx.lineWidth = root.borderWidth; + if (root.gapLength > 0) { + ctx.setLineDash([root.dashLength, root.gapLength]); // Set dash pattern + } + ctx.strokeRect(root.borderWidth / 2, root.borderWidth / 2, width - root.borderWidth, height - root.borderWidth); // Draw it + ctx.restore(); + } +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/DragManager.qml b/dots/.config/quickshell/ii/modules/common/widgets/DragManager.qml index 9a430d93b..4d6225400 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/DragManager.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/DragManager.qml @@ -14,12 +14,16 @@ MouseArea { property bool automaticallyReset: true readonly property real dragDiffX: _dragDiffX readonly property real dragDiffY: _dragDiffY + property real startX: 0 + property real startY: 0 + property real regionTopLeftX: Math.min(startX, startX + _dragDiffX) + property real regionTopLeftY: Math.min(startY, startY + _dragDiffY) + property real regionWidth: Math.abs(_dragDiffX) + property real regionHeight: Math.abs(_dragDiffY) signal dragPressed(diffX: real, diffY: real) signal dragReleased(diffX: real, diffY: real) - property real startX: 0 - property real startY: 0 property bool dragging: false property real _dragDiffX: 0 property real _dragDiffY: 0 diff --git a/dots/.config/quickshell/ii/modules/common/widgets/NotificationGroup.qml b/dots/.config/quickshell/ii/modules/common/widgets/NotificationGroup.qml index fc612dcb1..e3d64027b 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/NotificationGroup.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/NotificationGroup.qml @@ -121,7 +121,7 @@ MouseArea { // Notification group area id: background anchors.left: parent.left width: parent.width - color: popup ? ColorUtils.applyAlpha(Appearance.colors.colLayer2, 1 - Appearance.backgroundTransparency) : Appearance.colors.colLayer2 + color: popup ? Appearance.colors.colBackgroundSurfaceContainer : Appearance.colors.colLayer2 radius: Appearance.rounding.normal anchors.leftMargin: root.xOffset diff --git a/dots/.config/quickshell/ii/modules/common/widgets/SelectionDialog.qml b/dots/.config/quickshell/ii/modules/common/widgets/SelectionDialog.qml index 68d67a32c..6100c0864 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/SelectionDialog.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/SelectionDialog.qml @@ -32,7 +32,7 @@ Item { Rectangle { // The dialog id: dialog - color: Appearance.colors.colSurfaceContainerHigh + color: Appearance.m3colors.m3surfaceContainerHigh radius: Appearance.rounding.normal anchors.fill: parent anchors.margins: dialogMargin diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StyledComboBox.qml b/dots/.config/quickshell/ii/modules/common/widgets/StyledComboBox.qml index 18add2cc3..55504acb2 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/StyledComboBox.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/StyledComboBox.qml @@ -192,7 +192,7 @@ ComboBox { id: popupBackground anchors.fill: parent radius: Appearance.rounding.normal - color: Appearance.colors.colSurfaceContainerHigh + color: Appearance.m3colors.m3surfaceContainerHigh } } diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StyledSlider.qml b/dots/.config/quickshell/ii/modules/common/widgets/StyledSlider.qml index 98b8eebf3..971a1eb10 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/StyledSlider.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/StyledSlider.qml @@ -46,7 +46,8 @@ Slider { property real handleWidth: root.pressed ? handlePressedWidth : handleDefaultWidth property real handleMargins: 4 property real trackDotSize: 3 - property string tooltipContent: `${Math.round(value * 100)}%` + property bool usePercentTooltip: true + property string tooltipContent: usePercentTooltip ? `${Math.round(((value - from) / (to - from)) * 100)}%` : `${Math.round(value)}` property bool wavy: configuration === StyledSlider.Configuration.Wavy // If true, the progress bar will have a wavy fill effect property bool animateWave: true property real waveAmplitudeMultiplier: wavy ? 0.5 : 0 diff --git a/dots/.config/quickshell/ii/modules/common/widgets/ToolbarTabBar.qml b/dots/.config/quickshell/ii/modules/common/widgets/ToolbarTabBar.qml index 1bca18463..708b30bd7 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/ToolbarTabBar.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/ToolbarTabBar.qml @@ -12,19 +12,30 @@ Item { required property var tabButtonList function incrementCurrentIndex() { - tabBar.incrementCurrentIndex() + tabBar.incrementCurrentIndex(); } function decrementCurrentIndex() { - tabBar.decrementCurrentIndex() + tabBar.decrementCurrentIndex(); } function setCurrentIndex(index) { - tabBar.setCurrentIndex(index) + tabBar.setCurrentIndex(index); } Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter implicitWidth: contentItem.implicitWidth implicitHeight: 40 + property Component delegate: ToolbarTabButton { + required property int index + required property var modelData + current: index == root.currentIndex + text: modelData.name + materialSymbol: modelData.icon + onClicked: { + root.setCurrentIndex(index); + } + } + Row { id: contentItem z: 1 @@ -33,16 +44,7 @@ Item { Repeater { model: root.tabButtonList - delegate: ToolbarTabButton { - required property int index - required property var modelData - current: index == root.currentIndex - text: modelData.name - materialSymbol: modelData.icon - onClicked: { - root.setCurrentIndex(index) - } - } + delegate: root.delegate } } @@ -76,23 +78,23 @@ Item { z: 2 acceptedButtons: Qt.NoButton cursorShape: Qt.PointingHandCursor - onWheel: (event) => { + onWheel: event => { if (event.angleDelta.y < 0) { root.incrementCurrentIndex(); - } - else { + } else { root.decrementCurrentIndex(); } } } - // TabBar doesn't allow tabs to be of different sizes. Literally unusable. + // TabBar doesn't allow tabs to be of different sizes. That's what I thought... // We use it only for the logic and draw stuff manually TabBar { id: tabBar z: -1 background: null - Repeater { // This is to fool the TabBar that it has tabs so it does the indices properly + Repeater { + // This is to fool the TabBar that it has tabs so it does the indices properly model: root.tabButtonList.length delegate: TabButton { background: null diff --git a/dots/.config/quickshell/ii/modules/common/widgets/shapes b/dots/.config/quickshell/ii/modules/common/widgets/shapes index 8aa62a41b..e31ec4cb4 160000 --- a/dots/.config/quickshell/ii/modules/common/widgets/shapes +++ b/dots/.config/quickshell/ii/modules/common/widgets/shapes @@ -1 +1 @@ -Subproject commit 8aa62a41bd4cdc4899bdfdc0d9cf103ac34c51f6 +Subproject commit e31ec4cb4ebf6a46b267f5c42eabf6874916fa16 diff --git a/dots/.config/quickshell/ii/modules/ii/background/widgets/AbstractBackgroundWidget.qml b/dots/.config/quickshell/ii/modules/ii/background/widgets/AbstractBackgroundWidget.qml index b24c33aaa..6d8c14598 100644 --- a/dots/.config/quickshell/ii/modules/ii/background/widgets/AbstractBackgroundWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/background/widgets/AbstractBackgroundWidget.qml @@ -59,7 +59,8 @@ AbstractWidget { function onReadyChanged() { refreshPlacementIfNeeded() } } function refreshPlacementIfNeeded() { - if (!Config.ready || (root.placementStrategy === "free" && root.needsColText)) return; + if (!Config.ready) return; + if (root.placementStrategy === "free" && !root.needsColText) return; leastBusyRegionProc.wallpaperPath = root.wallpaperPath; leastBusyRegionProc.running = false; leastBusyRegionProc.running = true; diff --git a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockText.qml b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockText.qml new file mode 100644 index 000000000..133a2759c --- /dev/null +++ b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockText.qml @@ -0,0 +1,19 @@ +import qs.modules.common +import qs.modules.common.widgets +import QtQuick +import QtQuick.Layouts + +StyledText { + Layout.fillWidth: true + font { + family: Appearance.font.family.expressive + pixelSize: 20 + weight: 350 + // Set empty to prevent conflicts, not meaningless + styleName: "" + variableAxes: ({}) + } + style: Text.Raised + styleColor: Appearance.colors.colShadow + animateChange: Config.options.background.widgets.clock.digital.animateChange +} \ No newline at end of file diff --git a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockWidget.qml b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockWidget.qml index 97e1e468a..754b6d1be 100644 --- a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockWidget.qml @@ -26,7 +26,7 @@ AbstractBackgroundWidget { visibleWhenLocked: true property var textHorizontalAlignment: { - if (root.forceCenter) + if (!Config.options.background.widgets.clock.digital.adaptiveAlignment || root.forceCenter || Config.options.background.widgets.clock.digital.vertical) return Text.AlignHCenter; if (root.x < root.scaledScreenWidth / 3) return Text.AlignLeft; @@ -63,32 +63,9 @@ AbstractBackgroundWidget { anchors.horizontalCenter: parent.horizontalCenter shown: root.clockStyle === "digital" && (root.shouldShow) fade: false - sourceComponent: ColumnLayout { - id: clockColumn - spacing: 6 - - ClockText { - font.pixelSize: 90 - text: DateTime.time - } - ClockText { - Layout.topMargin: -5 - text: DateTime.longDate - } - StyledText { - // Somehow gets fucked up if made a ClockText??? - visible: Config.options.background.widgets.clock.quote.enable && Config.options.background.widgets.clock.quote.text.length > 0 - Layout.fillWidth: true - horizontalAlignment: root.textHorizontalAlignment - font { - pixelSize: Appearance.font.pixelSize.normal - weight: 350 - } - color: root.colText - style: Text.Raised - styleColor: Appearance.colors.colShadow - text: Config.options.background.widgets.clock.quote.text - } + sourceComponent: DigitalClock { + colText: root.colText + textHorizontalAlignment: root.textHorizontalAlignment } } StatusRow { @@ -154,19 +131,6 @@ AbstractBackgroundWidget { } } - component ClockText: StyledText { - Layout.fillWidth: true - horizontalAlignment: root.textHorizontalAlignment - font { - family: Appearance.font.family.expressive - pixelSize: 20 - weight: Font.DemiBold - } - color: root.colText - style: Text.Raised - styleColor: Appearance.colors.colShadow - animateChange: Config.options.background.widgets.clock.digital.animateChange - } component ClockStatusText: Row { id: statusTextRow property alias statusIcon: statusIconWidget.text @@ -190,6 +154,7 @@ AbstractBackgroundWidget { ClockText { id: statusTextWidget color: statusTextRow.textColor + horizontalAlignment: root.textHorizontalAlignment anchors.verticalCenter: statusTextRow.verticalCenter font { pixelSize: Appearance.font.pixelSize.large diff --git a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/CookieClock.qml b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/CookieClock.qml index 7eaa04b98..f6ab64d81 100644 --- a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/CookieClock.qml +++ b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/CookieClock.qml @@ -66,16 +66,9 @@ Item { } } - Connections { - target: Config - function onReadyChanged() { - categoryFileView.path = Directories.generatedWallpaperCategoryPath - } - } - FileView { id: categoryFileView - path: "" + path: Config.ready ? Directories.generatedWallpaperCategoryPath : "" watchChanges: true onFileChanged: reload() onLoaded: { @@ -85,7 +78,7 @@ Item { property bool useSineCookie: Config.options.background.widgets.clock.cookie.useSineCookie StyledDropShadow { - target: useSineCookie ? sineCookieLoader : roundedPolygonCookieLoader + target: root.useSineCookie ? sineCookieLoader : roundedPolygonCookieLoader RotationAnimation on rotation { running: Config.options.background.widgets.clock.cookie.constantlyRotate @@ -100,7 +93,7 @@ Item { id: sineCookieLoader z: 0 visible: false // The DropShadow already draws it - active: useSineCookie + active: root.useSineCookie sourceComponent: SineCookie { implicitSize: root.implicitSize sides: Config.options.background.widgets.clock.cookie.sides @@ -111,7 +104,7 @@ Item { id: roundedPolygonCookieLoader z: 0 visible: false // The DropShadow already draws it - active: !useSineCookie + active: !root.useSineCookie sourceComponent: MaterialCookie { implicitSize: root.implicitSize sides: Config.options.background.widgets.clock.cookie.sides diff --git a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml new file mode 100644 index 000000000..12f49f642 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml @@ -0,0 +1,71 @@ +pragma ComponentBehavior: Bound + +import qs.services +import qs.modules.common +import QtQuick +import QtQuick.Layouts + +ColumnLayout { + id: clockColumn + spacing: 4 + + property bool isVertical: Config.options.background.widgets.clock.digital.vertical + property color colText: Appearance.colors.colOnSecondaryContainer + property var textHorizontalAlignment: Text.AlignHCenter + + // Time + ClockText { + id: timeTextTop + text: clockColumn.isVertical ? DateTime.time.split(":")[0].padStart(2, "0") : DateTime.time + color: clockColumn.colText + horizontalAlignment: Text.AlignHCenter + font { + pixelSize: Config.options.background.widgets.clock.digital.font.size + weight: Config.options.background.widgets.clock.digital.font.weight + family: Config.options.background.widgets.clock.digital.font.family + variableAxes: ({ + "wdth": Config.options.background.widgets.clock.digital.font.width, + "ROND": Config.options.background.widgets.clock.digital.font.roundness + }) + } + } + + Loader { + Layout.topMargin: -40 + Layout.fillWidth: true + active: clockColumn.isVertical + visible: active + sourceComponent: ClockText { + id: timeTextBottom + text: DateTime.time.split(":")[1].split(" ")[0].padStart(2, "0") + color: clockColumn.colText + horizontalAlignment: clockColumn.textHorizontalAlignment + font { + pixelSize: timeTextTop.font.pixelSize + weight: timeTextTop.font.weight + family: timeTextTop.font.family + variableAxes: timeTextTop.font.variableAxes + } + } + } + + // Date + ClockText { + visible: Config.options.background.widgets.clock.digital.showDate + Layout.topMargin: -20 + Layout.fillWidth: true + text: DateTime.longDate + color: clockColumn.colText + horizontalAlignment: clockColumn.textHorizontalAlignment + } + + // Quote + ClockText { + visible: Config.options.background.widgets.clock.quote.enable && Config.options.background.widgets.clock.quote.text.length > 0 + font.pixelSize: Appearance.font.pixelSize.normal + text: Config.options.background.widgets.clock.quote.text + animateChange: false + color: clockColumn.colText + horizontalAlignment: clockColumn.textHorizontalAlignment + } +} diff --git a/dots/.config/quickshell/ii/modules/ii/bar/Bar.qml b/dots/.config/quickshell/ii/modules/ii/bar/Bar.qml index 0bf1ad917..67e730677 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/Bar.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/Bar.qml @@ -1,3 +1,5 @@ +pragma ComponentBehavior: Bound + import QtQuick import Quickshell import Quickshell.Io @@ -60,6 +62,7 @@ Scope { } color: "transparent" + // Positioning anchors { top: !Config.options.bar.bottom bottom: Config.options.bar.bottom @@ -72,6 +75,14 @@ Scope { bottom: (Config.options.interactions.deadPixelWorkaround.enable && barRoot.anchors.bottom) * -1 } + // Include in focus grab + Component.onCompleted: { + GlobalFocusGrab.addPersistent(barRoot); + } + Component.onDestruction: { + GlobalFocusGrab.removePersistent(barRoot); + } + MouseArea { id: hoverRegion hoverEnabled: true diff --git a/dots/.config/quickshell/ii/modules/ii/bar/BarContent.qml b/dots/.config/quickshell/ii/modules/ii/bar/BarContent.qml index bcca26740..2a72e3382 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/BarContent.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/BarContent.qml @@ -80,19 +80,21 @@ Item { // Bar content region RowLayout { id: leftSectionRowLayout anchors.fill: parent - spacing: 10 + spacing: 0 LeftSidebarButton { // Left sidebar button + id: leftSidebarButton Layout.alignment: Qt.AlignVCenter Layout.leftMargin: Appearance.rounding.screenRounding colBackground: barLeftSideMouseArea.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1) } ActiveWindow { - visible: root.useShortenedForm === 0 + Layout.leftMargin: 10 + (leftSidebarButton.visible ? 0 : Appearance.rounding.screenRounding) Layout.rightMargin: Appearance.rounding.screenRounding Layout.fillWidth: true Layout.fillHeight: true + visible: root.useShortenedForm === 0 } } } diff --git a/dots/.config/quickshell/ii/modules/ii/bar/LeftSidebarButton.qml b/dots/.config/quickshell/ii/modules/ii/bar/LeftSidebarButton.qml index 5ea1bf5a9..6c73ca72e 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/LeftSidebarButton.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/LeftSidebarButton.qml @@ -9,6 +9,11 @@ RippleButton { property bool showPing: false + property bool aiChatEnabled: Config.options.policies.ai !== 0 + property bool translatorEnabled: Config.options.sidebar.translator.enable + property bool animeEnabled: Config.options.policies.weeb !== 0 + visible: aiChatEnabled || translatorEnabled || animeEnabled + property real buttonPadding: 5 implicitWidth: distroIcon.width + buttonPadding * 2 implicitHeight: distroIcon.height + buttonPadding * 2 diff --git a/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml b/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml index f0a61ef23..36c488730 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/Workspaces.qml @@ -18,9 +18,10 @@ Item { property bool borderless: Config.options.bar.borderless readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.QsWindow.window?.screen) readonly property Toplevel activeWindow: ToplevelManager.activeToplevel + readonly property int effectiveActiveWorkspaceId: monitor?.activeWorkspace?.id ?? 1 readonly property int workspacesShown: Config.options.bar.workspaces.shown - readonly property int workspaceGroup: Math.floor((monitor?.activeWorkspace?.id - 1) / root.workspacesShown) + readonly property int workspaceGroup: Math.floor((effectiveActiveWorkspaceId - 1) / root.workspacesShown) property list workspaceOccupied: [] property int widgetPadding: 4 property int workspaceButtonWidth: 26 @@ -29,7 +30,7 @@ Item { property real workspaceIconSizeShrinked: workspaceButtonWidth * 0.55 property real workspaceIconOpacityShrinked: 1 property real workspaceIconMarginShrinked: -4 - property int workspaceIndexInGroup: (monitor?.activeWorkspace?.id - 1) % root.workspacesShown + property int workspaceIndexInGroup: (effectiveActiveWorkspaceId - 1) % root.workspacesShown property bool showNumbers: false Timer { @@ -122,8 +123,8 @@ Item { implicitWidth: workspaceButtonWidth implicitHeight: workspaceButtonWidth radius: (width / 2) - property var previousOccupied: (workspaceOccupied[index-1] && !(!activeWindow?.activated && monitor?.activeWorkspace?.id === index)) - property var rightOccupied: (workspaceOccupied[index+1] && !(!activeWindow?.activated && monitor?.activeWorkspace?.id === index+2)) + property var previousOccupied: (workspaceOccupied[index-1] && !(!activeWindow?.activated && root.effectiveActiveWorkspaceId === index)) + property var rightOccupied: (workspaceOccupied[index+1] && !(!activeWindow?.activated && root.effectiveActiveWorkspaceId === index+2)) property var radiusPrev: previousOccupied ? 0 : (width / 2) property var radiusNext: rightOccupied ? 0 : (width / 2) @@ -133,7 +134,7 @@ Item { bottomRightRadius: radiusNext color: ColorUtils.transparentize(Appearance.m3colors.m3secondaryContainer, 0.4) - opacity: (workspaceOccupied[index] && !(!activeWindow?.activated && monitor?.activeWorkspace?.id === index+1)) ? 1 : 0 + opacity: (workspaceOccupied[index] && !(!activeWindow?.activated && root.effectiveActiveWorkspaceId === index+1)) ? 1 : 0 Behavior on opacity { animation: Appearance.animation.elementMove.numberAnimation.createObject(this) @@ -225,7 +226,7 @@ Item { } text: Config.options?.bar.workspaces.numberMap[button.workspaceValue - 1] || button.workspaceValue elide: Text.ElideRight - color: (monitor?.activeWorkspace?.id == button.workspaceValue) ? + color: (root.effectiveActiveWorkspaceId == button.workspaceValue) ? Appearance.m3colors.m3onPrimary : (workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer1Inactive) @@ -245,7 +246,7 @@ Item { width: workspaceButtonWidth * 0.18 height: width radius: width / 2 - color: (monitor?.activeWorkspace?.id == button.workspaceValue) ? + color: (root.effectiveActiveWorkspaceId == button.workspaceValue) ? Appearance.m3colors.m3onPrimary : (workspaceOccupied[index] ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer1Inactive) diff --git a/dots/.config/quickshell/ii/modules/ii/bar/weather/WeatherPopup.qml b/dots/.config/quickshell/ii/modules/ii/bar/weather/WeatherPopup.qml index 85d9c1bac..5b00b4590 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/weather/WeatherPopup.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/weather/WeatherPopup.qml @@ -100,5 +100,16 @@ StyledPopup { value: Weather.data.sunset } } + + // Footer: last refresh + StyledText { + Layout.alignment: Qt.AlignHCenter + text: Translation.tr("Last refresh: %1").arg(Weather.data.lastRefresh) + font { + weight: Font.Medium + pixelSize: Appearance.font.pixelSize.smaller + } + color: Appearance.colors.colOnSurfaceVariant + } } } diff --git a/dots/.config/quickshell/ii/modules/ii/cheatsheet/Cheatsheet.qml b/dots/.config/quickshell/ii/modules/ii/cheatsheet/Cheatsheet.qml index f7fb68f68..c5df4fe82 100644 --- a/dots/.config/quickshell/ii/modules/ii/cheatsheet/Cheatsheet.qml +++ b/dots/.config/quickshell/ii/modules/ii/cheatsheet/Cheatsheet.qml @@ -54,13 +54,16 @@ Scope { // Scope item: cheatsheetBackground } - HyprlandFocusGrab { // Click outside to close - id: grab - windows: [cheatsheetRoot] - active: cheatsheetRoot.visible - onCleared: () => { - if (!active) - cheatsheetRoot.hide(); + Component.onCompleted: { + GlobalFocusGrab.addDismissable(cheatsheetRoot); + } + Component.onDestruction: { + GlobalFocusGrab.removeDismissable(cheatsheetRoot); + } + Connections { + target: GlobalFocusGrab + function onDismissed() { + cheatsheetRoot.hide(); } } diff --git a/dots/.config/quickshell/ii/modules/ii/dock/DockApps.qml b/dots/.config/quickshell/ii/modules/ii/dock/DockApps.qml index d575b7dd4..6e5e4cbc5 100644 --- a/dots/.config/quickshell/ii/modules/ii/dock/DockApps.qml +++ b/dots/.config/quickshell/ii/modules/ii/dock/DockApps.qml @@ -133,7 +133,7 @@ Item { animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) } clip: true - color: Appearance.colors.colSurfaceContainer + color: Appearance.m3colors.m3surfaceContainer radius: Appearance.rounding.normal anchors.bottom: parent.bottom anchors.bottomMargin: Appearance.sizes.elevationMargin diff --git a/dots/.config/quickshell/ii/modules/ii/lock/Lock.qml b/dots/.config/quickshell/ii/modules/ii/lock/Lock.qml index 3aae6ef7b..1837480ad 100644 --- a/dots/.config/quickshell/ii/modules/ii/lock/Lock.qml +++ b/dots/.config/quickshell/ii/modules/ii/lock/Lock.qml @@ -6,36 +6,61 @@ import qs.modules.common.functions import qs.modules.common.panels.lock import QtQuick import Quickshell -import Quickshell.Io -import Quickshell.Wayland import Quickshell.Hyprland LockScreen { id: root + // Monitor name -> workspace id to restore on unlock (set when locking) + property var savedWorkspaces: ({}) + + Timer { + id: restoreTimer + interval: 150 + repeat: false + onTriggered: { + var batch = "" + for (var j = 0; j < Quickshell.screens.length; ++j) { + var monName = Quickshell.screens[j].name + var wsId = root.savedWorkspaces[monName] + if (wsId !== undefined) { + batch += "dispatch focusmonitor " + monName + "; dispatch workspace " + wsId + "; " + } + } + if (batch.length > 0) { + Quickshell.execDetached(["hyprctl", "--batch", batch + "reload"]) + } + } + } + lockSurface: LockSurface { context: root.context } - // Push everything down - property var windowData: [] - function saveWindowPositionAndTile() { - Quickshell.execDetached(["hyprctl", "keyword", "dwindle:pseudotile", "true"]); - root.windowData = HyprlandData.windowList.filter(w => (w.floating && w.workspace.id === HyprlandData.activeWorkspace.id)); - root.windowData.forEach(w => { - Hyprland.dispatch(`pseudo address:${w.address}`); - Hyprland.dispatch(`settiled address:${w.address}`); - Hyprland.dispatch(`movetoworkspacesilent ${w.workspace.id},address:${w.address}`); - }); - } - function restoreWindowPositionAndTile() { - root.windowData.forEach(w => { - Hyprland.dispatch(`setfloating address:${w.address}`); - Hyprland.dispatch(`movewindowpixel exact ${w.at[0]} ${w.at[1]}, address:${w.address}`); - Hyprland.dispatch(`pseudo address:${w.address}`); - }); - Quickshell.execDetached(["hyprctl", "keyword", "dwindle:pseudotile", "false"]); + // Single batch for lock and unlock so we don't race multiple hyprctl calls + Connections { + target: GlobalStates + function onScreenLockedChanged() { + if (GlobalStates.screenLocked) { + // Lock: save workspace per monitor and move all to temp workspace in one batch + var next = {} + var batch = "keyword animation workspaces,1,7,menu_decel,slidevert; " + for (var i = 0; i < Quickshell.screens.length; ++i) { + var mon = Quickshell.screens[i].name + var mData = HyprlandData.monitors.find(m => m.name === mon) + var ws = (mData?.activeWorkspace?.id ?? 1) + next[mon] = ws + batch += "dispatch focusmonitor " + mon + "; dispatch workspace " + (2147483647 - ws) + "; " + } + root.savedWorkspaces = next + Quickshell.execDetached(["hyprctl", "--batch", batch + "reload"]) + } else { + restoreTimer.start() + } + } } + + // Push everything down (visual only; workspace switch is in Connections above) Variants { model: Quickshell.screens delegate: Scope { @@ -44,15 +69,6 @@ LockScreen { property string targetMonitorName: modelData.name property int verticalMovementDistance: modelData.height property int horizontalSqueeze: modelData.width * 0.2 - onShouldPushChanged: { - if (shouldPush) { - root.saveWindowPositionAndTile(); - Quickshell.execDetached(["bash", "-c", `hyprctl keyword monitor ${targetMonitorName}, addreserved, ${verticalMovementDistance}, ${-verticalMovementDistance}, ${horizontalSqueeze}, ${horizontalSqueeze}`]); - } else { - Quickshell.execDetached(["bash", "-c", `hyprctl keyword monitor ${targetMonitorName}, addreserved, 0, 0, 0, 0`]); - root.restoreWindowPositionAndTile(); - } - } } } } diff --git a/dots/.config/quickshell/ii/modules/ii/lock/LockSurface.qml b/dots/.config/quickshell/ii/modules/ii/lock/LockSurface.qml index b2482132d..82d10260a 100644 --- a/dots/.config/quickshell/ii/modules/ii/lock/LockSurface.qml +++ b/dots/.config/quickshell/ii/modules/ii/lock/LockSurface.qml @@ -136,6 +136,8 @@ MouseArea { // Style clip: true font.pixelSize: Appearance.font.pixelSize.small + selectedTextColor: materialShapeChars ? "transparent" : Appearance.colors.colOnSecondaryContainer + selectionColor: materialShapeChars ? "transparent" : Appearance.colors.colSecondaryContainer // Password enabled: !root.context.unlockInProgress @@ -195,6 +197,9 @@ MouseArea { } sourceComponent: PasswordChars { length: root.context.currentText.length + selectionStart: passwordBox.selectionStart + selectionEnd: passwordBox.selectionEnd + cursorPosition: passwordBox.cursorPosition } } } @@ -215,7 +220,7 @@ MouseArea { iconSize: 24 text: { if (root.context.targetAction === LockContext.ActionEnum.Unlock) { - return root.ctrlHeld ? "emoji_food_beverage" : "arrow_right_alt"; + return root.ctrlHeld ? "coffee" : "arrow_right_alt"; } else if (root.context.targetAction === LockContext.ActionEnum.Poweroff) { return "power_settings_new"; } else if (root.context.targetAction === LockContext.ActionEnum.Reboot) { diff --git a/dots/.config/quickshell/ii/modules/ii/lock/PasswordChars.qml b/dots/.config/quickshell/ii/modules/ii/lock/PasswordChars.qml index 400c2495a..07c856c05 100644 --- a/dots/.config/quickshell/ii/modules/ii/lock/PasswordChars.qml +++ b/dots/.config/quickshell/ii/modules/ii/lock/PasswordChars.qml @@ -9,29 +9,62 @@ import Quickshell StyledFlickable { id: root + required property int length + property int selectionStart + property int selectionEnd + property int cursorPosition + + property color color: Appearance.colors.colPrimary + property color selectedTextColor: Appearance.colors.colOnSecondaryContainer + property color selectionColor: Appearance.colors.colSecondaryContainer + + property int charSize: 20 + contentWidth: dotsRow.implicitWidth contentX: (Math.max(contentWidth - width, 0)) Behavior on contentX { animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) } + + Rectangle { + id: cursor + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + leftMargin: root.charSize * root.cursorPosition + } + color: root.color + implicitWidth: 2 + implicitHeight: root.charSize + Behavior on anchors.leftMargin { + animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(cursor) + } + } + Row { id: dotsRow anchors { left: parent.left verticalCenter: parent.verticalCenter - leftMargin: 4 + leftMargin: 4 - 5 // -5 to account for spacing being simulated by char item width } - spacing: 10 + spacing: 0 + Repeater { - model: ScriptModel { + model: ScriptModel { // TODO: use proper custom object model to insert new char at the correct pos values: Array(root.length) } - delegate: Item { + + delegate: Rectangle { id: charItem required property int index - implicitWidth: 10 - implicitHeight: 10 + implicitWidth: root.charSize + implicitHeight: root.charSize + property bool selected: index >= root.selectionStart && index < root.selectionEnd + + color: ColorUtils.transparentize(root.selectionColor, selected ? 0 : 1) + MaterialShape { id: materialShape anchors.centerIn: parent @@ -46,7 +79,7 @@ StyledFlickable { ] shape: charShapes[charItem.index % charShapes.length] // Animate on appearance - color: Appearance.colors.colPrimary + color: charItem.selected ? root.selectedTextColor : root.color implicitSize: 0 opacity: 0 scale: 0.5 diff --git a/dots/.config/quickshell/ii/modules/ii/mediaControls/MediaControls.qml b/dots/.config/quickshell/ii/modules/ii/mediaControls/MediaControls.qml index 3cd620834..3d98be7ca 100644 --- a/dots/.config/quickshell/ii/modules/ii/mediaControls/MediaControls.qml +++ b/dots/.config/quickshell/ii/modules/ii/mediaControls/MediaControls.qml @@ -81,7 +81,7 @@ Scope { } sourceComponent: PanelWindow { - id: mediaControlsRoot + id: panelWindow visible: true exclusionMode: ExclusionMode.Ignore @@ -98,9 +98,9 @@ Scope { right: Config.options.bar.vertical && Config.options.bar.bottom } margins { - top: Config.options.bar.vertical ? ((mediaControlsRoot.screen.height / 2) - widgetHeight * 1.5) : Appearance.sizes.barHeight + top: Config.options.bar.vertical ? ((panelWindow.screen.height / 2) - widgetHeight * 1.5) : Appearance.sizes.barHeight bottom: Appearance.sizes.barHeight - left: Config.options.bar.vertical ? Appearance.sizes.barHeight : ((mediaControlsRoot.screen.width / 2) - (osdWidth / 2) - widgetWidth) + left: Config.options.bar.vertical ? Appearance.sizes.barHeight : ((panelWindow.screen.width / 2) - (osdWidth / 2) - widgetWidth) right: Appearance.sizes.barHeight } @@ -108,13 +108,16 @@ Scope { item: playerColumnLayout } - HyprlandFocusGrab { - windows: [mediaControlsRoot] - active: mediaControlsLoader.active - onCleared: () => { - if (!active) { - GlobalStates.mediaControlsOpen = false; - } + Component.onCompleted: { + GlobalFocusGrab.addDismissable(panelWindow); + } + Component.onDestruction: { + GlobalFocusGrab.removeDismissable(panelWindow); + } + Connections { + target: GlobalFocusGrab + function onDismissed() { + GlobalStates.mediaControlsOpen = false; } } @@ -137,10 +140,13 @@ Scope { } } - Item { // No player placeholder + Item { + // No player placeholder Layout.alignment: { - if (mediaControlsRoot.anchors.left) return Qt.AlignLeft; - if (mediaControlsRoot.anchors.right) return Qt.AlignRight; + if (panelWindow.anchors.left) + return Qt.AlignLeft; + if (panelWindow.anchors.right) + return Qt.AlignRight; return Qt.AlignHCenter; } Layout.leftMargin: Appearance.sizes.hyprlandGapsOut @@ -153,7 +159,7 @@ Scope { target: placeholderBackground } - Rectangle { + Rectangle { id: placeholderBackground anchors.centerIn: parent color: Appearance.colors.colLayer0 diff --git a/dots/.config/quickshell/ii/modules/ii/onScreenKeyboard/OnScreenKeyboard.qml b/dots/.config/quickshell/ii/modules/ii/onScreenKeyboard/OnScreenKeyboard.qml index 3407c217f..9dc1068d8 100644 --- a/dots/.config/quickshell/ii/modules/ii/onScreenKeyboard/OnScreenKeyboard.qml +++ b/dots/.config/quickshell/ii/modules/ii/onScreenKeyboard/OnScreenKeyboard.qml @@ -57,6 +57,13 @@ Scope { // Scope item: oskBackground } + // Make it usable with other panels + Component.onCompleted: { + GlobalFocusGrab.addPersistent(oskRoot); + } + Component.onDestruction: { + GlobalFocusGrab.removePersistent(oskRoot); + } // Background StyledRectangularShadow { diff --git a/dots/.config/quickshell/ii/modules/ii/overlay/OverlayBackground.qml b/dots/.config/quickshell/ii/modules/ii/overlay/OverlayBackground.qml index d91e4cbba..205307cab 100644 --- a/dots/.config/quickshell/ii/modules/ii/overlay/OverlayBackground.qml +++ b/dots/.config/quickshell/ii/modules/ii/overlay/OverlayBackground.qml @@ -4,5 +4,5 @@ import qs.modules.common Rectangle { id: contentItem anchors.fill: parent - color: Appearance.colors.colSurfaceContainer + color: Appearance.m3colors.m3surfaceContainer } diff --git a/dots/.config/quickshell/ii/modules/ii/overlay/StyledOverlayWidget.qml b/dots/.config/quickshell/ii/modules/ii/overlay/StyledOverlayWidget.qml index 949cd2380..b3cf1e74a 100644 --- a/dots/.config/quickshell/ii/modules/ii/overlay/StyledOverlayWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/overlay/StyledOverlayWidget.qml @@ -190,7 +190,7 @@ AbstractOverlayWidget { fill: parent margins: root.resizeMargin } - color: ColorUtils.transparentize(Appearance.colors.colLayer1, (root.fancyBorders && GlobalStates.overlayOpen) ? 0 : 1) + color: ColorUtils.transparentize(Appearance.colors.colLayer1Base, (root.fancyBorders && GlobalStates.overlayOpen) ? 0 : 1) radius: root.radius border.color: ColorUtils.transparentize(Appearance.colors.colOutlineVariant, GlobalStates.overlayOpen ? 0 : 1) border.width: 1 @@ -217,7 +217,7 @@ AbstractOverlayWidget { Layout.fillWidth: true implicitWidth: titleBarRow.implicitWidth + root.padding * 2 implicitHeight: titleBarRow.implicitHeight + root.padding * 2 - color: root.fancyBorders ? "transparent" : Appearance.colors.colLayer1 + color: root.fancyBorders ? "transparent" : Appearance.colors.colLayer1Base // border.color: Appearance.colors.colOutlineVariant // border.width: 1 diff --git a/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml b/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml index c435f7f8a..fe3df64a3 100644 --- a/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml +++ b/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml @@ -14,116 +14,96 @@ import Quickshell.Hyprland Scope { id: overviewScope property bool dontAutoCancelSearch: false - Variants { - id: overviewVariants - model: Quickshell.screens - PanelWindow { - id: root - required property var modelData - property string searchingText: "" - readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen) - property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor?.id) - screen: modelData + + PanelWindow { + id: panelWindow + property string searchingText: "" + readonly property HyprlandMonitor monitor: Hyprland.monitorFor(panelWindow.screen) + property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor?.id) + visible: GlobalStates.overviewOpen + + WlrLayershell.namespace: "quickshell:overview" + WlrLayershell.layer: WlrLayer.Top + WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None + color: "transparent" + + mask: Region { + item: GlobalStates.overviewOpen ? columnLayout : null + } + + anchors { + top: true + bottom: true + left: true + right: true + } + + Connections { + target: GlobalStates + function onOverviewOpenChanged() { + if (!GlobalStates.overviewOpen) { + searchWidget.disableExpandAnimation(); + overviewScope.dontAutoCancelSearch = false; + GlobalFocusGrab.dismiss(); + } else { + if (!overviewScope.dontAutoCancelSearch) { + searchWidget.cancelSearch(); + } + GlobalFocusGrab.addDismissable(panelWindow); + } + } + } + + Connections { + target: GlobalFocusGrab + function onDismissed() { + GlobalStates.overviewOpen = false; + } + } + implicitWidth: columnLayout.implicitWidth + implicitHeight: columnLayout.implicitHeight + + function setSearchingText(text) { + searchWidget.setSearchingText(text); + searchWidget.focusFirstItem(); + } + + Column { + id: columnLayout visible: GlobalStates.overviewOpen - - WlrLayershell.namespace: "quickshell:overview" - WlrLayershell.layer: WlrLayer.Overlay - // WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None - color: "transparent" - - mask: Region { - item: GlobalStates.overviewOpen ? columnLayout : null - } - anchors { - top: true - bottom: true - left: true - right: true + horizontalCenter: parent.horizontalCenter + top: parent.top } + spacing: -8 - HyprlandFocusGrab { - id: grab - windows: [root] - property bool canBeActive: root.monitorIsFocused - active: false - onCleared: () => { - if (!active) - GlobalStates.overviewOpen = false; + Keys.onPressed: event => { + if (event.key === Qt.Key_Escape) { + GlobalStates.overviewOpen = false; + } else if (event.key === Qt.Key_Left) { + if (!panelWindow.searchingText) + Hyprland.dispatch("workspace r-1"); + } else if (event.key === Qt.Key_Right) { + if (!panelWindow.searchingText) + Hyprland.dispatch("workspace r+1"); } } - Connections { - target: GlobalStates - function onOverviewOpenChanged() { - if (!GlobalStates.overviewOpen) { - searchWidget.disableExpandAnimation(); - overviewScope.dontAutoCancelSearch = false; - } else { - if (!overviewScope.dontAutoCancelSearch) { - searchWidget.cancelSearch(); - } - delayedGrabTimer.start(); - } + SearchWidget { + id: searchWidget + anchors.horizontalCenter: parent.horizontalCenter + Synchronizer on searchingText { + property alias source: panelWindow.searchingText } } - Timer { - id: delayedGrabTimer - interval: Config.options.hacks.arbitraryRaceConditionDelay - repeat: false - onTriggered: { - if (!grab.canBeActive) - return; - grab.active = GlobalStates.overviewOpen; - } - } - - implicitWidth: columnLayout.implicitWidth - implicitHeight: columnLayout.implicitHeight - - function setSearchingText(text) { - searchWidget.setSearchingText(text); - searchWidget.focusFirstItem(); - } - - Column { - id: columnLayout - visible: GlobalStates.overviewOpen - anchors { - horizontalCenter: parent.horizontalCenter - top: parent.top - } - spacing: -8 - - Keys.onPressed: event => { - if (event.key === Qt.Key_Escape) { - GlobalStates.overviewOpen = false; - } else if (event.key === Qt.Key_Left) { - if (!root.searchingText) - Hyprland.dispatch("workspace r-1"); - } else if (event.key === Qt.Key_Right) { - if (!root.searchingText) - Hyprland.dispatch("workspace r+1"); - } - } - - SearchWidget { - id: searchWidget - anchors.horizontalCenter: parent.horizontalCenter - Synchronizer on searchingText { - property alias source: root.searchingText - } - } - - Loader { - id: overviewLoader - anchors.horizontalCenter: parent.horizontalCenter - active: GlobalStates.overviewOpen && (Config?.options.overview.enable ?? true) - sourceComponent: OverviewWidget { - panelWindow: root - visible: (root.searchingText == "") - } + Loader { + id: overviewLoader + anchors.horizontalCenter: parent.horizontalCenter + active: GlobalStates.overviewOpen && (Config?.options.overview.enable ?? true) + sourceComponent: OverviewWidget { + screen: panelWindow.screen + visible: (panelWindow.searchingText == "") } } } @@ -134,15 +114,9 @@ Scope { GlobalStates.overviewOpen = false; return; } - for (let i = 0; i < overviewVariants.instances.length; i++) { - let panelWindow = overviewVariants.instances[i]; - if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { - overviewScope.dontAutoCancelSearch = true; - panelWindow.setSearchingText(Config.options.search.prefix.clipboard); - GlobalStates.overviewOpen = true; - return; - } - } + overviewScope.dontAutoCancelSearch = true; + panelWindow.setSearchingText(Config.options.search.prefix.clipboard); + GlobalStates.overviewOpen = true; } function toggleEmojis() { @@ -150,15 +124,9 @@ Scope { GlobalStates.overviewOpen = false; return; } - for (let i = 0; i < overviewVariants.instances.length; i++) { - let panelWindow = overviewVariants.instances[i]; - if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { - overviewScope.dontAutoCancelSearch = true; - panelWindow.setSearchingText(Config.options.search.prefix.emojis); - GlobalStates.overviewOpen = true; - return; - } - } + overviewScope.dontAutoCancelSearch = true; + panelWindow.setSearchingText(Config.options.search.prefix.emojis); + GlobalStates.overviewOpen = true; } IpcHandler { @@ -192,6 +160,14 @@ Scope { GlobalStates.overviewOpen = !GlobalStates.overviewOpen; } } + GlobalShortcut { + name: "overviewWorkspacesClose" + description: "Closes overview on press" + + onPressed: { + GlobalStates.overviewOpen = false; + } + } GlobalShortcut { name: "overviewWorkspacesToggle" description: "Toggles overview on press" diff --git a/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml b/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml index 8b791f44a..96d72d9d1 100644 --- a/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml @@ -13,11 +13,13 @@ import Quickshell.Hyprland Item { id: root - required property var panelWindow - readonly property HyprlandMonitor monitor: Hyprland.monitorFor(panelWindow.screen) + required property var screen + readonly property HyprlandMonitor monitor: Hyprland.monitorFor(screen) readonly property var toplevels: ToplevelManager.toplevels + // Clamp to avoid lock-screen temp workspace (2147483647 - N) leaking into UI + readonly property int effectiveActiveWorkspaceId: Math.max(1, Math.min(100, monitor?.activeWorkspace?.id ?? 1)) readonly property int workspacesShown: Config.options.overview.rows * Config.options.overview.columns - readonly property int workspaceGroup: Math.floor((monitor.activeWorkspace?.id - 1) / workspacesShown) + readonly property int workspaceGroup: Math.floor((effectiveActiveWorkspaceId - 1) / workspacesShown) property bool monitorIsFocused: (Hyprland.focusedMonitor?.name == monitor.name) property var windows: HyprlandData.windowList property var windowByAddress: HyprlandData.windowByAddress @@ -101,7 +103,7 @@ Item { required property int index property int colIndex: index property int workspaceValue: root.workspaceGroup * root.workspacesShown + getWsInCell(row.index, colIndex) - property color defaultWorkspaceColor: ColorUtils.mix(Appearance.colors.colBackgroundSurfaceContainer, Appearance.colors.colSurfaceContainerHigh, 0.8) + property color defaultWorkspaceColor: Appearance.colors.colSurfaceContainerLow property color hoveredWorkspaceColor: ColorUtils.mix(defaultWorkspaceColor, Appearance.colors.colLayer1Hover, 0.1) property color hoveredBorderColor: Appearance.colors.colLayer2Hover property bool hoveredWhileDragging: false @@ -301,8 +303,8 @@ Item { Rectangle { // Focused workspace indicator id: focusedWorkspaceIndicator - property int rowIndex: getWsRow(monitor.activeWorkspace?.id) - property int colIndex: getWsColumn(monitor.activeWorkspace?.id) + property int rowIndex: getWsRow(root.effectiveActiveWorkspaceId) + property int colIndex: getWsColumn(root.effectiveActiveWorkspaceId) x: (root.workspaceImplicitWidth + workspaceSpacing) * colIndex y: (root.workspaceImplicitHeight + workspaceSpacing) * rowIndex z: root.windowZ diff --git a/dots/.config/quickshell/ii/modules/ii/overview/SearchBar.qml b/dots/.config/quickshell/ii/modules/ii/overview/SearchBar.qml index 6a5de6a7e..56a7f0574 100644 --- a/dots/.config/quickshell/ii/modules/ii/overview/SearchBar.qml +++ b/dots/.config/quickshell/ii/modules/ii/overview/SearchBar.qml @@ -1,15 +1,12 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Layouts +import Quickshell import qs import qs.services import qs.modules.common import qs.modules.common.widgets import qs.modules.common.functions -import Qt5Compat.GraphicalEffects -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts -import Quickshell -import Quickshell.Io -import Quickshell.Hyprland RowLayout { id: root @@ -92,6 +89,16 @@ RowLayout { } } } + + Keys.onPressed: event => { + if (event.key === Qt.Key_Tab) { + if (LauncherSearch.results.length === 0) return; + const tabbedText = LauncherSearch.results[0].name; + LauncherSearch.query = tabbedText; + searchInput.text = tabbedText; + event.accepted = true; + } + } } IconToolbarButton { diff --git a/dots/.config/quickshell/ii/modules/ii/overview/SearchWidget.qml b/dots/.config/quickshell/ii/modules/ii/overview/SearchWidget.qml index 6b982c4d2..c99b930c1 100644 --- a/dots/.config/quickshell/ii/modules/ii/overview/SearchWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/overview/SearchWidget.qml @@ -1,23 +1,28 @@ +pragma ComponentBehavior: Bound + +import Qt.labs.synchronizer +import Qt5Compat.GraphicalEffects +import QtQuick +import QtQuick.Layouts +import Quickshell + import qs import qs.services import qs.modules.common import qs.modules.common.widgets import qs.modules.common.functions -import Qt.labs.synchronizer -import Qt5Compat.GraphicalEffects -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts -import Quickshell -import Quickshell.Io Item { // Wrapper id: root + readonly property string xdgConfigHome: Directories.config + readonly property int typingDebounceInterval: 200 + readonly property int typingResultLimit: 15 // Should be enough to cover the whole view + property string searchingText: LauncherSearch.query property bool showResults: searchingText != "" implicitWidth: searchWidgetContent.implicitWidth + Appearance.sizes.elevationMargin * 2 - implicitHeight: searchBar.implicitHeight + searchBar.verticalPadding * 2 + Appearance.sizes.elevationMargin * 2 + implicitHeight: searchWidgetContent.implicitHeight + searchBar.verticalPadding * 2 + Appearance.sizes.elevationMargin * 2 function focusFirstItem() { appResults.currentIndex = 0; @@ -178,30 +183,48 @@ Item { // Wrapper } } - model: ScriptModel { - id: model - objectProp: "key" - values: LauncherSearch.results - onValuesChanged: { - root.focusFirstItem(); + Timer { + id: debounceTimer + interval: root.typingDebounceInterval + onTriggered: { + resultModel.values = LauncherSearch.results ?? []; } } + Connections { + target: LauncherSearch + function onResultsChanged() { + resultModel.values = LauncherSearch.results.slice(0, root.typingResultLimit); + root.focusFirstItem(); + debounceTimer.restart(); + } + } + + model: ScriptModel { + id: resultModel + objectProp: "key" + } + delegate: SearchItem { + id: searchItem // The selectable item for each search result required property var modelData anchors.left: parent?.left anchors.right: parent?.right entry: modelData - query: StringUtils.cleanOnePrefix(root.searchingText, [ - Config.options.search.prefix.action, - Config.options.search.prefix.app, - Config.options.search.prefix.clipboard, - Config.options.search.prefix.emojis, - Config.options.search.prefix.math, - Config.options.search.prefix.shellCommand, - Config.options.search.prefix.webSearch - ]) + query: StringUtils.cleanOnePrefix(root.searchingText, [Config.options.search.prefix.action, Config.options.search.prefix.app, Config.options.search.prefix.clipboard, Config.options.search.prefix.emojis, Config.options.search.prefix.math, Config.options.search.prefix.shellCommand, Config.options.search.prefix.webSearch]) + + Keys.onPressed: event => { + if (event.key === Qt.Key_Tab) { + if (LauncherSearch.results.length === 0) + return; + const tabbedText = searchItem.modelData.name; + LauncherSearch.query = tabbedText; + searchBar.searchInput.text = tabbedText; + event.accepted = true; + root.focusSearchInput(); + } + } } } } diff --git a/dots/.config/quickshell/ii/modules/ii/regionSelector/CursorGuide.qml b/dots/.config/quickshell/ii/modules/ii/regionSelector/CursorGuide.qml new file mode 100644 index 000000000..0cbec1297 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/ii/regionSelector/CursorGuide.qml @@ -0,0 +1,112 @@ +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import QtQuick + +Item { + id: root + property var action + property var selectionMode + + property string description: switch (root.action) { + case RegionSelection.SnipAction.Copy: + case RegionSelection.SnipAction.Edit: + return Translation.tr("Copy region (LMB) or annotate (RMB)"); + case RegionSelection.SnipAction.Search: + return Translation.tr("Search with Google Lens"); + case RegionSelection.SnipAction.CharRecognition: + return Translation.tr("Recognize text"); + case RegionSelection.SnipAction.Record: + case RegionSelection.SnipAction.RecordWithSound: + return Translation.tr("Record region"); + } + property string materialSymbol: switch (root.action) { + case RegionSelection.SnipAction.Copy: + case RegionSelection.SnipAction.Edit: + return "content_cut"; + case RegionSelection.SnipAction.Search: + return "image_search"; + case RegionSelection.SnipAction.CharRecognition: + return "document_scanner"; + case RegionSelection.SnipAction.Record: + case RegionSelection.SnipAction.RecordWithSound: + return "videocam"; + default: + return ""; + } + + property bool showDescription: true + function hideDescription() { + root.showDescription = false + } + Timer { + id: descTimeout + interval: 1000 + running: true + onTriggered: { + root.hideDescription() + } + } + onActionChanged: { + root.showDescription = true + descTimeout.restart() + } + + property int margins: 8 + implicitWidth: content.implicitWidth + margins * 2 + implicitHeight: content.implicitHeight + margins * 2 + + Rectangle { + id: content + anchors.centerIn: parent + + property real padding: 8 + implicitHeight: 38 + implicitWidth: root.showDescription ? contentRow.implicitWidth + padding * 2 : implicitHeight + clip: true + + topLeftRadius: 6 + bottomLeftRadius: implicitHeight - topLeftRadius + bottomRightRadius: bottomLeftRadius + topRightRadius: bottomLeftRadius + + color: Appearance.colors.colPrimary + + Behavior on topLeftRadius { + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) + } + Behavior on implicitWidth { + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) + } + + Row { + id: contentRow + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + leftMargin: content.padding + } + spacing: 12 + + MaterialSymbol { + anchors.verticalCenter: parent.verticalCenter + iconSize: 22 + color: Appearance.colors.colOnPrimary + animateChange: true + text: root.materialSymbol + } + + FadeLoader { + id: descriptionLoader + anchors.verticalCenter: parent.verticalCenter + shown: root.showDescription + sourceComponent: StyledText { + color: Appearance.colors.colOnPrimary + text: root.description + anchors.right: parent.right + anchors.rightMargin: 6 + } + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/ii/regionSelector/OptionsToolbar.qml b/dots/.config/quickshell/ii/modules/ii/regionSelector/OptionsToolbar.qml index 63a3e8c7e..1c68886c8 100644 --- a/dots/.config/quickshell/ii/modules/ii/regionSelector/OptionsToolbar.qml +++ b/dots/.config/quickshell/ii/modules/ii/regionSelector/OptionsToolbar.qml @@ -23,48 +23,6 @@ Toolbar { // Signals signal dismiss() - MaterialShape { - Layout.fillHeight: true - Layout.leftMargin: 2 - Layout.rightMargin: 2 - implicitSize: 36 // Intentionally smaller because this one is brighter than others - shape: switch (root.action) { - case RegionSelection.SnipAction.Copy: - case RegionSelection.SnipAction.Edit: - return MaterialShape.Shape.Cookie4Sided; - case RegionSelection.SnipAction.Search: - return MaterialShape.Shape.Pentagon; - case RegionSelection.SnipAction.CharRecognition: - return MaterialShape.Shape.Sunny; - case RegionSelection.SnipAction.Record: - case RegionSelection.SnipAction.RecordWithSound: - return MaterialShape.Shape.Gem; - default: - return MaterialShape.Shape.Cookie12Sided; - } - color: Appearance.colors.colPrimary - MaterialSymbol { - anchors.centerIn: parent - iconSize: 22 - color: Appearance.colors.colOnPrimary - animateChange: true - text: switch (root.action) { - case RegionSelection.SnipAction.Copy: - case RegionSelection.SnipAction.Edit: - return "content_cut"; - case RegionSelection.SnipAction.Search: - return "image_search"; - case RegionSelection.SnipAction.CharRecognition: - return "document_scanner"; - case RegionSelection.SnipAction.Record: - case RegionSelection.SnipAction.RecordWithSound: - return "videocam"; - default: - return ""; - } - } - } - ToolbarTabBar { id: tabBar tabButtonList: [ @@ -76,5 +34,4 @@ Toolbar { root.selectionMode = currentIndex === 0 ? RegionSelection.SelectionMode.RectCorners : RegionSelection.SelectionMode.Circle; } } - } diff --git a/dots/.config/quickshell/ii/modules/ii/regionSelector/RectCornersSelectionDetails.qml b/dots/.config/quickshell/ii/modules/ii/regionSelector/RectCornersSelectionDetails.qml index 40069131d..3682e7d51 100644 --- a/dots/.config/quickshell/ii/modules/ii/regionSelector/RectCornersSelectionDetails.qml +++ b/dots/.config/quickshell/ii/modules/ii/regionSelector/RectCornersSelectionDetails.qml @@ -33,22 +33,40 @@ Item { } // Selection border - Rectangle { + // Rectangle { + // id: selectionBorder + // z: 1 + // anchors { + // left: parent.left + // top: parent.top + // leftMargin: root.regionX + // topMargin: root.regionY + // } + // width: root.regionWidth + // height: root.regionHeight + // color: "transparent" + // border.color: root.color + // border.width: 2 + // // radius: root.standardRounding + // radius: 0 // TODO: figure out how to make the overlay thing work with rounding + // } + + DashedBorder { id: selectionBorder - z: 1 + z: 9 anchors { left: parent.left top: parent.top - leftMargin: root.regionX - topMargin: root.regionY + leftMargin: Math.round(root.regionX) - borderWidth + topMargin: Math.round(root.regionY) - borderWidth } - width: root.regionWidth - height: root.regionHeight - color: "transparent" - border.color: root.color - border.width: 2 - // radius: root.standardRounding - radius: 0 // TODO: figure out how to make the overlay thing work with rounding + width: Math.round(root.regionWidth) + borderWidth * 2 + height: Math.round(root.regionHeight) + borderWidth * 2 + + color: root.color + dashLength: 6 + gapLength: 3 + borderWidth: 1 } StyledText { diff --git a/dots/.config/quickshell/ii/modules/ii/regionSelector/RegionSelection.qml b/dots/.config/quickshell/ii/modules/ii/regionSelector/RegionSelection.qml index 59dc186e7..5f5ed79ab 100644 --- a/dots/.config/quickshell/ii/modules/ii/regionSelector/RegionSelection.qml +++ b/dots/.config/quickshell/ii/modules/ii/regionSelector/RegionSelection.qml @@ -1,5 +1,6 @@ pragma ComponentBehavior: Bound import qs.modules.common +import qs.modules.common.utils import qs.modules.common.functions import qs.modules.common.widgets import qs.services @@ -32,15 +33,9 @@ PanelWindow { property var action: RegionSelection.SnipAction.Copy property var selectionMode: RegionSelection.SelectionMode.RectCorners signal dismiss() - - property string saveScreenshotDir: Config.options.screenSnip.savePath !== "" - ? Config.options.screenSnip.savePath - : "" property string screenshotDir: Directories.screenshotTemp - property string imageSearchEngineBaseUrl: Config.options.search.imageSearch.imageSearchEngineBaseUrl - property string fileUploadApiEndpoint: "https://uguu.se/upload" - property color overlayColor: "#88111111" + property color overlayColor: ColorUtils.transparentize("#000000", 0.4) property color brightText: Appearance.m3colors.darkmode ? Appearance.colors.colOnLayer0 : Appearance.colors.colLayer0 property color brightSecondary: Appearance.m3colors.darkmode ? Appearance.colors.colSecondary : Appearance.colors.colOnSecondary property color brightTertiary: Appearance.m3colors.darkmode ? Appearance.colors.colTertiary : Qt.lighter(Appearance.colors.colPrimary) @@ -180,10 +175,12 @@ PanelWindow { property real regionX: Math.min(dragStartX, draggingX) property real regionY: Math.min(dragStartY, draggingY) - Process { + TempScreenshotProcess { id: screenshotProc running: true - command: ["bash", "-c", `mkdir -p '${StringUtils.shellSingleQuoteEscape(root.screenshotDir)}' && grim -o '${StringUtils.shellSingleQuoteEscape(root.screen.name)}' '${StringUtils.shellSingleQuoteEscape(root.screenshotPath)}'`] + screen: root.screen + screenshotDir: root.screenshotDir + screenshotPath: root.screenshotPath onExited: (exitCode, exitStatus) => { if (root.enableContentRegions) imageDetectionProcess.running = true; root.preparationDone = !checkRecordingProc.running; @@ -229,6 +226,27 @@ PanelWindow { } } + function getScreenshotAction() { + switch(root.action) { + case RegionSelection.SnipAction.Copy: + return ScreenshotAction.Action.Copy; + case RegionSelection.SnipAction.Edit: + return ScreenshotAction.Action.Edit; + case RegionSelection.SnipAction.Search: + return ScreenshotAction.Action.Search; + case RegionSelection.SnipAction.CharRecognition: + return ScreenshotAction.Action.CharRecognition; + case RegionSelection.SnipAction.Record: + return ScreenshotAction.Action.Record; + case RegionSelection.SnipAction.RecordWithSound: + return ScreenshotAction.Action.RecordWithSound; + default: + console.warn("[Region Selector] Unknown snip action, skipping snip."); + root.dismiss(); + return; + } + } + function snip() { // Validity check if (root.regionWidth <= 0 || root.regionHeight <= 0) { @@ -246,62 +264,20 @@ PanelWindow { if (root.action === RegionSelection.SnipAction.Copy || root.action === RegionSelection.SnipAction.Edit) { root.action = root.mouseButton === Qt.RightButton ? RegionSelection.SnipAction.Edit : RegionSelection.SnipAction.Copy; } - - // Set command for action - const rx = Math.round(root.regionX * root.monitorScale); - const ry = Math.round(root.regionY * root.monitorScale); - const rw = Math.round(root.regionWidth * root.monitorScale); - const rh = Math.round(root.regionHeight * root.monitorScale); - const cropBase = `magick ${StringUtils.shellSingleQuoteEscape(root.screenshotPath)} ` - + `-crop ${rw}x${rh}+${rx}+${ry}` - const cropToStdout = `${cropBase} -` - const cropInPlace = `${cropBase} '${StringUtils.shellSingleQuoteEscape(root.screenshotPath)}'` - const cleanup = `rm '${StringUtils.shellSingleQuoteEscape(root.screenshotPath)}'` - const slurpRegion = `${rx},${ry} ${rw}x${rh}` - const uploadAndGetUrl = (filePath) => { - return `curl -sF files[]=@'${StringUtils.shellSingleQuoteEscape(filePath)}' ${root.fileUploadApiEndpoint} | jq -r '.files[0].url'` - } - const annotationCommand = `${Config.options.regionSelector.annotation.useSatty ? "satty" : "swappy"} -f -`; - switch (root.action) { - case RegionSelection.SnipAction.Copy: - if (saveScreenshotDir === "") { - // not saving the screenshot, just copy to clipboard - snipProc.command = ["bash", "-c", `${cropToStdout} | wl-copy && ${cleanup}`] - break; - } - - const savePathBase = root.saveScreenshotDir - - snipProc.command = [ - "bash", "-c", - `mkdir -p '${StringUtils.shellSingleQuoteEscape(savePathBase)}' && \ - saveFileName="screenshot-$(date '+%Y-%m-%d_%H.%M.%S').png" && \ - savePath="${savePathBase}/$saveFileName" && \ - ${cropToStdout} | tee >(wl-copy) > "$savePath" && \ - ${cleanup}` - ] - - break; - case RegionSelection.SnipAction.Edit: - snipProc.command = ["bash", "-c", `${cropToStdout} | ${annotationCommand} && ${cleanup}`] - break; - case RegionSelection.SnipAction.Search: - snipProc.command = ["bash", "-c", `${cropInPlace} && xdg-open "${root.imageSearchEngineBaseUrl}$(${uploadAndGetUrl(root.screenshotPath)})" && ${cleanup}`] - break; - case RegionSelection.SnipAction.CharRecognition: - snipProc.command = ["bash", "-c", `${cropInPlace} && tesseract '${StringUtils.shellSingleQuoteEscape(root.screenshotPath)}' stdout -l $(tesseract --list-langs | awk 'NR>1{print $1}' | tr '\\n' '+' | sed 's/\\+$/\\n/') | wl-copy && ${cleanup}`] - break; - case RegionSelection.SnipAction.Record: - snipProc.command = ["bash", "-c", `${Directories.recordScriptPath} --region '${slurpRegion}'`] - break; - case RegionSelection.SnipAction.RecordWithSound: - snipProc.command = ["bash", "-c", `${Directories.recordScriptPath} --region '${slurpRegion}' --sound`] - break; - default: - console.warn("[Region Selector] Unknown snip action, skipping snip."); - root.dismiss(); - return; - } + + const screenshotDir = Config.options.screenSnip.savePath !== "" ? // + Config.options.screenSnip.savePath : ""; + var screenshotAction = root.getScreenshotAction(); + const command = ScreenshotAction.getCommand( + root.regionX * root.monitorScale, // + root.regionY * root.monitorScale, // + root.regionWidth * root.monitorScale,// + root.regionHeight * root.monitorScale, // + root.screenshotPath, // + screenshotAction, // + screenshotDir + ) + snipProc.command = command; // Image post-processing snipProc.startDetached(); @@ -399,6 +375,14 @@ PanelWindow { } } + CursorGuide { + z: 9999 + x: root.dragging ? root.regionX + root.regionWidth : mouseArea.mouseX + y: root.dragging ? root.regionY + root.regionHeight : mouseArea.mouseY + action: root.action + selectionMode: root.selectionMode + } + // Window regions Repeater { model: ScriptModel { @@ -471,7 +455,7 @@ PanelWindow { // Controls Row { id: regionSelectionControls - z: 9999 + z: 10 anchors { horizontalCenter: parent.horizontalCenter bottom: parent.bottom diff --git a/dots/.config/quickshell/ii/modules/ii/regionSelector/TargetRegion.qml b/dots/.config/quickshell/ii/modules/ii/regionSelector/TargetRegion.qml index a1ecbcd0f..d3763e0ab 100644 --- a/dots/.config/quickshell/ii/modules/ii/regionSelector/TargetRegion.qml +++ b/dots/.config/quickshell/ii/modules/ii/regionSelector/TargetRegion.qml @@ -25,6 +25,10 @@ Rectangle { border.width: targeted ? 4 : 2 radius: 4 + Behavior on color { + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) + } + visible: opacity > 0 Behavior on opacity { animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) diff --git a/dots/.config/quickshell/ii/modules/ii/sessionScreen/SessionScreen.qml b/dots/.config/quickshell/ii/modules/ii/sessionScreen/SessionScreen.qml index 6350d18a9..78b93c4d0 100644 --- a/dots/.config/quickshell/ii/modules/ii/sessionScreen/SessionScreen.qml +++ b/dots/.config/quickshell/ii/modules/ii/sessionScreen/SessionScreen.qml @@ -45,9 +45,7 @@ Scope { WlrLayershell.namespace: "quickshell:session" WlrLayershell.layer: WlrLayer.Overlay WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive - // This is a big surface so we needa carefully choose the transparency, - // or we'll get a large scary rgb blob - color: ColorUtils.transparentize(Appearance.m3colors.m3background, Appearance.m3colors.darkmode ? 0.04 : 0.12) + color: ColorUtils.transparentize(Appearance.m3colors.m3background, Appearance.m3colors.darkmode ? 0.05 : 0.12) anchors { top: true @@ -240,7 +238,7 @@ Scope { } } - RowLayout { + ColumnLayout { anchors { top: contentColumn.bottom topMargin: 10 @@ -249,19 +247,22 @@ Scope { spacing: 10 Loader { - active: SessionWarnings.packageManagerRunning + Layout.alignment: Qt.AlignHCenter + active: SessionWarnings.downloadRunning visible: active sourceComponent: DescriptionLabel { - text: Translation.tr("Your package manager is running") + text: Translation.tr("There might be a download in progress. Check your Downloads folder.") textColor: Appearance.m3colors.m3onErrorContainer color: Appearance.m3colors.m3errorContainer } } + Loader { - active: SessionWarnings.downloadRunning + Layout.alignment: Qt.AlignHCenter + active: SessionWarnings.packageManagerRunning visible: active sourceComponent: DescriptionLabel { - text: Translation.tr("There might be a download in progress") + text: Translation.tr("Your package manager is running") textColor: Appearance.m3colors.m3onErrorContainer color: Appearance.m3colors.m3errorContainer } diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarLeft/AiChat.qml b/dots/.config/quickshell/ii/modules/ii/sidebarLeft/AiChat.qml index 820626b51..3dc181fe4 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarLeft/AiChat.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarLeft/AiChat.qml @@ -314,7 +314,10 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) implicitWidth: statusRowLayout.implicitWidth + 10 * 2 implicitHeight: Math.max(statusRowLayout.implicitHeight, 38) radius: Appearance.rounding.normal - root.padding - color: Appearance.colors.colLayer2 + color: messageListView.atYBeginning ? Appearance.colors.colLayer2 : Appearance.colors.colLayer2Base + Behavior on color { + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) + } RowLayout { id: statusRowLayout anchors.centerIn: parent diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarLeft/SidebarLeft.qml b/dots/.config/quickshell/ii/modules/ii/sidebarLeft/SidebarLeft.qml index fc275976c..d58deb3f1 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarLeft/SidebarLeft.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarLeft/SidebarLeft.qml @@ -67,6 +67,7 @@ Scope { // Scope onDetachChanged: { if (root.detach) { + GlobalFocusGrab.removeDismissable(sidebarLoader.item) // Remove sidebar from the focus grab system sidebarContent.parent = null; // Detach content from sidebar sidebarLoader.active = false; // Unload sidebar detachedSidebarLoader.active = true; // Load detached window @@ -84,11 +85,11 @@ Scope { // Scope active: true sourceComponent: PanelWindow { // Window - id: sidebarRoot + id: panelWindow visible: GlobalStates.sidebarLeftOpen property bool extend: false - property real sidebarWidth: sidebarRoot.extend ? Appearance.sizes.sidebarWidthExtended : Appearance.sizes.sidebarWidth + property real sidebarWidth: panelWindow.extend ? Appearance.sizes.sidebarWidthExtended : Appearance.sizes.sidebarWidth property var contentParent: sidebarLeftBackground function hide() { @@ -113,15 +114,17 @@ Scope { // Scope item: sidebarLeftBackground } - HyprlandFocusGrab { // Click outside to close - id: grab - windows: [ sidebarRoot ] - active: sidebarRoot.visible && !root.pin - onActiveChanged: { // Focus the selected tab - if (active) sidebarLeftBackground.children[0].focusActiveItem() + onVisibleChanged: { + if (visible) { + GlobalFocusGrab.addDismissable(panelWindow); + } else { + GlobalFocusGrab.removeDismissable(panelWindow); } - onCleared: () => { - if (!active) sidebarRoot.hide() + } + Connections { + target: GlobalFocusGrab + function onDismissed() { + panelWindow.hide(); } } @@ -136,7 +139,7 @@ Scope { // Scope anchors.left: parent.left anchors.topMargin: Appearance.sizes.hyprlandGapsOut anchors.leftMargin: Appearance.sizes.hyprlandGapsOut - width: sidebarRoot.sidebarWidth - Appearance.sizes.hyprlandGapsOut - Appearance.sizes.elevationMargin + width: panelWindow.sidebarWidth - Appearance.sizes.hyprlandGapsOut - Appearance.sizes.elevationMargin height: parent.height - Appearance.sizes.hyprlandGapsOut * 2 color: Appearance.colors.colLayer0 border.width: 1 @@ -149,11 +152,11 @@ Scope { // Scope Keys.onPressed: (event) => { if (event.key === Qt.Key_Escape) { - sidebarRoot.hide(); + panelWindow.hide(); } if (event.modifiers === Qt.ControlModifier) { if (event.key === Qt.Key_O) { - sidebarRoot.extend = !sidebarRoot.extend; + panelWindow.extend = !panelWindow.extend; } else if (event.key === Qt.Key_D) { root.toggleDetach(); } else if (event.key === Qt.Key_P) { diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarLeft/SidebarLeftContent.qml b/dots/.config/quickshell/ii/modules/ii/sidebarLeft/SidebarLeftContent.qml index f3ebfd356..654b46dfc 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarLeft/SidebarLeftContent.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarLeft/SidebarLeftContent.qml @@ -48,6 +48,7 @@ Item { spacing: sidebarPadding Toolbar { + visible: tabButtonList.length > 0 Layout.alignment: Qt.AlignHCenter enableShadow: false ToolbarTabBar { @@ -83,9 +84,10 @@ Item { } contentChildren: [ - ...((root.aiChatEnabled || (!root.translatorEnabled && !root.animeEnabled)) ? [aiChat.createObject()] : []), + ...(root.aiChatEnabled ? [aiChat.createObject()] : []), ...(root.translatorEnabled ? [translator.createObject()] : []), - ...(root.animeEnabled ? [anime.createObject()] : []) + ...((root.tabButtonList.length === 0 || (!root.aiChatEnabled && !root.translatorEnabled && root.animeCloset)) ? [placeholder.createObject()] : []), + ...(root.animeEnabled ? [anime.createObject()] : []), ] } } @@ -102,6 +104,15 @@ Item { id: anime Anime {} } - + Component { + id: placeholder + Item { + StyledText { + anchors.centerIn: parent + text: root.animeCloset ? Translation.tr("Nothing") : Translation.tr("Enjoy your empty sidebar...") + color: Appearance.colors.colSubtext + } + } + } } } \ No newline at end of file diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarLeft/anime/BooruImage.qml b/dots/.config/quickshell/ii/modules/ii/sidebarLeft/anime/BooruImage.qml index e403417ba..feeeeb05b 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarLeft/anime/BooruImage.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarLeft/anime/BooruImage.qml @@ -126,7 +126,7 @@ Button { opacity: root.showActions ? 1 : 0 visible: opacity > 0 radius: Appearance.rounding.small - color: Appearance.colors.colSurfaceContainer + color: Appearance.m3colors.m3surfaceContainer implicitHeight: contextMenuColumnLayout.implicitHeight + radius * 2 implicitWidth: contextMenuColumnLayout.implicitWidth diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarRight/BottomWidgetGroup.qml b/dots/.config/quickshell/ii/modules/ii/sidebarRight/BottomWidgetGroup.qml index 4c6ea6dd9..e9c49dd4b 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarRight/BottomWidgetGroup.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarRight/BottomWidgetGroup.qml @@ -138,7 +138,7 @@ Rectangle { anchors.fill: parent // implicitHeight: tabStack.implicitHeight - spacing: 10 + spacing: 20 // Navigation rail Item { @@ -146,7 +146,7 @@ Rectangle { Layout.fillWidth: false Layout.leftMargin: 10 Layout.topMargin: 10 - width: tabBar.width + implicitWidth: tabBar.implicitWidth // Navigation rail buttons NavigationRailTabArray { id: tabBar diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarRight/SidebarRight.qml b/dots/.config/quickshell/ii/modules/ii/sidebarRight/SidebarRight.qml index fcba76710..e7da61e44 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarRight/SidebarRight.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarRight/SidebarRight.qml @@ -12,18 +12,17 @@ Scope { property int sidebarWidth: Appearance.sizes.sidebarWidth PanelWindow { - id: sidebarRoot + id: panelWindow visible: GlobalStates.sidebarRightOpen function hide() { - GlobalStates.sidebarRightOpen = false + GlobalStates.sidebarRightOpen = false; } exclusiveZone: 0 implicitWidth: sidebarWidth WlrLayershell.namespace: "quickshell:sidebarRight" - // Hyprland 0.49: Focus is always exclusive and setting this breaks mouse focus grab - // WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive + WlrLayershell.keyboardFocus: GlobalStates.sidebarRightOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None color: "transparent" anchors { @@ -32,12 +31,17 @@ Scope { bottom: true } - HyprlandFocusGrab { - id: grab - windows: [ sidebarRoot ] - active: GlobalStates.sidebarRightOpen - onCleared: () => { - if (!active) sidebarRoot.hide() + onVisibleChanged: { + if (visible) { + GlobalFocusGrab.addDismissable(panelWindow); + } else { + GlobalFocusGrab.removeDismissable(panelWindow); + } + } + Connections { + target: GlobalFocusGrab + function onDismissed() { + panelWindow.hide(); } } @@ -53,16 +57,14 @@ Scope { height: parent.height - Appearance.sizes.hyprlandGapsOut * 2 focus: GlobalStates.sidebarRightOpen - Keys.onPressed: (event) => { + Keys.onPressed: event => { if (event.key === Qt.Key_Escape) { - sidebarRoot.hide(); + panelWindow.hide(); } } sourceComponent: SidebarRightContent {} } - - } IpcHandler { @@ -105,5 +107,4 @@ Scope { GlobalStates.sidebarRightOpen = false; } } - } diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarRight/bluetoothDevices/BluetoothDeviceItem.qml b/dots/.config/quickshell/ii/modules/ii/sidebarRight/bluetoothDevices/BluetoothDeviceItem.qml index 8c713d3ca..e9d817563 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarRight/bluetoothDevices/BluetoothDeviceItem.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarRight/bluetoothDevices/BluetoothDeviceItem.qml @@ -47,6 +47,7 @@ DialogListItem { color: Appearance.colors.colOnSurfaceVariant elide: Text.ElideRight text: root.device?.name || Translation.tr("Unknown device") + textFormat: Text.PlainText } StyledText { visible: (root.device?.connected || root.device?.paired) ?? false diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarRight/todo/TodoWidget.qml b/dots/.config/quickshell/ii/modules/ii/sidebarRight/todo/TodoWidget.qml index 36c885523..8341887fd 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarRight/todo/TodoWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarRight/todo/TodoWidget.qml @@ -139,7 +139,7 @@ Item { anchors.margins: root.dialogMargins implicitHeight: dialogColumnLayout.implicitHeight - color: Appearance.colors.colSurfaceContainerHigh + color: Appearance.m3colors.m3surfaceContainerHigh radius: Appearance.rounding.normal function addTask() { diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarRight/wifiNetworks/WifiNetworkItem.qml b/dots/.config/quickshell/ii/modules/ii/sidebarRight/wifiNetworks/WifiNetworkItem.qml index 9f75224dd..9d411c4d9 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarRight/wifiNetworks/WifiNetworkItem.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarRight/wifiNetworks/WifiNetworkItem.qml @@ -40,6 +40,7 @@ DialogListItem { color: Appearance.colors.colOnSurfaceVariant elide: Text.ElideRight text: root.wifiNetwork?.ssid ?? Translation.tr("Unknown") + textFormat: Text.PlainText } MaterialSymbol { visible: (root.wifiNetwork?.isSecure || root.wifiNetwork?.active) ?? false diff --git a/dots/.config/quickshell/ii/modules/ii/verticalBar/VerticalBar.qml b/dots/.config/quickshell/ii/modules/ii/verticalBar/VerticalBar.qml index 3851f06d3..bf11091d6 100644 --- a/dots/.config/quickshell/ii/modules/ii/verticalBar/VerticalBar.qml +++ b/dots/.config/quickshell/ii/modules/ii/verticalBar/VerticalBar.qml @@ -66,6 +66,7 @@ Scope { } color: "transparent" + // Positioning anchors { left: !Config.options.bar.bottom right: Config.options.bar.bottom @@ -73,6 +74,14 @@ Scope { bottom: true } + // Include in focus grab + Component.onCompleted: { + GlobalFocusGrab.addPersistent(barRoot); + } + Component.onDestruction: { + GlobalFocusGrab.removePersistent(barRoot); + } + MouseArea { id: hoverRegion hoverEnabled: true diff --git a/dots/.config/quickshell/ii/modules/ii/wallpaperSelector/WallpaperDirectoryItem.qml b/dots/.config/quickshell/ii/modules/ii/wallpaperSelector/WallpaperDirectoryItem.qml index 23f7915ef..a2720605d 100644 --- a/dots/.config/quickshell/ii/modules/ii/wallpaperSelector/WallpaperDirectoryItem.qml +++ b/dots/.config/quickshell/ii/modules/ii/wallpaperSelector/WallpaperDirectoryItem.qml @@ -73,7 +73,7 @@ MouseArea { target: Wallpapers function onThumbnailGenerated(directory) { if (thumbnailImage.status !== Image.Error) return; - if (FileUtils.parentDirectory(thumbnailImage.sourcePath) !== directory) return; + if (FileUtils.parentDirectory(thumbnailImage.sourcePath) !== FileUtils.trimFileProtocol(directory)) return; thumbnailImage.source = ""; thumbnailImage.source = thumbnailImage.thumbnailPath; } diff --git a/dots/.config/quickshell/ii/modules/ii/wallpaperSelector/WallpaperSelector.qml b/dots/.config/quickshell/ii/modules/ii/wallpaperSelector/WallpaperSelector.qml index 197acbf2e..9d9ba9008 100644 --- a/dots/.config/quickshell/ii/modules/ii/wallpaperSelector/WallpaperSelector.qml +++ b/dots/.config/quickshell/ii/modules/ii/wallpaperSelector/WallpaperSelector.qml @@ -25,6 +25,7 @@ Scope { exclusionMode: ExclusionMode.Ignore WlrLayershell.namespace: "quickshell:wallpaperSelector" WlrLayershell.layer: WlrLayer.Overlay + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand color: "transparent" anchors.top: true @@ -39,12 +40,16 @@ Scope { implicitHeight: Appearance.sizes.wallpaperSelectorHeight implicitWidth: Appearance.sizes.wallpaperSelectorWidth - HyprlandFocusGrab { // Click outside to close - id: grab - windows: [ panelWindow ] - active: wallpaperSelectorLoader.active - onCleared: () => { - if (!active) GlobalStates.wallpaperSelectorOpen = false; + Component.onCompleted: { + GlobalFocusGrab.addDismissable(panelWindow); + } + Component.onDestruction: { + GlobalFocusGrab.removeDismissable(panelWindow); + } + Connections { + target: GlobalFocusGrab + function onDismissed() { + GlobalStates.wallpaperSelectorOpen = false; } } diff --git a/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml b/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml index 66630cc24..80814146f 100644 --- a/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml @@ -53,9 +53,9 @@ ContentPage { } ContentSection { + id: settingsClock icon: "clock_loader_40" title: Translation.tr("Widget: Clock") - id: settingsClock function stylePresent(styleName) { if (!Config.options.background.widgets.clock.showOnlyWhenLocked && Config.options.background.widgets.clock.style === styleName) { @@ -120,61 +120,161 @@ ContentPage { } } - ContentSubsection { - visible: !Config.options.background.widgets.clock.showOnlyWhenLocked - title: Translation.tr("Clock style") - ConfigSelectionArray { - currentValue: Config.options.background.widgets.clock.style - onSelected: newValue => { - Config.options.background.widgets.clock.style = newValue; - } - options: [ - { - displayName: Translation.tr("Digital"), - icon: "timer_10", - value: "digital" - }, - { - displayName: Translation.tr("Cookie"), - icon: "cookie", - value: "cookie" + ConfigRow { + ContentSubsection { + visible: !Config.options.background.widgets.clock.showOnlyWhenLocked + title: Translation.tr("Clock style") + Layout.fillWidth: true + ConfigSelectionArray { + currentValue: Config.options.background.widgets.clock.style + onSelected: newValue => { + Config.options.background.widgets.clock.style = newValue; } - ] + options: [ + { + displayName: Translation.tr("Digital"), + icon: "timer_10", + value: "digital" + }, + { + displayName: Translation.tr("Cookie"), + icon: "cookie", + value: "cookie" + } + ] + } } - } - ContentSubsection { - title: Translation.tr("Clock style (locked)") - ConfigSelectionArray { - currentValue: Config.options.background.widgets.clock.styleLocked - onSelected: newValue => { - Config.options.background.widgets.clock.styleLocked = newValue; - } - options: [ - { - displayName: Translation.tr("Digital"), - icon: "timer_10", - value: "digital" - }, - { - displayName: Translation.tr("Cookie"), - icon: "cookie", - value: "cookie" + ContentSubsection { + title: Translation.tr("Clock style (locked)") + Layout.fillWidth: false + ConfigSelectionArray { + currentValue: Config.options.background.widgets.clock.styleLocked + onSelected: newValue => { + Config.options.background.widgets.clock.styleLocked = newValue; } - ] + options: [ + { + displayName: Translation.tr("Digital"), + icon: "timer_10", + value: "digital" + }, + { + displayName: Translation.tr("Cookie"), + icon: "cookie", + value: "cookie" + } + ] + } } } ContentSubsection { visible: settingsClock.digitalPresent title: Translation.tr("Digital clock settings") + tooltip: Translation.tr("Font width and roundness settings are only available for some fonts like Google Sans Flex") - ConfigSwitch { - buttonIcon: "animation" - text: Translation.tr("Animate time change") - checked: Config.options.background.widgets.clock.digital.animateChange - onCheckedChanged: { - Config.options.background.widgets.clock.digital.animateChange = checked; + ConfigRow { + uniform: true + ConfigSwitch { + buttonIcon: "vertical_distribute" + text: Translation.tr("Vertical") + checked: Config.options.background.widgets.clock.digital.vertical + onCheckedChanged: { + Config.options.background.widgets.clock.digital.vertical = checked; + } + } + ConfigSwitch { + buttonIcon: "animation" + text: Translation.tr("Animate time change") + checked: Config.options.background.widgets.clock.digital.animateChange + onCheckedChanged: { + Config.options.background.widgets.clock.digital.animateChange = checked; + } + } + } + + ConfigRow { + uniform: true + + ConfigSwitch { + buttonIcon: "date_range" + text: Translation.tr("Show date") + checked: Config.options.background.widgets.clock.digital.showDate + onCheckedChanged: { + Config.options.background.widgets.clock.digital.showDate = checked; + } + } + ConfigSwitch { + buttonIcon: "activity_zone" + text: Translation.tr("Use adaptive alignment") + checked: Config.options.background.widgets.clock.digital.adaptiveAlignment + onCheckedChanged: { + Config.options.background.widgets.clock.digital.adaptiveAlignment = checked; + } + StyledToolTip { + text: Translation.tr("Aligns the date and quote to left, center or right depending on its position on the screen.") + } + } + } + + MaterialTextArea { + Layout.fillWidth: true + placeholderText: Translation.tr("Font family") + text: Config.options.background.widgets.clock.digital.font.family + wrapMode: TextEdit.Wrap + onTextChanged: { + Config.options.background.widgets.clock.digital.font.family = text; + } + } + + ConfigSlider { + text: Translation.tr("Font weight") + value: Config.options.background.widgets.clock.digital.font.weight + usePercentTooltip: false + buttonIcon: "format_bold" + from: 1 + to: 1000 + stopIndicatorValues: [350] + onValueChanged: { + Config.options.background.widgets.clock.digital.font.weight = value; + } + } + + ConfigSlider { + text: Translation.tr("Font size") + value: Config.options.background.widgets.clock.digital.font.size + usePercentTooltip: false + buttonIcon: "format_size" + from: 50 + to: 700 + stopIndicatorValues: [90] + onValueChanged: { + Config.options.background.widgets.clock.digital.font.size = value; + } + } + + ConfigSlider { + text: Translation.tr("Font width") + value: Config.options.background.widgets.clock.digital.font.width + usePercentTooltip: false + buttonIcon: "fit_width" + from: 25 + to: 125 + stopIndicatorValues: [100] + onValueChanged: { + Config.options.background.widgets.clock.digital.font.width = value; + } + } + ConfigSlider { + text: Translation.tr("Font roundness") + value: Config.options.background.widgets.clock.digital.font.roundness + usePercentTooltip: false + buttonIcon: "line_curve" + from: 0 + to: 100 + onValueChanged: { + Config.options.background.widgets.clock.digital.font.roundness = value; } } } diff --git a/dots/.config/quickshell/ii/modules/settings/QuickConfig.qml b/dots/.config/quickshell/ii/modules/settings/QuickConfig.qml index c9c540afa..5964f4dff 100644 --- a/dots/.config/quickshell/ii/modules/settings/QuickConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/QuickConfig.qml @@ -141,7 +141,7 @@ ContentPage { key: "Ctrl" } KeyboardKey { - key: "󰖳" + key: Config.options.cheatsheet.superKey ?? "󰖳" } StyledText { Layout.alignment: Qt.AlignVCenter @@ -225,9 +225,6 @@ ContentPage { onCheckedChanged: { Config.options.appearance.transparency.enable = checked; } - StyledToolTip { - text: Translation.tr("Might look ass. Unsupported.") - } } } diff --git a/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml b/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml index f5931bb1b..aec8dc98a 100644 --- a/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/ServicesConfig.qml @@ -203,6 +203,32 @@ ContentPage { } } + // There's no update indicator in ii for now so we shouldn't show this yet + // ContentSection { + // icon: "deployed_code_update" + // title: Translation.tr("System updates (Arch only)") + + // ConfigSwitch { + // text: Translation.tr("Enable update checks") + // checked: Config.options.updates.enableCheck + // onCheckedChanged: { + // Config.options.updates.enableCheck = checked; + // } + // } + + // ConfigSpinBox { + // icon: "av_timer" + // text: Translation.tr("Check interval (mins)") + // value: Config.options.updates.checkInterval + // from: 60 + // to: 1440 + // stepSize: 60 + // onValueChanged: { + // Config.options.updates.checkInterval = value; + // } + // } + // } + ContentSection { icon: "weather_mix" title: Translation.tr("Weather") diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/bluetooth/BluetoothDeviceItem.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/bluetooth/BluetoothDeviceItem.qml index 73d7bee83..1d9af6c63 100644 --- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/bluetooth/BluetoothDeviceItem.qml +++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/bluetooth/BluetoothDeviceItem.qml @@ -41,6 +41,7 @@ ExpandableChoiceButton { elide: Text.ElideRight font.pixelSize: Looks.font.pixelSize.large text: root.device?.name || Translation.tr("Unknown device") + textFormat: Text.PlainText } WText { // Status id: statusText diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WWifiNetworkItem.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WWifiNetworkItem.qml index 9274b2e86..ff3d09eb6 100644 --- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WWifiNetworkItem.qml +++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WWifiNetworkItem.qml @@ -61,6 +61,7 @@ ExpandableChoiceButton { elide: Text.ElideRight font.pixelSize: Looks.font.pixelSize.large text: root.wifiNetwork?.ssid ?? Translation.tr("Unknown") + textFormat: Text.PlainText } WText { // Status id: statusText diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/WaffleBar.qml b/dots/.config/quickshell/ii/modules/waffle/bar/WaffleBar.qml index 2326a47f7..b69b7c8e2 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/WaffleBar.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/WaffleBar.qml @@ -13,7 +13,7 @@ Scope { LazyLoader { id: barLoader - active: GlobalStates.barOpen && !GlobalStates.screenLocked + active: GlobalStates.barOpen component: Variants { model: Quickshell.screens delegate: PanelWindow { // Bar window diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/tasks/TaskAppButton.qml b/dots/.config/quickshell/ii/modules/waffle/bar/tasks/TaskAppButton.qml index 51e6b61a1..d4d7c072b 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/tasks/TaskAppButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/tasks/TaskAppButton.qml @@ -12,7 +12,20 @@ AppButton { required property var appEntry readonly property bool isSeparator: appEntry.appId === "SEPARATOR" - readonly property var desktopEntry: DesktopEntries.heuristicLookup(appEntry.appId) + property var desktopEntry: DesktopEntries.heuristicLookup(appEntry.appId) + + Timer { + // Retry looking up the desktop entry if it failed (e.g. database not loaded yet) + property int retryCount: 5 + interval: 1000 + running: !root.isSeparator && root.desktopEntry === null && retryCount > 0 + repeat: true + onTriggered: { + retryCount--; + root.desktopEntry = DesktopEntries.heuristicLookup(root.appEntry.appId); + } + } + property bool active: root.appEntry.toplevels.some(t => t.activated) property bool hasWindows: appEntry.toplevels.length > 0 diff --git a/dots/.config/quickshell/ii/modules/waffle/lock/WaffleLock.qml b/dots/.config/quickshell/ii/modules/waffle/lock/WaffleLock.qml index 581ce9152..b1ff6f353 100644 --- a/dots/.config/quickshell/ii/modules/waffle/lock/WaffleLock.qml +++ b/dots/.config/quickshell/ii/modules/waffle/lock/WaffleLock.qml @@ -36,15 +36,34 @@ LockScreen { Image { id: bg z: 0 - anchors.fill: parent + width: parent.width + height: parent.height + onStatusChanged: { + if (status === Image.Ready) { + print("Lock wallpaper loaded"); + print(lockSurfaceItem.height); + y = -lockSurfaceItem.height; + openAnim.restart(); + } + } sourceSize: Qt.size(lockSurfaceItem.width, lockSurfaceItem.height) source: Config.options.background.wallpaperPath fillMode: Image.PreserveAspectCrop + + PropertyAnimation { + id: openAnim + target: bg + property: "y" + to: 0 + duration: 350 + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn + } } GaussianBlur { z: 1 - anchors.fill: parent + anchors.fill: bg source: bg radius: 100 samples: radius * 2 + 1 @@ -67,7 +86,7 @@ LockScreen { Interactables { id: interactables z: 2 - anchors.fill: parent + anchors.fill: bg } } @@ -83,12 +102,31 @@ LockScreen { // } function switchToFocusedView() { - root.passwordView = true; + switchToPasswordViewAnim.restart(); + } + + SequentialAnimation { + id: switchToPasswordViewAnim + PropertyAnimation { + target: unfocusedContent + property: "y" + from: 0 + to: -height * 1.1 + duration: 250 + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn + } + ScriptAction { + script: { + root.passwordView = true; + } + } } Item { id: unfocusedContent - anchors.fill: parent + width: parent.width + height: parent.height visible: !root.passwordView ClockTextGroup { anchors { diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml index 8120aa85e..7bad59e84 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml @@ -17,8 +17,9 @@ Singleton { property string iconsPath: `${Directories.assetsPath}/icons/fluent` property bool dark: Appearance.m3colors.darkmode - property real backgroundTransparency: 0.16 - property real panelBackgroundTransparency: 0.14 + readonly property bool transparencyEnabled: Config.options.appearance.transparency.enable + property real backgroundTransparency: transparencyEnabled ? 0.16 : 0 + property real panelBackgroundTransparency: transparencyEnabled ? 0.14 : 0 property real panelLayerTransparency: root.dark ? 0.9 : 0.7 property real contentTransparency: root.dark ? 0.87 : 0.5 function applyBackgroundTransparency(col) { @@ -27,23 +28,22 @@ Singleton { function applyContentTransparency(col) { return ColorUtils.applyAlpha(col, 1 - root.contentTransparency) } - lightColors: QtObject { // TODO: figure out transparency + lightColors: QtObject { id: lightColors - property color bgPanelFooter: "#EEEEEE" property color bgPanelBody: "#F2F2F2" property color bgPanelSeparator: "#E0E0E0" property color bg0: "#EEEEEE" - property color bg0Border: '#adadad' - property color bg1: "#F7F7F7" + property color bg0Border: '#BEBEBE' property color bg1Base: "#F7F7F7" + property color bg1: "#F7F7F7" property color bg1Hover: "#F7F7F7" property color bg1Active: '#EFEFEF' - property color bg1Border: '#d7d7d7' + property color bg1Border: '#E9E9E9' property color bg2: "#FBFBFB" property color bg2Base: "#FBFBFB" property color bg2Hover: '#ffffff' property color bg2Active: '#eeeeee' - property color bg2Border: '#cdcdcd' + property color bg2Border: '#E0E0E0' property color subfg: "#5C5C5C" property color fg: "#000000" property color fg1: "#626262" @@ -58,21 +58,20 @@ Singleton { } darkColors: QtObject { id: darkColors - property color bgPanelFooter: "#1C1C1C" - property color bgPanelBody: '#616161' + property color bgPanelBody: '#242424' property color bgPanelSeparator: "#191919" property color bg0: "#1C1C1C" property color bg0Border: "#404040" - property color bg1Base: "#2C2C2C" - property color bg1: '#9f9f9f' - property color bg1Hover: "#b3b3b3" - property color bg1Active: '#727272' + property color bg1Base: '#2C2C2C' + property color bg1: '#2C2C2C' + property color bg1Hover: "#292929" + property color bg1Active: '#252525' property color bg1Border: '#bebebe' property color bg2Base: "#313131" - property color bg2: '#8a8a8a' - property color bg2Hover: '#b1b1b1' - property color bg2Active: '#919191' - property color bg2Border: '#bdbdbd' + property color bg2: '#313131' + property color bg2Hover: '#363636' + property color bg2Active: '#2B2B2B' + property color bg2Border: '#404040' property color subfg: "#CED1D7" property color fg: "#FFFFFF" property color fg1: "#D1D1D1" @@ -87,38 +86,47 @@ Singleton { } colors: QtObject { id: colors + // Special property color shadow: ColorUtils.transparentize('#161616', 0.62) property color ambientShadow: ColorUtils.transparentize("#000000", 0.75) - property color bgPanelFooterBase: ColorUtils.transparentize(root.dark ? root.darkColors.bgPanelFooter : root.lightColors.bgPanelFooter, root.panelBackgroundTransparency) - property color bgPanelFooter: ColorUtils.transparentize(root.dark ? root.darkColors.bgPanelFooter : root.lightColors.bgPanelFooter, root.panelLayerTransparency) - property color bgPanelBody: ColorUtils.transparentize(root.dark ? root.darkColors.bgPanelBody : root.lightColors.bgPanelBody, root.panelLayerTransparency) - property color bgPanelSeparator: ColorUtils.transparentize(root.dark ? root.darkColors.bgPanelSeparator : root.lightColors.bgPanelSeparator, root.backgroundTransparency) - property color bg0Opaque: root.dark ? root.darkColors.bg0 : root.lightColors.bg0 - property color bg0: ColorUtils.transparentize(bg0Opaque, root.backgroundTransparency) + property color bgPanelFooterBase: root.dark ? root.darkColors.bg0 : root.lightColors.bg0 + property color bgPanelFooterBackground: ColorUtils.transparentize(root.dark ? root.darkColors.bg0 : root.lightColors.bg0, root.panelBackgroundTransparency) + property color bgPanelFooter: ColorUtils.transparentize(bgPanelFooterBackground, root.panelLayerTransparency) + property color bgPanelBodyBase: root.dark ? root.darkColors.bgPanelBody : root.lightColors.bgPanelBody + property color bgPanelBody: ColorUtils.solveOverlayColor(bgPanelFooterBackground,bgPanelBodyBase, 1 - root.panelLayerTransparency) + property color bgPanelSeparator: ColorUtils.solveOverlayColor(bgPanelBodyBase, root.dark ? root.darkColors.bgPanelSeparator : root.lightColors.bgPanelSeparator, 1 - root.panelBackgroundTransparency) + // Layer 0 + property color bg0Base: root.dark ? root.darkColors.bg0 : root.lightColors.bg0 + property color bg0: ColorUtils.transparentize(bg0Base, root.backgroundTransparency) property color bg0Border: ColorUtils.transparentize(root.dark ? root.darkColors.bg0Border : root.lightColors.bg0Border, root.backgroundTransparency) - property color bg1Base: root.dark ? root.darkColors.bg1Base : root.lightColors.bg1Base - property color bg1: ColorUtils.transparentize(root.dark ? root.darkColors.bg1 : root.lightColors.bg1, root.contentTransparency) - property color bg1Hover: ColorUtils.transparentize(root.dark ? root.darkColors.bg1Hover : root.lightColors.bg1Hover, root.contentTransparency) - property color bg1Active: ColorUtils.transparentize(root.dark ? root.darkColors.bg1Active : root.lightColors.bg1Active, root.contentTransparency) - property color bg1Border: ColorUtils.transparentize(root.dark ? root.darkColors.bg1Border : root.lightColors.bg1Border, root.contentTransparency) - property color bg2Base: root.dark ? root.darkColors.bg2Base : root.lightColors.bg2Base - property color bg2: ColorUtils.transparentize(root.dark ? root.darkColors.bg2 : root.lightColors.bg2, root.contentTransparency) - property color bg2Hover: ColorUtils.transparentize(root.dark ? root.darkColors.bg2Hover : root.lightColors.bg2Hover, root.contentTransparency) - property color bg2Active: ColorUtils.transparentize(root.dark ? root.darkColors.bg2Active : root.lightColors.bg2Active, root.contentTransparency) - property color bg2Border: ColorUtils.transparentize(root.dark ? root.darkColors.bg2Border : root.lightColors.bg2Border, root.contentTransparency) + // Layer 1 + property color bg1Base: root.dark ? root.darkColors.bg1 : root.lightColors.bg1 + property color bg1: ColorUtils.solveOverlayColor(bg0Base, bg1Base, 1 - root.contentTransparency) + property color bg1Hover: ColorUtils.solveOverlayColor(bg0Base, root.dark ? root.darkColors.bg1Hover : root.lightColors.bg1Hover, 1 - root.contentTransparency) + property color bg1Active: ColorUtils.solveOverlayColor(bg0Base, root.dark ? root.darkColors.bg1Active : root.lightColors.bg1Active, 1 - root.contentTransparency) + property color bg1Border: ColorUtils.solveOverlayColor(bg0Base, root.dark ? root.darkColors.bg1Border : root.lightColors.bg1Border, 1 - root.contentTransparency) + // Layer 2 + property color bg2Base: root.dark ? root.darkColors.bg2 : root.lightColors.bg2 + property color bg2: ColorUtils.solveOverlayColor(bgPanelBodyBase, bg2Base, 1 - root.contentTransparency) + property color bg2Hover: ColorUtils.solveOverlayColor(bgPanelBodyBase, root.dark ? root.darkColors.bg2Hover : root.lightColors.bg2Hover, 1 - root.contentTransparency) + property color bg2Active: ColorUtils.solveOverlayColor(bgPanelBodyBase, root.dark ? root.darkColors.bg2Active : root.lightColors.bg2Active, 1 - root.contentTransparency) + property color bg2Border: ColorUtils.solveOverlayColor(bgPanelBodyBase, root.dark ? root.darkColors.bg2Border : root.lightColors.bg2Border, 1 - root.contentTransparency) + // Foreground / Text property color subfg: root.dark ? root.darkColors.subfg : root.lightColors.subfg property color fg: root.dark ? root.darkColors.fg : root.lightColors.fg property color fg1: root.dark ? root.darkColors.fg1 : root.lightColors.fg1 property color inactiveIcon: root.dark ? root.darkColors.inactiveIcon : root.lightColors.inactiveIcon + property color link: root.dark ? root.darkColors.link : root.lightColors.link + // Controls property color controlBgInactive: root.dark ? root.darkColors.controlBgInactive : root.lightColors.controlBgInactive property color controlBg: root.dark ? root.darkColors.controlBg : root.lightColors.controlBg property color controlBgHover: root.dark ? root.darkColors.controlBgHover : root.lightColors.controlBgHover property color controlFg: root.dark ? root.darkColors.controlFg : root.lightColors.controlFg property color inputBg: root.dark ? root.darkColors.inputBg : root.lightColors.inputBg - property color link: root.dark ? root.darkColors.link : root.lightColors.link property color danger: "#C42B1C" property color dangerActive: "#B62D1F" property color warning: "#FF9900" + // Accent property color accent: Appearance.colors.colPrimary property color accentHover: Appearance.colors.colPrimaryHover property color accentActive: Appearance.colors.colPrimaryActive diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml index ceed470ba..d2cef0634 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml @@ -39,9 +39,10 @@ Button { } } property color fgColor: { + if (!root.enabled) return root.colForegroundDisabled if (root.checked) return root.colForegroundToggled if (root.enabled) return root.colForeground - return root.colForegroundDisabled + return root.colForeground } property alias horizontalAlignment: buttonText.horizontalAlignment font { diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml index 9f4f7f340..1d30576ec 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml @@ -76,8 +76,9 @@ Menu { contentItem: Item { implicitWidth: menuListView.implicitWidth implicitHeight: menuListView.implicitHeight - ListView { + WListView { id: menuListView + interactive: contentHeight > height anchors { left: parent.left right: parent.right @@ -87,6 +88,7 @@ Menu { topMargin: root.downDirection ? root.sourceEdgeMargin : root.margins bottomMargin: root.downDirection ? root.margins : root.sourceEdgeMargin } + clip: true implicitHeight: contentHeight implicitWidth: Array.from({ length: count diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WMenuItem.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WMenuItem.qml index 731b0d704..0a27c6cb3 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WMenuItem.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WMenuItem.qml @@ -6,6 +6,7 @@ import Quickshell import Quickshell.Hyprland import qs.modules.common import qs.modules.common.functions +import qs.modules.common.widgets import qs.modules.waffle.looks MenuItem { @@ -14,11 +15,11 @@ MenuItem { property color colBackground: ColorUtils.transparentize(Looks.colors.bg1) property color colBackgroundHover: Looks.colors.bg2Hover property color colBackgroundActive: Looks.colors.bg2Active - property color colBackgroundToggled: Looks.colors.accent - property color colBackgroundToggledHover: Looks.colors.accentHover - property color colBackgroundToggledActive: Looks.colors.accentActive + property color colBackgroundToggled: Looks.colors.bg2Hover + property color colBackgroundToggledHover: Looks.colors.bg2Active + property color colBackgroundToggledActive: Looks.colors.bg2Hover property color colForeground: Looks.colors.fg - property color colForegroundToggled: Looks.colors.accentFg + property color colForegroundToggled: Looks.colors.fg property color colForegroundDisabled: ColorUtils.transparentize(Looks.colors.subfg, 0.4) property color color: { if (!root.enabled) @@ -70,27 +71,57 @@ MenuItem { implicitHeight: Math.max(28, contentItem.implicitHeight) + topInset + bottomInset implicitWidth: contentItem.implicitWidth + leftInset + rightInset + leftPadding + rightPadding - contentItem: RowLayout { - id: contentLayout - spacing: 12 - FluentIcon { - id: buttonIcon - monochrome: true - implicitSize: 20 - Layout.fillWidth: false - Layout.alignment: Qt.AlignVCenter - color: root.fgColor - visible: root.icon.name !== ""; - icon: root.icon.name + contentItem: Item { + implicitWidth: contentLayout.implicitWidth + implicitHeight: contentLayout.implicitHeight + + RowLayout { + id: contentLayout + anchors.fill: parent + spacing: 12 + FluentIcon { + id: buttonIcon + monochrome: true + implicitSize: 20 + Layout.fillWidth: false + Layout.alignment: Qt.AlignVCenter + color: root.fgColor + visible: root.icon.name !== "" + icon: root.icon.name + } + WText { + id: buttonText + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft + text: root.text + horizontalAlignment: Text.AlignLeft + font.pixelSize: Looks.font.pixelSize.large + color: root.fgColor + } } - WText { - id: buttonText - Layout.fillWidth: true - Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft - text: root.text - horizontalAlignment: Text.AlignLeft - font.pixelSize: Looks.font.pixelSize.large - color: root.fgColor + + WFadeLoader { + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + leftMargin: -root.leftPadding + width + } + shown: root.checked + sourceComponent: Rectangle { + implicitWidth: 3 + implicitHeight: 3 + radius: width / 2 + color: Looks.colors.accent + property bool forceZeroHeight: true + height: forceZeroHeight ? 0 : Math.max(root.down ? 10 : 16, root.background.height - 18 * 2) + Component.onCompleted: { + forceZeroHeight = false; + } + + Behavior on height { + animation: Looks.transition.resize.createObject(this) + } + } } } } diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml index f281a8b7c..01f20d8d2 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml @@ -12,6 +12,7 @@ Item { id: root property Item contentItem property real radius: Looks.radius.large + property alias color: contentRect.color property alias border: borderRect property alias borderColor: borderRect.border.color property alias borderWidth: borderRect.border.width @@ -42,7 +43,7 @@ Item { anchors.centerIn: parent z: 0 - color: Looks.colors.bgPanelFooterBase + color: Looks.colors.bgPanelFooterBackground implicitWidth: contentItem.implicitWidth implicitHeight: contentItem.implicitHeight layer.enabled: true diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WToolbar.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WToolbar.qml new file mode 100644 index 000000000..6d58e8f7c --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WToolbar.qml @@ -0,0 +1,38 @@ +import QtQuick +import QtQuick.Layouts +import qs.modules.common +import qs.modules.common.widgets + +Item { + id: root + + property real padding: 9 + property alias colBackground: background.color + property alias spacing: toolbarLayout.spacing + property alias radius: background.radius + default property alias data: toolbarLayout.data + + implicitWidth: background.implicitWidth + implicitHeight: background.implicitHeight + + Rectangle { + id: background + anchors.fill: parent + implicitHeight: 50 + implicitWidth: toolbarLayout.implicitWidth + root.padding * 2 + radius: Looks.radius.large + color: Looks.colors.bg0Base + + border.width: 1 + border.color: Looks.colors.bg1Border + + RowLayout { + id: toolbarLayout + spacing: 4 + anchors { + fill: parent + margins: root.padding + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarButton.qml new file mode 100644 index 000000000..59dc5ee99 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarButton.qml @@ -0,0 +1,8 @@ +import QtQuick +import QtQuick.Layouts +import qs.modules.common + +WButton { + implicitHeight: 32 + radius: Looks.radius.medium +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarIconButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarIconButton.qml new file mode 100644 index 000000000..4a3644735 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarIconButton.qml @@ -0,0 +1,16 @@ +import QtQuick +import QtQuick.Layouts +import qs.modules.common + +WToolbarButton { + id: root + implicitWidth: height + contentItem: Item { + FluentIcon { + anchors.centerIn: parent + icon: root.icon.name + implicitSize: 18 + color: root.fgColor + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarIconTabButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarIconTabButton.qml new file mode 100644 index 000000000..0dc6ae208 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarIconTabButton.qml @@ -0,0 +1,21 @@ +import QtQuick +import QtQuick.Controls +import qs.modules.common + +TabButton { + id: root + + implicitWidth: 38 + implicitHeight: 32 + padding: 0 + + background: null + contentItem: Item { + FluentIcon { + anchors.centerIn: parent + icon: root.icon.name + color: root.icon.color + implicitSize: 18 + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarSeparator.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarSeparator.qml new file mode 100644 index 000000000..d67c4b767 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarSeparator.qml @@ -0,0 +1,11 @@ +import QtQuick +import QtQuick.Layouts +import qs.modules.common + +Rectangle { + Layout.leftMargin: 4 + Layout.rightMargin: 4 + implicitHeight: 24 + implicitWidth: 1 + color: Looks.colors.bg0Border +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarTabBar.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarTabBar.qml new file mode 100644 index 000000000..025ee53a4 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarTabBar.qml @@ -0,0 +1,53 @@ +import QtQuick +import QtQuick.Controls +import qs.modules.common +import qs.modules.common.functions + +TabBar { + id: root + implicitHeight: 32 + + background: Rectangle { + radius: Looks.radius.medium + color: Looks.colors.bgPanelFooter + border.color: ColorUtils.transparentize(Looks.colors.bg0Border, 0.7) + border.width: 1 + + // Indicator + Rectangle { + anchors { + top: parent.top + bottom: parent.bottom + left: parent.left + leftMargin: root.currentIndex * (root.width / root.count) + Behavior on leftMargin { + animation: Looks.transition.resize.createObject(this) + } + } + radius: Looks.radius.medium + color: Looks.colors.bg2Base + border.color: Looks.colors.bg0Border + border.width: 1 + width: root.width / root.count + + Rectangle { + anchors { + horizontalCenter: parent.horizontalCenter + bottom: parent.bottom + bottomMargin: 1 + } + implicitWidth: pressDetector.containsPress ? 16 : 12 + implicitHeight: 3 + radius: height / 2 + color: Looks.colors.accent + } + } + } + + MouseArea { + id: pressDetector + z: 9999 + anchors.fill: parent + acceptedButtons: Qt.LeftButton + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml index 7c01a0b01..16dc831e1 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml @@ -79,7 +79,7 @@ BodyRectangle { id: dayButton required property var model checked: model.today - enabled: hovered || calendarView.scrolling || checked || model.month === calendarView.focusedMonth + enabled: hovered || checked || model.month === calendarView.focusedMonth implicitWidth: calendarView.buttonSize implicitHeight: calendarView.buttonSize radius: height / 2 diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationPaneContent.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationPaneContent.qml index 5e9582bf1..96669e353 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationPaneContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationPaneContent.qml @@ -7,7 +7,7 @@ import qs.modules.common.widgets import qs.modules.common.functions import qs.modules.waffle.looks -BodyRectangle { +FooterRectangle { id: root anchors.fill: parent implicitHeight: 230 diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml index 350e7b0ab..e7fb58a12 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml @@ -15,6 +15,9 @@ MouseArea { required property var notification property bool expanded: notification.actions.length > 0 property string groupExpandControlMessage: "" + + readonly property bool isPopup: notification?.popup ?? false + signal groupExpandToggle hoverEnabled: true @@ -56,13 +59,13 @@ MouseArea { Rectangle { id: contentItem width: parent.width - color: Looks.colors.bgPanelBody - radius: Looks.radius.medium + color: root.isPopup ? Looks.colors.bg0 : Looks.colors.bgPanelBody + radius: root.isPopup ? Looks.radius.large : Looks.radius.medium property real padding: 12 implicitHeight: notificationContent.implicitHeight + padding * 2 implicitWidth: notificationContent.implicitWidth + padding * 2 border.width: 1 - border.color: ColorUtils.applyAlpha(Looks.colors.ambientShadow, 0.1) + border.color: root.isPopup ? Looks.colors.bg2Border : Looks.colors.bgPanelSeparator Behavior on x { animation: Looks.transition.enter.createObject(this) @@ -157,9 +160,9 @@ MouseArea { NotificationHeaderButton { Layout.rightMargin: 4 - opacity: root.containsMouse ? 1 : 0 + opacity: (root.containsMouse || root.isPopup) ? 1 : 0 icon.name: "dismiss" - implicitSize: 12 + implicitSize: 14 onClicked: root.dismiss() } } diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationPopup/WaffleNotificationPopup.qml b/dots/.config/quickshell/ii/modules/waffle/notificationPopup/WaffleNotificationPopup.qml new file mode 100644 index 000000000..7deb54c30 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/notificationPopup/WaffleNotificationPopup.qml @@ -0,0 +1,66 @@ +import QtQuick +import QtQuick.Controls +import Quickshell +import Quickshell.Wayland +import Quickshell.Hyprland +import qs +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.waffle.looks +import qs.modules.waffle.notificationCenter + +Scope { + id: notificationPopup + + PanelWindow { + id: root + visible: (Notifications.popupList.length > 0) && !GlobalStates.screenLocked + screen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name) ?? null + + WlrLayershell.namespace: "quickshell:notificationPopup" + WlrLayershell.layer: WlrLayer.Overlay + exclusiveZone: 0 + + anchors { + top: true + right: true + bottom: true + } + + mask: Region { + item: listview.contentItem + } + + color: "transparent" + implicitWidth: listview.implicitWidth + + WListView { + id: listview + anchors { + bottom: parent.bottom + right: parent.right + left: parent.left + } + leftMargin: 16 + rightMargin: 16 + topMargin: 16 + bottomMargin: 16 + + height: Math.min(contentItem.height + topMargin + bottomMargin, parent.height) + width: parent.width - Appearance.sizes.elevationMargin * 2 + + implicitWidth: 396 + spacing:12 + + model: ScriptModel { + values: Notifications.popupList + } + delegate: WSingleNotification { + required property var modelData + notification: modelData + width: ListView.view.width - ListView.view.leftMargin - ListView.view.rightMargin + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/polkit/WPolkitContent.qml b/dots/.config/quickshell/ii/modules/waffle/polkit/WPolkitContent.qml index 1218efb69..1fcb25072 100644 --- a/dots/.config/quickshell/ii/modules/waffle/polkit/WPolkitContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/polkit/WPolkitContent.qml @@ -135,7 +135,7 @@ Rectangle { } BodyRectangle { implicitHeight: 80 - color: Looks.colors.bgPanelFooterBase + color: Looks.colors.bgPanelFooterBackground RowLayout { anchors.fill: parent anchors.margins: 24 diff --git a/dots/.config/quickshell/ii/modules/waffle/screenSnip/WRectangularSelection.qml b/dots/.config/quickshell/ii/modules/waffle/screenSnip/WRectangularSelection.qml new file mode 100644 index 000000000..1de501613 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/screenSnip/WRectangularSelection.qml @@ -0,0 +1,62 @@ +import QtQuick +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.modules.waffle.looks + +Item { + id: root + + required property int regionX + required property int regionY + required property int regionWidth + required property int regionHeight + + property bool dashed: true + property color borderColor: "#ffffff" + property color overlayColor: ColorUtils.transparentize("#000000", 1) + Component.onCompleted: overlayColor = ColorUtils.transparentize("#000000", 0.4) + Behavior on overlayColor { + ColorAnimation { + duration: 150 + easing.type: Easing.InOutQuad + } + } + + // Overlay to darken screen + // Base dark overlay around region + Rectangle { + id: darkenOverlay + z: 1 + anchors { + left: parent.left + top: parent.top + leftMargin: root.regionX - darkenOverlay.border.width + topMargin: root.regionY - darkenOverlay.border.width + } + width: root.regionWidth + darkenOverlay.border.width * 2 + height: root.regionHeight + darkenOverlay.border.width * 2 + color: "transparent" + border.color: root.overlayColor + border.width: Math.max(root.width, root.height) + } + + // Selection border + DashedBorder { + id: border + z: 2 + visible: root.regionWidth > 0 && root.regionHeight > 0 + anchors { + left: parent.left + top: parent.top + leftMargin: Math.round(root.regionX - borderWidth) + topMargin: Math.round(root.regionY - borderWidth) + } + width: Math.round(root.regionWidth + borderWidth * 2) + height: Math.round(root.regionHeight + borderWidth * 2) + color: root.borderColor + dashLength: 4 + gapLength: root.dashed ? 3 : 0 + borderWidth: 1 + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/screenSnip/WRegionSelectionPanel.qml b/dots/.config/quickshell/ii/modules/waffle/screenSnip/WRegionSelectionPanel.qml new file mode 100644 index 000000000..3224df446 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/screenSnip/WRegionSelectionPanel.qml @@ -0,0 +1,374 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Qt.labs.synchronizer +import Quickshell +import Quickshell.Io +import Quickshell.Wayland +import Quickshell.Hyprland +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.utils +import qs.modules.common.widgets +import qs.modules.waffle.looks + +PanelWindow { + id: root + + enum MediaType { + Image, + Video + } + enum ImageAction { + Copy, + Menu, + CharRecognition, + Search + } + enum VideoAction { + Record, + RecordWithSound + } + enum SelectionMode { + Rect, + Window + } + + function close() { + root.closed(); + } + + property var mediaType: WRegionSelectionPanel.MediaType.Image + property var imageAction: WRegionSelectionPanel.ImageAction.Copy + property var selectionMode: WRegionSelectionPanel.SelectionMode.Rect + + visible: false + color: "transparent" + WlrLayershell.namespace: "quickshell:regionSelector" + WlrLayershell.layer: WlrLayer.Overlay + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + exclusionMode: ExclusionMode.Ignore + anchors { + left: true + right: true + top: true + bottom: true + } + + // Hyprland stuff + readonly property HyprlandMonitor hyprlandMonitor: Hyprland.monitorFor(screen) + readonly property real monitorScale: hyprlandMonitor.scale + readonly property var windows: [...HyprlandData.windowList].sort((a, b) => { + // Sort floating=true windows before others + if (a.floating === b.floating) + return 0; + return a.floating ? -1 : 1; + }) + + property string screenshotDir: Directories.screenshotTemp + property string screenshotPath: `${root.screenshotDir}/image-${screen.name}` + TempScreenshotProcess { + id: screenshotProc + running: true + screen: root.screen + screenshotDir: root.screenshotDir + screenshotPath: root.screenshotPath + onExited: (exitCode, exitStatus) => { + root.preparationDone = true; + } + } + property bool preparationDone: false + onPreparationDoneChanged: { + if (!preparationDone) + return; + root.visible = true; + } + + function getScreenshotAction() { + switch (root.mediaType) { + case WRegionSelectionPanel.MediaType.Image: + switch (root.imageAction) { + case WRegionSelectionPanel.ImageAction.Copy: + return ScreenshotAction.Action.Copy; + case WRegionSelectionPanel.ImageAction.Menu: + return ScreenshotAction.Action.Edit; + case WRegionSelectionPanel.ImageAction.CharRecognition: + return ScreenshotAction.Action.CharRecognition; + case WRegionSelectionPanel.ImageAction.Search: + return ScreenshotAction.Action.Search; + default: + return ScreenshotAction.Action.Copy; + } + break; + case WRegionSelectionPanel.MediaType.Video: + switch (root.videoAction) { + case WRegionSelectionPanel.VideoAction.Record: + return ScreenshotAction.Action.Record; + case WRegionSelectionPanel.VideoAction.RecordWithSound: + return ScreenshotAction.Action.RecordWithSound; + } + } + } + + Process { + id: snipProc + } + + ScreencopyView { + id: screencopyView + anchors.fill: parent + live: false + captureSource: root.screen + + focus: root.visible + Keys.onPressed: event => { // Esc to close + if (event.key === Qt.Key_Escape) { + root.close(); + } else if (event.key === Qt.Key_E && event.modifiers & Qt.ControlModifier) { + if (root.imageAction === WRegionSelectionPanel.ImageAction.Menu) { + root.imageAction = WRegionSelectionPanel.ImageAction.Copy; + } else { + root.imageAction = WRegionSelectionPanel.ImageAction.Menu; + } + } + } + + DragManager { + id: dragArea + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.LeftButton | Qt.RightButton + cursorShape: Qt.CrossCursor + + property bool isWindowSelection: root.selectionMode === WRegionSelectionPanel.SelectionMode.Window + property var hoveredWindow: root.windows.find(w => { + const inCurrentWorkspace = w.workspace.id === HyprlandData.activeWorkspace.id; + const withinXRange = w.at[0] <= dragArea.mouseX && dragArea.mouseX <= w.at[0] + w.size[0]; + const withinYRange = w.at[1] <= dragArea.mouseY && dragArea.mouseY <= w.at[1] + w.size[1]; + return inCurrentWorkspace && withinXRange && withinYRange; + }) + property int winPadding: 1 + property int selectionX: isWindowSelection ? ((hoveredWindow?.at[0] ?? 0) - winPadding) : regionTopLeftX + property int selectionY: isWindowSelection ? ((hoveredWindow?.at[1] ?? 0) - winPadding) : regionTopLeftY + property int selectionWidth: isWindowSelection ? ((hoveredWindow?.size[0] ?? 0) + winPadding * 2) : regionWidth + property int selectionHeight: isWindowSelection ? ((hoveredWindow?.size[1] ?? 0) + winPadding * 2) : regionHeight + + onDragReleased: (diffX, diffY) => { + if (selectionWidth === 0 || selectionHeight === 0) { + return; + } + const screenshotDir = Config.options.screenSnip.savePath !== "" ? Config.options.screenSnip.savePath : ""; + const screenshotAction = root.getScreenshotAction(); + const command = ScreenshotAction.getCommand(dragArea.selectionX * root.monitorScale // + , dragArea.selectionY * root.monitorScale // + , dragArea.selectionWidth * root.monitorScale// + , dragArea.selectionHeight * root.monitorScale // + , root.screenshotPath // + , screenshotAction // + , screenshotDir); // yo wtf is this formatting qmlls do be funnie + snipProc.command = command; + + // Image post-processing + snipProc.startDetached(); + root.close(); + } + + WRectangularSelection { + id: rectangularSelection + anchors.fill: parent + regionX: dragArea.selectionX + regionY: dragArea.selectionY + regionWidth: dragArea.selectionWidth + regionHeight: dragArea.selectionHeight + dashed: root.selectionMode === WRegionSelectionPanel.SelectionMode.Rect + } + + RegionSelectionOptionsToolbar { + anchors { + horizontalCenter: parent.horizontalCenter + top: parent.top + topMargin: 12 + } + } + } + } + + component RegionSelectionOptionsToolbar: WToolbar { + // Image/video + WToolbarTabBar { + currentIndex: switch (root.mediaType) { + case WRegionSelectionPanel.MediaType.Image: + return 0; + case WRegionSelectionPanel.MediaType.Video: + return 1; + default: + return 0; + } + WToolbarIconTabButton { + icon.name: "camera" + icon.color: Looks.colors.fg + } + WToolbarIconTabButton { + icon.name: "video" + icon.color: Looks.colors.fg + } + onCurrentIndexChanged: { + switch (currentIndex) { + case 0: + root.mediaType = WRegionSelectionPanel.MediaType.Image; + break; + case 1: + root.mediaType = WRegionSelectionPanel.MediaType.Video; + break; + } + } + + WToolTip { + text: Translation.tr("Snip") + } + } + + // Selection type + WToolbarButton { + id: selectionTypeBtn + implicitWidth: selectionTypeBtnRow.implicitWidth + 11 * 2 + leftPadding: 11 + rightPadding: 11 + onClicked: { + selectionTypeMenu.visible = !selectionTypeMenu.visible; + } + contentItem: Row { + id: selectionTypeBtnRow + spacing: 4 + FluentIcon { + anchors.verticalCenter: parent.verticalCenter + icon: switch (root.selectionMode) { + case WRegionSelectionPanel.SelectionMode.Rect: + return "crop"; + case WRegionSelectionPanel.SelectionMode.Window: + return "calendar-add"; + default: + return "crop"; + } + implicitSize: 18 + } + FluentIcon { + anchors { + top: parent.top + topMargin: (parent.height - height) / 2 + (selectionTypeBtn.down ? 2 : 0) + Behavior on topMargin { + animation: Looks.transition.enter.createObject(this) + } + } + icon: "chevron-down" + implicitSize: 12 + } + } + + WMenu { + id: selectionTypeMenu + onClosed: screencopyView.focus = true + x: -margins + y: -margins - (selectionTypeBtn.parent.height - selectionTypeBtn.height) - 16 + topMargin: -6 + height: implicitHeight + sourceEdgeMargin + + color: Looks.colors.bg1Base + + Action { + icon.name: "crop" + text: Translation.tr("Rectangle") + checked: root.selectionMode === WRegionSelectionPanel.SelectionMode.Rect + onTriggered: { + root.selectionMode = WRegionSelectionPanel.SelectionMode.Rect; + } + } + Action { + icon.name: "calendar-add" + text: Translation.tr("Window") + checked: root.selectionMode === WRegionSelectionPanel.SelectionMode.Window + onTriggered: { + root.selectionMode = WRegionSelectionPanel.SelectionMode.Window; + } + } + } + + WToolTip { + text: Translation.tr("Snipping area") + } + } + + // Markup + WToolbarIconButton { + icon.name: "image-edit" + enabled: root.mediaType === WRegionSelectionPanel.MediaType.Image + checked: root.imageAction === WRegionSelectionPanel.ImageAction.Menu + onClicked: { + if (root.imageAction === WRegionSelectionPanel.ImageAction.Menu) { + root.imageAction = WRegionSelectionPanel.ImageAction.Copy; + } else { + root.imageAction = WRegionSelectionPanel.ImageAction.Menu; + } + } + WToolTip { + text: Translation.tr("Quick markup (Ctrl+E)") + } + } + + WToolbarSeparator {} + + // Tools + WToolbarIconButton { + icon.name: "search-visual" + checked: root.imageAction === WRegionSelectionPanel.ImageAction.Search + onClicked: { + if (root.imageAction === WRegionSelectionPanel.ImageAction.Search && root.mediaType === WRegionSelectionPanel.MediaType.Image) { + root.imageAction = WRegionSelectionPanel.ImageAction.Copy; + } else { + root.mediaType = WRegionSelectionPanel.MediaType.Image; + root.imageAction = WRegionSelectionPanel.ImageAction.Search; + } + } + WToolTip { + text: Translation.tr("Image search") + } + } + WToolbarIconButton { + icon.name: "eyedropper" + onClicked: { + Quickshell.execDetached(["bash", "-c", "sleep 0.2; hyprpicker -a"]); + root.closed(); + } + WToolTip { + text: Translation.tr("Color picker") + } + } + WToolbarIconButton { + icon.name: "scan-text" + checked: root.imageAction === WRegionSelectionPanel.ImageAction.CharRecognition + onClicked: { + if (root.imageAction === WRegionSelectionPanel.ImageAction.CharRecognition && root.mediaType === WRegionSelectionPanel.MediaType.Image) { + root.imageAction = WRegionSelectionPanel.ImageAction.Copy; + } else { + root.mediaType = WRegionSelectionPanel.MediaType.Image; + root.imageAction = WRegionSelectionPanel.ImageAction.CharRecognition; + } + } + WToolTip { + text: Translation.tr("Text extractor") + } + } + + WToolbarSeparator {} + + WToolbarIconButton { + icon.name: "dismiss" + onClicked: root.close() + WToolTip { + text: Translation.tr("Close (Esc)") + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/screenSnip/WScreenSnip.qml b/dots/.config/quickshell/ii/modules/waffle/screenSnip/WScreenSnip.qml new file mode 100644 index 000000000..c462f9e7b --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/screenSnip/WScreenSnip.qml @@ -0,0 +1,106 @@ +pragma ComponentBehavior: Bound +import qs +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.services +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Qt5Compat.GraphicalEffects +import Quickshell +import Quickshell.Io +import Quickshell.Wayland +import Quickshell.Widgets +import Quickshell.Hyprland + +Scope { + id: root + + function dismiss() { + GlobalStates.regionSelectorOpen = false; + } + + Loader { + id: regionSelectorLoader + active: GlobalStates.regionSelectorOpen + + sourceComponent: WRegionSelectionPanel { + onClosed: root.dismiss() + } + } + + function screenshot() { + GlobalStates.regionSelectorOpen = true; + } + + function ocr() { + GlobalStates.regionSelectorOpen = true; + regionSelectorLoader.item.mediaType = WRegionSelectionPanel.MediaType.Image; + regionSelectorLoader.item.imageAction = WRegionSelectionPanel.ImageAction.CharRecognition; + } + + function record() { + GlobalStates.regionSelectorOpen = true; + regionSelectorLoader.item.mediaType = WRegionSelectionPanel.MediaType.Video; + regionSelectorLoader.item.videoAction = WRegionSelectionPanel.VideoAction.Record; + } + + function recordWithSound() { + GlobalStates.regionSelectorOpen = true; + regionSelectorLoader.item.mediaType = WRegionSelectionPanel.MediaType.Video; + regionSelectorLoader.item.videoAction = WRegionSelectionPanel.VideoAction.RecordWithSound; + } + + function search() { + GlobalStates.regionSelectorOpen = true; + regionSelectorLoader.item.mediaType = WRegionSelectionPanel.MediaType.Image; + regionSelectorLoader.item.imageAction = WRegionSelectionPanel.ImageAction.Search; + } + + IpcHandler { + target: "region" + + function screenshot() { + root.screenshot(); + } + function ocr() { + root.ocr(); + } + function record() { + root.record(); + } + function recordWithSound() { + root.recordWithSound(); + } + function search() { + root.search(); + } + } + + GlobalShortcut { + name: "regionScreenshot" + description: "Takes a screenshot of the selected region" + onPressed: root.screenshot() + } + GlobalShortcut { + name: "regionSearch" + description: "Searches the selected region" + onPressed: root.search() + } + GlobalShortcut { + name: "regionOcr" + description: "Recognizes text in the selected region" + onPressed: root.ocr() + } + GlobalShortcut { + name: "regionRecord" + description: "Records the selected region" + onPressed: root.record() + } + GlobalShortcut { + name: "regionRecordWithSound" + description: "Records the selected region with sound" + onPressed: root.recordWithSound() + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml index fb506dd49..64ad321c6 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml @@ -86,7 +86,7 @@ WBarAttachedPanelContent { id: searchBar Layout.fillWidth: true implicitWidth: 832 // TODO: Make sizes naturally inferred - horizontalPadding: root.searching ? 24 : 32 + horizontalPadding: 32 // verticalPadding: root.searching ? 32 : 16 // TODO: make this not nuke the panel Synchronizer on searching { property alias target: root.searching diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/SearchResultButton.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/SearchResultButton.qml index b7fb1df14..db45480f4 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/SearchResultButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/SearchResultButton.qml @@ -44,7 +44,7 @@ WChoiceButton { Layout.fillHeight: true horizontalPadding: 10 verticalPadding: 11 - implicitHeight: root.firstEntry ? 62 : 36 + implicitHeight: Math.max(root.firstEntry ? 62 : 36, entryContentRow.implicitHeight + 8 * 2) implicitWidth: entryContentRow.implicitWidth + leftPadding + rightPadding topRightRadius: 0 bottomRightRadius: 0 @@ -54,6 +54,7 @@ WChoiceButton { id: entryContentRow anchors { left: parent.left + right: parent.right verticalCenter: parent.verticalCenter } spacing: 8 @@ -102,6 +103,7 @@ WChoiceButton { text: root.entry.name font.pixelSize: Looks.font.pixelSize.large maximumLineCount: 2 + elide: Text.ElideRight } WText { @@ -109,6 +111,7 @@ WChoiceButton { visible: root.firstEntry text: root.entry.type color: Looks.colors.accentUnfocused + elide: Text.ElideRight } } diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/SearchResults.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/SearchResults.qml index aeaa4748c..fc4adc684 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/SearchResults.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/SearchResults.qml @@ -15,6 +15,7 @@ RowLayout { id: root property int maxResultsPerCategory: 4 + property int resultLimit: 20 property StartMenuContext context property int currentIndex: context.currentIndex onCurrentIndexChanged: { @@ -99,21 +100,33 @@ RowLayout { // Collect max 4 per category var categorizedResults = []; - categories.forEach(category => { + let categoriesArray = Array.from(categories); + let totalCount = 0; + for (let c = 0; c < categoriesArray.length; c++) { + let category = categoriesArray[c]; let count = 0; for (let i = 0; i < allResults.length; i++) { if (allResults[i].type === category) { + if (totalCount >= root.resultLimit) { + break; + } const entry = allResults[i]; const tweakedEntry = searchResultComp.createObject(null, Object.assign({}, entry)); - tweakedEntry.category = categorizedResults.length === 0 ? Translation.tr("Best match") : entry.type + tweakedEntry.category = categorizedResults.length === 0 ? Translation.tr("Best match") : entry.type; + categorizedResults.push(tweakedEntry); // Section header count++; + totalCount++; if (count >= root.maxResultsPerCategory) { break; } } } - }); + if (totalCount >= root.resultLimit) { + break; + } + } + // print(JSON.stringify(categorizedResults, null, 2)); return categorizedResults; } diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/AllAppsGrid.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/AllAppsGrid.qml index aa1321eb8..4a704909d 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/AllAppsGrid.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/AllAppsGrid.qml @@ -28,13 +28,13 @@ GridLayout { }), aggAppCatComp.createObject(null, { name: Translation.tr("Creativity"), categories: ["AudioVideo", "Graphics"] - }), aggAppCatComp.createObject(null, { - name: Translation.tr("Other"), - categories: ["Game"] }), aggAppCatComp.createObject(null, { name: Translation.tr("System"), categories: ["Settings", "System"] - }) + }), aggAppCatComp.createObject(null, { + name: Translation.tr("Other"), + categories: ["Game"] + }), ] Repeater { diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml index cbb61b2fe..6828098cf 100644 --- a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml @@ -16,29 +16,43 @@ Rectangle { id: root color: ColorUtils.transparentize(Looks.colors.bg1Base, 0.5) + property bool draggingWindow: false property real openProgress: 0 + property Item hoveredWorkspace: null + signal closed Component.onCompleted: { openAnim.start(); } + function close() { + closeAnim.start(); + } PropertyAnimation { id: openAnim target: root property: "openProgress" to: 1 - duration: 200 + duration: 250 easing.type: Easing.BezierSpline easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn } - PropertyAnimation { + SequentialAnimation { id: closeAnim - target: root - property: "openProgress" - to: 0 - duration: 200 - easing.type: Easing.BezierSpline - easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn + + PropertyAnimation { + target: root + property: "openProgress" + to: 0 + duration: 250 + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn + } + ScriptAction { + script: { + root.closed(); + } + } } // Windows @@ -81,8 +95,18 @@ Rectangle { return resultLayout; } + MouseArea { + z: 0 + anchors.fill: parent + onClicked: { + GlobalStates.overviewOpen = false; + } + } + // Windows WListView { + id: windowListView + z: root.openProgress == 1 ? 2 : 1 anchors { left: parent.left right: parent.right @@ -96,9 +120,8 @@ Rectangle { rightMargin: root.padding height: Math.min(contentHeight + topMargin + bottomMargin, root.height - (wsBorder.height + 16)) - interactive: height < contentHeight - - clip: true + interactive: (height < contentHeight) && !root.draggingWindow + clip: root.openProgress > 0.99 && !root.draggingWindow model: ScriptModel { values: root.arrangedToplevels @@ -107,7 +130,7 @@ Rectangle { id: clientRow required property var modelData spacing: root.spacing - anchors.horizontalCenter: parent.horizontalCenter + anchors.horizontalCenter: parent?.horizontalCenter ?? undefined Repeater { model: ScriptModel { @@ -117,24 +140,61 @@ Rectangle { id: clientGridArea required property int index required property var modelData - implicitWidth: windowItem.implicitWidth - implicitHeight: windowItem.implicitHeight + implicitWidth: windowItem.openedSize.width + implicitHeight: windowItem.openedSize.height + windowItem.titleBarImplicitHeight TaskViewWindow { id: windowItem - z: 9999 - drag { - target: this + z: Drag.active ? 2 : 1 + opacity: openAnim.running ? root.openProgress : 1 + + property int mappedX: { + // print("AAAWAWAAWAWWA: ", -(clientRow.x + clientGridArea.x + root.padding)); + var rootPosToThis = -(clientRow.x + clientGridArea.x + root.padding); + return rootPosToThis + hyprlandClient.at[0]; + } + property int mappedY: { + // print("AAAWAWAAWAWWA YYYY YUIUSDFOIU: ", clientRow.y + windowListView.y + root.padding + windowItem.titleBarImplicitHeight) + var rootPosToThis = -(clientRow.y + windowListView.y + root.padding + windowItem.titleBarImplicitHeight); + return rootPosToThis + hyprlandClient.at[1]; + } + property int openedX: 0 + property int openedY: 0 + // property int openedX: Drag.active ? (dragHandler.xAxis.activeValue) : 0 + // property int openedY: Drag.active ? (dragHandler.yAxis.activeValue) : 0 + scaleSize: (root.openProgress > 0 && !closeAnim.running) + x: mappedX + (openedX - mappedX) * root.openProgress + y: mappedY + (openedY - mappedY) * root.openProgress + + droppable: root.hoveredWorkspace !== null + Drag.active: dragHandler.active + Drag.hotSpot.x: mouseX + Drag.hotSpot.y: mouseY + + DragHandler { + id: dragHandler + target: null + xAxis.onActiveValueChanged: { + windowItem.openedX = dragHandler.xAxis.activeValue; + } + yAxis.onActiveValueChanged: { + windowItem.openedY = dragHandler.yAxis.activeValue; + } onActiveChanged: { - if (drag.active) { - parent = root; + if (active) { + root.draggingWindow = true; } else { - parent = clientGridArea; - x = 0; - y = 0; + root.draggingWindow = false; + if (root.hoveredWorkspace !== null && root.hoveredWorkspace.workspace !== windowItem.hyprlandClient.workspace.id) { + Hyprland.dispatch(`movetoworkspacesilent ${root.hoveredWorkspace.workspace}, address:${windowItem.hyprlandClient.address}`); + } else { + windowItem.openedX = 0; + windowItem.openedY = 0; + } } } } + Layout.alignment: Qt.AlignTop maxHeight: root.maxWindowHeight maxWidth: root.maxWindowWidth @@ -148,6 +208,7 @@ Rectangle { // Workspaces Rectangle { id: wsBorder + z: root.openProgress == 1 ? 1 : 2 property real sourceEdgeMargin: -(height + 8) + root.openProgress * (height + 16) anchors { left: parent.left @@ -170,7 +231,7 @@ Rectangle { anchors.fill: parent anchors.margins: wsBorder.border.width radius: wsBorder.radius - wsBorder.border.width - color: Looks.colors.bgPanelFooterBase + color: Looks.colors.bgPanelFooterBackground implicitHeight: 174 @@ -210,9 +271,29 @@ Rectangle { } } delegate: TaskViewWorkspace { + id: workspaceItem required property int index workspace: index + 1 newWorkspace: index == workspaceIndexModel.count - 1 + + droppable: root.hoveredWorkspace === workspaceItem + DropArea { + anchors.fill: parent + onEntered: drag => { + root.hoveredWorkspace = workspaceItem; + } + onExited: { + if (root.hoveredWorkspace === workspaceItem) { + root.hoveredWorkspace = null; + } + } + } + + onClicked: { + GlobalStates.overviewOpen = false; + root.closed(); // Close immediately to avoid weird animations + Hyprland.dispatch(`workspace ${workspaceItem.workspace}`); + } } } } diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWindow.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWindow.qml index d8bf4b45b..b91655097 100644 --- a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWindow.qml +++ b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWindow.qml @@ -25,47 +25,60 @@ WMouseAreaButton { property string iconName: AppSearch.guessIcon(hyprlandClient?.class) - color: containsMouse ? Looks.colors.bg1Base : Looks.colors.bgPanelFooterBase - borderColor: Looks.colors.bg2Border + color: drag.active ? ColorUtils.transparentize(Looks.colors.bg1Base) : (containsMouse ? Looks.colors.bg1Base : Looks.colors.bgPanelFooterBackground) + borderColor: ColorUtils.transparentize(Looks.colors.bg2Border, drag.active ? 1 : 0) radius: Looks.radius.xLarge - property size size: WindowLayout.scaleWindow(hyprlandClient, maxWidth, maxHeight) + property real titleBarImplicitHeight: titleBar.implicitHeight + property bool scaleSize: true + property size openedSize: WindowLayout.scaleWindow(hyprlandClient, maxWidth, maxHeight); + property size fullSize: Qt.size(hyprlandClient?.size[0] ?? maxWidth, hyprlandClient?.size[1] ?? maxHeight) + property size size: scaleSize ? openedSize : fullSize implicitWidth: Math.max(Math.round(contentItem.implicitWidth), 138) implicitHeight: Math.round(contentItem.implicitHeight) layer.enabled: true layer.effect: OpacityMask { - maskSource: Rectangle { + maskSource: Item { width: root.background.width height: root.background.height - radius: root.background.radius + Rectangle { + radius: root.background.radius + anchors { + fill: parent + topMargin: root.drag.active ? root.titleBarImplicitHeight : 0 + } + } } } - scale: (root.pressedButtons & Qt.LeftButton) ? 0.95 : 1 + property bool droppable: false + scale: (root.pressedButtons & Qt.LeftButton || root.Drag.active) ? (droppable ? 0.4 : 0.95) : 1 Behavior on scale { NumberAnimation { id: scaleAnim - duration: 300 + duration: 200 easing.type: Easing.OutExpo } } function closeWindow() { - Hyprland.dispatch(`closewindow address:${root.hyprlandClient?.address}`) + Hyprland.dispatch(`closewindow address:${root.hyprlandClient?.address}`); } acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton - onClicked: (event) => { + onClicked: event => { if (event.button === Qt.LeftButton) { - GlobalStates.overviewOpen = false - Hyprland.dispatch(`focuswindow address:${root.hyprlandClient?.address}`) + GlobalStates.overviewOpen = false; + Hyprland.dispatch(`focuswindow address:${root.hyprlandClient?.address}`); GlobalStates.overviewOpen = false; } else if (event.button === Qt.MiddleButton) { root.closeWindow(); event.accepted = true; } else if (event.button === Qt.RightButton) { - if (!windowMenu.visible) windowMenu.popup(); - else windowMenu.close(); + if (!windowMenu.visible) + windowMenu.popup(); + else + windowMenu.close(); } } @@ -77,6 +90,8 @@ WMouseAreaButton { spacing: 0 RowLayout { + id: titleBar + opacity: root.drag.active ? 0 : 1 spacing: 8 WAppIcon { Layout.leftMargin: 10 @@ -104,6 +119,14 @@ WMouseAreaButton { Layout.alignment: Qt.AlignHCenter implicitWidth: Math.round(root.size.width) implicitHeight: Math.round(root.size.height) + constraintSize: Qt.size(Math.round(root.size.width), Math.round(root.size.height)) + + Behavior on implicitWidth { + animation: Looks.transition.enter.createObject(this) + } + Behavior on implicitHeight { + animation: Looks.transition.enter.createObject(this) + } captureSource: root.toplevel ?? null live: true @@ -120,7 +143,7 @@ WMouseAreaButton { icon.name: isPinned ? "checkmark" : "empty" text: Translation.tr("Show this window on all desktops") onTriggered: { - Hyprland.dispatch(`pin address:${root.hyprlandClient?.address}`) + Hyprland.dispatch(`pin address:${root.hyprlandClient?.address}`); } } Action { diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml index 56451a4e7..8fe3f35bd 100644 --- a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml +++ b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml @@ -16,6 +16,7 @@ WMouseAreaButton { required property int workspace property bool newWorkspace: false + property bool droppable: false readonly property bool isActiveWorkspace: HyprlandData.activeWorkspace?.id === root.workspace readonly property real screenWidth: QsWindow.window?.width ?? 0 @@ -28,12 +29,7 @@ WMouseAreaButton { height: ListView.view?.height ?? 100 implicitWidth: 244 // for now - onClicked: { - GlobalStates.overviewOpen = false; - Hyprland.dispatch(`workspace ${root.workspace}`); - } - - colBackground: ColorUtils.transparentize(Looks.colors.bg2, isActiveWorkspace ? 0 : 1) + colBackground: ColorUtils.transparentize(Looks.colors.bg2, (isActiveWorkspace || droppable) ? 0 : 1) Behavior on color { animation: Looks.transition.color.createObject(this) } @@ -119,6 +115,14 @@ WMouseAreaButton { icon: "add" } } + + Rectangle { + z: 2 + visible: root.droppable && !root.newWorkspace + anchors.fill: parent + color: Looks.colors.accent + opacity: 0.2 + } } } diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/WaffleTaskView.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/WaffleTaskView.qml index 75e71ccb1..05a2ccf40 100644 --- a/dots/.config/quickshell/ii/modules/waffle/taskView/WaffleTaskView.qml +++ b/dots/.config/quickshell/ii/modules/waffle/taskView/WaffleTaskView.qml @@ -23,7 +23,14 @@ Scope { Loader { id: panelLoader required property var modelData - active: GlobalStates.overviewOpen + active: false + Connections { + target: GlobalStates + function onOverviewOpenChanged() { + if (GlobalStates.overviewOpen) + panelLoader.active = true; + } + } sourceComponent: PanelWindow { id: root property string searchingText: "" @@ -33,7 +40,7 @@ Scope { WlrLayershell.namespace: "quickshell:wTaskView" WlrLayershell.layer: WlrLayer.Overlay - // WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand color: "transparent" anchors { @@ -44,7 +51,26 @@ Scope { } TaskViewContent { + id: taskViewContent anchors.fill: parent + + Component.onCompleted: { + taskViewContent.forceActiveFocus(); + } + Keys.onPressed: event => { + if (event.key === Qt.Key_Escape) { + GlobalStates.overviewOpen = false; + } + } + + Connections { + target: GlobalStates + function onOverviewOpenChanged() { + if (!GlobalStates.overviewOpen) + taskViewContent.close(); + } + } + onClosed: panelLoader.active = false } } } diff --git a/dots/.config/quickshell/ii/panelFamilies/IllogicalImpulseFamily.qml b/dots/.config/quickshell/ii/panelFamilies/IllogicalImpulseFamily.qml new file mode 100644 index 000000000..f4ffda651 --- /dev/null +++ b/dots/.config/quickshell/ii/panelFamilies/IllogicalImpulseFamily.qml @@ -0,0 +1,45 @@ +import QtQuick +import Quickshell + +import qs.modules.common +import qs.modules.ii.background +import qs.modules.ii.bar +import qs.modules.ii.cheatsheet +import qs.modules.ii.dock +import qs.modules.ii.lock +import qs.modules.ii.mediaControls +import qs.modules.ii.notificationPopup +import qs.modules.ii.onScreenDisplay +import qs.modules.ii.onScreenKeyboard +import qs.modules.ii.overview +import qs.modules.ii.polkit +import qs.modules.ii.regionSelector +import qs.modules.ii.screenCorners +import qs.modules.ii.sessionScreen +import qs.modules.ii.sidebarLeft +import qs.modules.ii.sidebarRight +import qs.modules.ii.overlay +import qs.modules.ii.verticalBar +import qs.modules.ii.wallpaperSelector + +Scope { + PanelLoader { extraCondition: !Config.options.bar.vertical; component: Bar {} } + PanelLoader { component: Background {} } + PanelLoader { component: Cheatsheet {} } + PanelLoader { extraCondition: Config.options.dock.enable; component: Dock {} } + PanelLoader { component: Lock {} } + PanelLoader { component: MediaControls {} } + PanelLoader { component: NotificationPopup {} } + PanelLoader { component: OnScreenDisplay {} } + PanelLoader { component: OnScreenKeyboard {} } + PanelLoader { component: Overlay {} } + PanelLoader { component: Overview {} } + PanelLoader { component: Polkit {} } + PanelLoader { component: RegionSelector {} } + PanelLoader { component: ScreenCorners {} } + PanelLoader { component: SessionScreen {} } + PanelLoader { component: SidebarLeft {} } + PanelLoader { component: SidebarRight {} } + PanelLoader { extraCondition: Config.options.bar.vertical; component: VerticalBar {} } + PanelLoader { component: WallpaperSelector {} } +} diff --git a/dots/.config/quickshell/ii/panelFamilies/PanelLoader.qml b/dots/.config/quickshell/ii/panelFamilies/PanelLoader.qml new file mode 100644 index 000000000..1e7b52f42 --- /dev/null +++ b/dots/.config/quickshell/ii/panelFamilies/PanelLoader.qml @@ -0,0 +1,9 @@ +import QtQuick +import Quickshell + +import qs.modules.common + +LazyLoader { + property bool extraCondition: true + active: Config.ready && extraCondition +} diff --git a/dots/.config/quickshell/ii/panelFamilies/WaffleFamily.qml b/dots/.config/quickshell/ii/panelFamilies/WaffleFamily.qml new file mode 100644 index 000000000..67d35de55 --- /dev/null +++ b/dots/.config/quickshell/ii/panelFamilies/WaffleFamily.qml @@ -0,0 +1,44 @@ +import QtQuick +import Quickshell + +import qs.modules.common +import qs.modules.waffle.actionCenter +import qs.modules.waffle.background +import qs.modules.waffle.bar +import qs.modules.waffle.lock +import qs.modules.waffle.notificationCenter +import qs.modules.waffle.notificationPopup +import qs.modules.waffle.onScreenDisplay +// import qs.modules.waffle.overlay +import qs.modules.waffle.polkit +import qs.modules.waffle.screenSnip +import qs.modules.waffle.startMenu +import qs.modules.waffle.sessionScreen +import qs.modules.waffle.taskView + +// Fallbacks +import qs.modules.ii.cheatsheet +import qs.modules.ii.onScreenKeyboard +import qs.modules.ii.overlay +import qs.modules.ii.wallpaperSelector + +Scope { + PanelLoader { component: WaffleActionCenter {} } + PanelLoader { component: WaffleBar {} } + PanelLoader { component: WaffleBackground {} } + PanelLoader { component: WaffleLock {} } + PanelLoader { component: WaffleNotificationCenter {} } + PanelLoader { component: WaffleNotificationPopup {} } + PanelLoader { component: WaffleOSD {} } + // PanelLoader { component: WaffleOverlay {} } + PanelLoader { component: WafflePolkit {} } + PanelLoader { component: WScreenSnip {} } + PanelLoader { component: WaffleStartMenu {} } + PanelLoader { component: WaffleSessionScreen {} } + PanelLoader { component: WaffleTaskView {} } + + PanelLoader { component: Cheatsheet {} } + PanelLoader { component: OnScreenKeyboard {} } + PanelLoader { component: Overlay {} } + PanelLoader { component: WallpaperSelector {} } +} diff --git a/dots/.config/quickshell/ii/scripts/ai/gemini-categorize-wallpaper.sh b/dots/.config/quickshell/ii/scripts/ai/gemini-categorize-wallpaper.sh index 41963d161..ead46561d 100755 --- a/dots/.config/quickshell/ii/scripts/ai/gemini-categorize-wallpaper.sh +++ b/dots/.config/quickshell/ii/scripts/ai/gemini-categorize-wallpaper.sh @@ -8,7 +8,7 @@ fi # Variables SOURCE_IMG_PATH="$1" -MODEL="${2:-${GEMINI_WALLPAPER_MODEL:-gemini-2.0-flash}}" # We use the flash variant so it's fast +MODEL="${2:-${GEMINI_WALLPAPER_MODEL:-gemini-2.5-flash}}" # We use the flash variant so it's fast WALLPAPER_NAME="$(basename "$SOURCE_IMG_PATH")" PROMPT="${3:-${GEMINI_WALLPAPER_PROMPT:-Categorize the wallpaper. Its file name is $WALLPAPER_NAME}}" RESIZED_IMG_PATH="/tmp/quickshell/ai/wallpaper.jpg" diff --git a/dots/.config/quickshell/ii/scripts/colors/code/material-code-set-color.sh b/dots/.config/quickshell/ii/scripts/colors/code/material-code-set-color.sh index b29fa7775..2f68d2658 100755 --- a/dots/.config/quickshell/ii/scripts/colors/code/material-code-set-color.sh +++ b/dots/.config/quickshell/ii/scripts/colors/code/material-code-set-color.sh @@ -8,6 +8,8 @@ settings_paths=( "${XDG_CONFIG_HOME:-$HOME/.config}/Code - OSS/User/settings.json" "${XDG_CONFIG_HOME:-$HOME/.config}/Code - Insiders/User/settings.json" "${XDG_CONFIG_HOME:-$HOME/.config}/Cursor/User/settings.json" + "${XDG_CONFIG_HOME:-$HOME/.config}/Antigravity/User/settings.json" + # Add more paths as needed for other forks ) diff --git a/dots/.config/quickshell/ii/scripts/colors/switchwall.sh b/dots/.config/quickshell/ii/scripts/colors/switchwall.sh index 430114d5b..6352e3f6d 100755 --- a/dots/.config/quickshell/ii/scripts/colors/switchwall.sh +++ b/dots/.config/quickshell/ii/scripts/colors/switchwall.sh @@ -155,6 +155,12 @@ set_thumbnail_path() { fi } +categorize_wallpaper() { + img_cat=$("$SCRIPT_DIR/../ai/gemini-categorize-wallpaper.sh" "$1") + # notify-send "Wallpaper category" "$img_cat" + echo "$img_cat" > "$STATE_DIR/user/generated/wallpaper/category.txt" +} + switch() { imgpath="$1" mode_flag="$2" @@ -163,9 +169,9 @@ switch() { color="$5" # Start Gemini auto-categorization if enabled - aiStylingEnabled=$(jq -r '.background.clock.cookie.aiStyling' "$SHELL_CONFIG_FILE") + aiStylingEnabled=$(jq -r '.background.widgets.clock.cookie.aiStyling' "$SHELL_CONFIG_FILE") if [[ "$aiStylingEnabled" == "true" ]]; then - "$SCRIPT_DIR/../ai/gemini-categorize-wallpaper.sh" "$imgpath" > "$STATE_DIR/user/generated/wallpaper/category.txt" & + categorize_wallpaper "$imgpath" & fi read scale screenx screeny screensizey < <(hyprctl monitors -j | jq '.[] | select(.focused) | .scale, .x, .y, .height' | xargs) @@ -406,6 +412,12 @@ main() { imgpath="$(kdialog --getopenfilename . --title 'Choose wallpaper')" fi + if [[ -n "$imgpath" && -z "$noswitch_flag" ]]; then + set_accent_color "" + color_flag="" + color="" + fi + # If type_flag is 'auto', detect scheme type from image (after imgpath is set) if [[ "$type_flag" == "auto" ]]; then if [[ -n "$imgpath" && -f "$imgpath" ]]; then diff --git a/dots/.config/quickshell/ii/scripts/musicRecognition/recognize-music.sh b/dots/.config/quickshell/ii/scripts/musicRecognition/recognize-music.sh index 183432b97..09d77f902 100755 --- a/dots/.config/quickshell/ii/scripts/musicRecognition/recognize-music.sh +++ b/dots/.config/quickshell/ii/scripts/musicRecognition/recognize-music.sh @@ -53,7 +53,7 @@ while true; do fi ffmpeg -f s16le -ar 44100 -ac 2 -i "$TMP_RAW" -acodec libmp3lame -y -hide_banner -loglevel error "$TMP_MP3" 2>/dev/null - RESULT=$(songrec audio-file-to-recognized-song "$TMP_MP3" 2>/dev/null || true) + RESULT=$(songrec recognize --json "$TMP_MP3" 2>/dev/null || true) if echo "$RESULT" | grep -q '"matches": \[' && [ ${#RESULT} -gt $MIN_VALID_RESULT_LENGTH ]; then echo "$RESULT" diff --git a/dots/.config/quickshell/ii/scripts/thumbnails/thumbgen-venv.sh b/dots/.config/quickshell/ii/scripts/thumbnails/thumbgen-venv.sh index 5b24f16f9..9b0d92385 100755 --- a/dots/.config/quickshell/ii/scripts/thumbnails/thumbgen-venv.sh +++ b/dots/.config/quickshell/ii/scripts/thumbnails/thumbgen-venv.sh @@ -3,5 +3,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source $(eval echo $ILLOGICAL_IMPULSE_VIRTUAL_ENV)/bin/activate GIO_USE_VFS=local "$SCRIPT_DIR/thumbgen.py" "$@" +THUMBGEN_EXIT_CODE=$? deactivate +exit $THUMBGEN_EXIT_CODE diff --git a/dots/.config/quickshell/ii/services/Ai.qml b/dots/.config/quickshell/ii/services/Ai.qml index ccd237de8..a273415f5 100644 --- a/dots/.config/quickshell/ii/services/Ai.qml +++ b/dots/.config/quickshell/ii/services/Ai.qml @@ -255,19 +255,6 @@ Singleton { // - api_format: The API format of the model. Can be "openai" or "gemini". Default is "openai". // - extraParams: Extra parameters to be passed to the model. This is a JSON object. property var models: Config.options.policies.ai === 2 ? {} : { - "gemini-2.0-flash": aiModelComponent.createObject(this, { - "name": "Gemini 2.0 Flash", - "icon": "google-gemini-symbolic", - "description": Translation.tr("Online | Google's model\nFast, can perform searches for up-to-date information"), - "homepage": "https://aistudio.google.com", - "endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:streamGenerateContent", - "model": "gemini-2.0-flash", - "requires_key": true, - "key_id": "gemini", - "key_get_link": "https://aistudio.google.com/app/apikey", - "key_get_description": Translation.tr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"), - "api_format": "gemini", - }), "gemini-2.5-flash": aiModelComponent.createObject(this, { "name": "Gemini 2.5 Flash", "icon": "google-gemini-symbolic", @@ -281,26 +268,13 @@ Singleton { "key_get_description": Translation.tr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"), "api_format": "gemini", }), - "gemini-2.5-flash-pro": aiModelComponent.createObject(this, { - "name": "Gemini 2.5 Pro", + "gemini-3-flash": aiModelComponent.createObject(this, { + "name": "Gemini 3 Flash", "icon": "google-gemini-symbolic", - "description": Translation.tr("Online | Google's model\nGoogle's state-of-the-art multipurpose model that excels at coding and complex reasoning tasks."), + "description": Translation.tr("Online | Google's model\nPro-level intelligence at the speed and pricing of Flash."), "homepage": "https://aistudio.google.com", - "endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-pro:streamGenerateContent", - "model": "gemini-2.5-pro", - "requires_key": true, - "key_id": "gemini", - "key_get_link": "https://aistudio.google.com/app/apikey", - "key_get_description": Translation.tr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"), - "api_format": "gemini", - }), - "gemini-2.5-flash-lite": aiModelComponent.createObject(this, { - "name": "Gemini 2.5 Flash-Lite", - "icon": "google-gemini-symbolic", - "description": Translation.tr("Online | Google's model\nA Gemini 2.5 Flash model optimized for cost-efficiency and high throughput."), - "homepage": "https://aistudio.google.com", - "endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-lite:streamGenerateContent", - "model": "gemini-2.5-flash-lite", + "endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-3-flash-preview:streamGenerateContent", + "model": "gemini-3-flash-preview", "requires_key": true, "key_id": "gemini", "key_get_link": "https://aistudio.google.com/app/apikey", @@ -320,31 +294,6 @@ Singleton { "key_get_description": Translation.tr("**Instructions**: Log into Mistral account, go to Keys on the sidebar, click Create new key"), "api_format": "mistral", }), - "github-gpt-5-nano": aiModelComponent.createObject(this, { - "name": "GPT-5 Nano (GH Models)", - "icon": "github-symbolic", - "api_format": "openai", - "description": Translation.tr("Online via %1 | %2's model").arg("GitHub Models").arg("OpenAI"), - "homepage": "https://github.com/marketplace/models", - "endpoint": "https://models.inference.ai.azure.com/chat/completions", - "model": "gpt-5-nano", - "requires_key": true, - "key_id": "github", - "key_get_link": "https://github.com/settings/tokens", - "key_get_description": Translation.tr("**Pricing**: Free tier available with limited rates. See https://docs.github.com/en/billing/concepts/product-billing/github-models\n\n**Instructions**: Generate a GitHub personal access token with Models permission, then set as API key here\n\n**Note**: To use this you will have to set the temperature parameter to 1"), - }), - "openrouter-deepseek-r1": aiModelComponent.createObject(this, { - "name": "DeepSeek R1", - "icon": "deepseek-symbolic", - "description": Translation.tr("Online via %1 | %2's model").arg("OpenRouter").arg("DeepSeek"), - "homepage": "https://openrouter.ai/deepseek/deepseek-r1:free", - "endpoint": "https://openrouter.ai/api/v1/chat/completions", - "model": "deepseek/deepseek-r1:free", - "requires_key": true, - "key_id": "openrouter", - "key_get_link": "https://openrouter.ai/settings/keys", - "key_get_description": Translation.tr("**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key"), - }), } property var modelList: Object.keys(root.models) property var currentModelId: Persistent.states?.ai?.model || modelList[0] diff --git a/dots/.config/quickshell/ii/services/Brightness.qml b/dots/.config/quickshell/ii/services/Brightness.qml index f2f4f0a7e..b0c7f303e 100644 --- a/dots/.config/quickshell/ii/services/Brightness.qml +++ b/dots/.config/quickshell/ii/services/Brightness.qml @@ -148,14 +148,12 @@ Singleton { const brightnessValue = Math.max(monitor.multipliedBrightness, 0); if (isDdc) { const rawValueRounded = Math.max(Math.floor(brightnessValue * monitor.rawMaxBrightness), 1); - setProc.command = ["ddcutil", "-b", busNum, "setvcp", "10", rawValueRounded]; - setProc.startDetached(); + setProc.exec(["ddcutil", "-b", busNum, "setvcp", "10", rawValueRounded]); } else { const valuePercentNumber = Math.floor(brightnessValue * 100); let valuePercent = `${valuePercentNumber}%`; if (valuePercentNumber == 0) valuePercent = "1"; // Prevent fully black - setProc.command = ["brightnessctl", "--class", "backlight", "s", valuePercent, "--quiet"]; - setProc.startDetached(); + setProc.exec(["brightnessctl", "--class", "backlight", "s", valuePercent, "--quiet"]) } } diff --git a/dots/.config/quickshell/ii/services/GlobalFocusGrab.qml b/dots/.config/quickshell/ii/services/GlobalFocusGrab.qml new file mode 100644 index 000000000..6385d3df7 --- /dev/null +++ b/dots/.config/quickshell/ii/services/GlobalFocusGrab.qml @@ -0,0 +1,72 @@ +pragma Singleton +pragma ComponentBehavior: Bound +import QtQuick +import Quickshell +import Quickshell.Hyprland + +/** + * Manages a HyprlandFocusGrab that's to be shared by all windows. + * "Persistent" is for windows that should always be included but not closed on dismiss, like bar and onscreen keyboard. + * "Dismissable" is for stuff like sidebars + */ +Singleton { + id: root + + signal dismissed() + + property list persistent: [] + property list dismissable: [] + + function dismiss() { + root.dismissable = []; + root.dismissed(); + } + + Component.onCompleted: { + console.log("[GlobalFocusGrab] Initialized"); + } + + function addPersistent(window) { + if (root.persistent.indexOf(window) === -1) { + root.persistent.push(window); + } + } + + function removePersistent(window) { + var index = root.persistent.indexOf(window); + if (index !== -1) { + root.persistent.splice(index, 1); + } + } + + function addDismissable(window) { + if (root.dismissable.indexOf(window) === -1) { + root.dismissable.push(window); + } + } + + function removeDismissable(window) { + var index = root.dismissable.indexOf(window); + if (index !== -1) { + root.dismissable.splice(index, 1); + } + } + + function hasActive(element) { + return element?.activeFocus || Array.from( + element?.children + ).some( + (child) => hasActive(child) + ); + } + + HyprlandFocusGrab { + id: grab + windows: root.dismissable.every(w => !w?.focusable) || root.dismissable.some(w => hasActive(w?.contentItem)) ? [...root.dismissable, ...root.persistent] : [...root.dismissable] + active: root.dismissable.length > 0 + onCleared: () => { + root.dismiss(); + } + } + +} diff --git a/dots/.config/quickshell/ii/services/HyprlandData.qml b/dots/.config/quickshell/ii/services/HyprlandData.qml index 7bb437c84..5dfedab6a 100644 --- a/dots/.config/quickshell/ii/services/HyprlandData.qml +++ b/dots/.config/quickshell/ii/services/HyprlandData.qml @@ -88,6 +88,7 @@ Singleton { function onRawEvent(event) { // console.log("Hyprland raw event:", event.name); + if (["openlayer", "closelayer", "screencast"].includes(event.name)) return; updateAll() } } @@ -138,7 +139,9 @@ Singleton { stdout: StdioCollector { id: workspacesCollector onStreamFinished: { - root.workspaces = JSON.parse(workspacesCollector.text); + var rawWorkspaces = JSON.parse(workspacesCollector.text); + // Filter out invalid workspace ids (e.g. lock-screen temp workspace 2147483647 - N) + root.workspaces = rawWorkspaces.filter(ws => ws.id >= 1 && ws.id <= 100); let tempWorkspaceById = {}; for (var i = 0; i < root.workspaces.length; ++i) { var ws = root.workspaces[i]; diff --git a/dots/.config/quickshell/ii/services/MprisController.qml b/dots/.config/quickshell/ii/services/MprisController.qml index 02151f1c1..7faed2274 100644 --- a/dots/.config/quickshell/ii/services/MprisController.qml +++ b/dots/.config/quickshell/ii/services/MprisController.qml @@ -25,22 +25,16 @@ Singleton { property var activeTrack; - property bool hasPlasmaIntegration: false - Process { - id: plasmaIntegrationAvailabilityCheckProc - running: true - command: ["bash", "-c", "command -v plasma-browser-integration-host"] - onExited: (exitCode, exitStatus) => { - root.hasPlasmaIntegration = (exitCode === 0); - } - } + readonly property bool hasActivePlasmaIntegration: Mpris.players.values.some( + p => p.dbusName?.startsWith('org.mpris.MediaPlayer2.plasma-browser-integration') + ) function isRealPlayer(player) { if (!Config.options.media.filterDuplicatePlayers) { return true; } return ( - // Remove unecessary native buses from browsers if there's plasma integration - !(hasPlasmaIntegration && player.dbusName.startsWith('org.mpris.MediaPlayer2.firefox')) && !(hasPlasmaIntegration && player.dbusName.startsWith('org.mpris.MediaPlayer2.chromium')) && + // Remove native browser buses only if plasma-browser-integration is actually active on D-Bus + !(hasActivePlasmaIntegration && player.dbusName.startsWith('org.mpris.MediaPlayer2.firefox')) && !(hasActivePlasmaIntegration && player.dbusName.startsWith('org.mpris.MediaPlayer2.chromium')) && // playerctld just copies other buses and we don't need duplicates !player.dbusName?.startsWith('org.mpris.MediaPlayer2.playerctld') && // Non-instance mpd bus diff --git a/dots/.config/quickshell/ii/services/Network.qml b/dots/.config/quickshell/ii/services/Network.qml index 69bd5337d..943c9f757 100644 --- a/dots/.config/quickshell/ii/services/Network.qml +++ b/dots/.config/quickshell/ii/services/Network.qml @@ -36,13 +36,13 @@ Singleton { property int networkStrength property string materialSymbol: root.ethernet ? "lan" - : root.wifiEnabled + : (root.wifiEnabled && root.wifiStatus === "connected") ? ( - Network.networkStrength > 83 ? "signal_wifi_4_bar" : - Network.networkStrength > 67 ? "network_wifi" : - Network.networkStrength > 50 ? "network_wifi_3_bar" : - Network.networkStrength > 33 ? "network_wifi_2_bar" : - Network.networkStrength > 17 ? "network_wifi_1_bar" : + (root.active?.strength ?? 0) > 83 ? "signal_wifi_4_bar" : + (root.active?.strength ?? 0) > 67 ? "network_wifi" : + (root.active?.strength ?? 0) > 50 ? "network_wifi_3_bar" : + (root.active?.strength ?? 0) > 33 ? "network_wifi_2_bar" : + (root.active?.strength ?? 0) > 17 ? "network_wifi_1_bar" : "signal_wifi_0_bar" ) : (root.wifiStatus === "connecting") @@ -89,9 +89,10 @@ Singleton { network.askingPassword = false; changePasswordProc.exec({ "environment": { - "PASSWORD": password + "PASSWORD": password, + "SSID": network.ssid }, - "command": ["bash", "-c", `nmcli connection modify ${network.ssid} wifi-sec.psk "$PASSWORD"`] + "command": ["bash", "-c", 'nmcli connection modify "$SSID" wifi-sec.psk "$PASSWORD"'] }) } @@ -232,7 +233,7 @@ Singleton { Process { id: updateNetworkStrength running: true - command: ["sh", "-c", "nmcli -f IN-USE,SIGNAL,SSID device wifi | awk '/^\*/{if (NR!=1) {print $2}}'"] + command: ["sh", "-c", "nmcli -f IN-USE,SIGNAL,SSID device wifi | awk '/^\\*/{if (NR!=1) {print $2}}'"] stdout: SplitParser { onRead: data => { root.networkStrength = parseInt(data); diff --git a/dots/.config/quickshell/ii/services/Notifications.qml b/dots/.config/quickshell/ii/services/Notifications.qml index 702da0f8a..a45b86007 100644 --- a/dots/.config/quickshell/ii/services/Notifications.qml +++ b/dots/.config/quickshell/ii/services/Notifications.qml @@ -135,8 +135,8 @@ Singleton { property var groupsByAppName: groupsForList(root.list) property var popupGroupsByAppName: groupsForList(root.popupList) - property var appNameList: appNameListForGroups(root.groupsByAppName) - property var popupAppNameList: appNameListForGroups(root.popupGroupsByAppName) + property list appNameList: appNameListForGroups(root.groupsByAppName) + property list popupAppNameList: appNameListForGroups(root.popupGroupsByAppName) // Quickshell's notification IDs starts at 1 on each run, while saved notifications // can already contain higher IDs. This is for avoiding id collisions diff --git a/dots/.config/quickshell/ii/services/Updates.qml b/dots/.config/quickshell/ii/services/Updates.qml index 48549ac3c..ee9093143 100644 --- a/dots/.config/quickshell/ii/services/Updates.qml +++ b/dots/.config/quickshell/ii/services/Updates.qml @@ -29,7 +29,7 @@ Singleton { Timer { interval: Config.options.updates.checkInterval * 60 * 1000 repeat: true - running: Config.ready + running: Config.ready && Config.options.updates.enableCheck onTriggered: { print("[Updates] Periodic update check due") root.refresh(); @@ -38,7 +38,7 @@ Singleton { Process { id: checkAvailabilityProc - running: true + running: Config.ready && Config.options.updates.enableCheck command: ["which", "checkupdates"] onExited: (exitCode, exitStatus) => { root.available = (exitCode === 0); diff --git a/dots/.config/quickshell/ii/services/Weather.qml b/dots/.config/quickshell/ii/services/Weather.qml index a7bb8d5ce..c42c7cb8e 100644 --- a/dots/.config/quickshell/ii/services/Weather.qml +++ b/dots/.config/quickshell/ii/services/Weather.qml @@ -42,7 +42,8 @@ Singleton { visib: 0, press: 0, temp: 0, - tempFeelsLike: 0 + tempFeelsLike: 0, + lastRefresh: 0, }) function refineData(data) { @@ -75,6 +76,7 @@ Singleton { temp.temp += "°C"; temp.tempFeelsLike += "°C"; } + temp.lastRefresh = DateTime.time + " • " + DateTime.date; root.data = temp; } diff --git a/dots/.config/quickshell/ii/shell.qml b/dots/.config/quickshell/ii/shell.qml index feced64cd..13cf7af4e 100644 --- a/dots/.config/quickshell/ii/shell.qml +++ b/dots/.config/quickshell/ii/shell.qml @@ -3,52 +3,25 @@ //@ pragma Env QT_QUICK_CONTROLS_STYLE=Basic //@ pragma Env QT_QUICK_FLICKABLE_WHEEL_DECELERATION=10000 -// Adjust this to make the shell smaller or larger -//@ pragma Env QT_SCALE_FACTOR=1 +// Remove two slashes below and adjust the value to change the UI scale +////@ pragma Env QT_SCALE_FACTOR=1 -import qs.modules.common -import qs.modules.ii.background -import qs.modules.ii.bar -import qs.modules.ii.cheatsheet -import qs.modules.ii.dock -import qs.modules.ii.lock -import qs.modules.ii.mediaControls -import qs.modules.ii.notificationPopup -import qs.modules.ii.onScreenDisplay -import qs.modules.ii.onScreenKeyboard -import qs.modules.ii.overview -import qs.modules.ii.polkit -import qs.modules.ii.regionSelector -import qs.modules.ii.screenCorners -import qs.modules.ii.sessionScreen -import qs.modules.ii.sidebarLeft -import qs.modules.ii.sidebarRight -import qs.modules.ii.overlay -import qs.modules.ii.verticalBar -import qs.modules.ii.wallpaperSelector - -import qs.modules.waffle.actionCenter -import qs.modules.waffle.background -import qs.modules.waffle.bar -import qs.modules.waffle.lock -import qs.modules.waffle.notificationCenter -import qs.modules.waffle.onScreenDisplay -import qs.modules.waffle.polkit -import qs.modules.waffle.startMenu -import qs.modules.waffle.sessionScreen -import qs.modules.waffle.taskView +import "modules/common" +import "services" +import "panelFamilies" import QtQuick import QtQuick.Window import Quickshell import Quickshell.Io import Quickshell.Hyprland -import qs.services ShellRoot { id: root - // Force initialization of some singletons + // Stuff for every panel family + ReloadPopup {} + Component.onCompleted: { MaterialThemeLoader.reapplyTheme() Hyprsunset.load() @@ -59,60 +32,33 @@ ShellRoot { Updates.load() } - // Load enabled stuff - // Well, these loaders only *allow* them to be loaded, to always load or not is defined in each component - // The media controls for example is not loaded if it's not opened - PanelLoader { identifier: "iiBar"; extraCondition: !Config.options.bar.vertical; component: Bar {} } - PanelLoader { identifier: "iiBackground"; component: Background {} } - PanelLoader { identifier: "iiCheatsheet"; component: Cheatsheet {} } - PanelLoader { identifier: "iiDock"; extraCondition: Config.options.dock.enable; component: Dock {} } - PanelLoader { identifier: "iiLock"; component: Lock {} } - PanelLoader { identifier: "iiMediaControls"; component: MediaControls {} } - PanelLoader { identifier: "iiNotificationPopup"; component: NotificationPopup {} } - PanelLoader { identifier: "iiOnScreenDisplay"; component: OnScreenDisplay {} } - PanelLoader { identifier: "iiOnScreenKeyboard"; component: OnScreenKeyboard {} } - PanelLoader { identifier: "iiOverlay"; component: Overlay {} } - PanelLoader { identifier: "iiOverview"; component: Overview {} } - PanelLoader { identifier: "iiPolkit"; component: Polkit {} } - PanelLoader { identifier: "iiRegionSelector"; component: RegionSelector {} } - PanelLoader { identifier: "iiScreenCorners"; component: ScreenCorners {} } - PanelLoader { identifier: "iiSessionScreen"; component: SessionScreen {} } - PanelLoader { identifier: "iiSidebarLeft"; component: SidebarLeft {} } - PanelLoader { identifier: "iiSidebarRight"; component: SidebarRight {} } - PanelLoader { identifier: "iiVerticalBar"; extraCondition: Config.options.bar.vertical; component: VerticalBar {} } - PanelLoader { identifier: "iiWallpaperSelector"; component: WallpaperSelector {} } - - PanelLoader { identifier: "wActionCenter"; component: WaffleActionCenter {} } - PanelLoader { identifier: "wBar"; component: WaffleBar {} } - PanelLoader { identifier: "wBackground"; component: WaffleBackground {} } - PanelLoader { identifier: "wLock"; component: WaffleLock {} } - PanelLoader { identifier: "wNotificationCenter"; component: WaffleNotificationCenter {} } - PanelLoader { identifier: "wOnScreenDisplay"; component: WaffleOSD {} } - PanelLoader { identifier: "wPolkit"; component: WafflePolkit {} } - PanelLoader { identifier: "wStartMenu"; component: WaffleStartMenu {} } - PanelLoader { identifier: "wSessionScreen"; component: WaffleSessionScreen {} } - PanelLoader { identifier: "wTaskView"; component: WaffleTaskView {} } - ReloadPopup {} - - component PanelLoader: LazyLoader { - required property string identifier - property bool extraCondition: true - active: Config.ready && Config.options.enabledPanels.includes(identifier) && extraCondition - } // Panel families property list families: ["ii", "waffle"] - property var panelFamilies: ({ - "ii": ["iiBar", "iiBackground", "iiCheatsheet", "iiDock", "iiLock", "iiMediaControls", "iiNotificationPopup", "iiOnScreenDisplay", "iiOnScreenKeyboard", "iiOverlay", "iiOverview", "iiPolkit", "iiRegionSelector", "iiScreenCorners", "iiSessionScreen", "iiSidebarLeft", "iiSidebarRight", "iiVerticalBar", "iiWallpaperSelector"], - "waffle": ["wActionCenter", "wBar", "wBackground", "wLock", "wNotificationCenter", "wOnScreenDisplay", "wTaskView", "wPolkit", "wSessionScreen", "wStartMenu", "iiCheatsheet", "iiNotificationPopup", "iiOnScreenKeyboard", "iiOverlay", "iiRegionSelector", "iiWallpaperSelector"], - }) function cyclePanelFamily() { const currentIndex = families.indexOf(Config.options.panelFamily) const nextIndex = (currentIndex + 1) % families.length Config.options.panelFamily = families[nextIndex] - Config.options.enabledPanels = panelFamilies[Config.options.panelFamily] } + component PanelFamilyLoader: LazyLoader { + required property string identifier + property bool extraCondition: true + active: Config.ready && Config.options.panelFamily === identifier && extraCondition + } + + PanelFamilyLoader { + identifier: "ii" + component: IllogicalImpulseFamily {} + } + + PanelFamilyLoader { + identifier: "waffle" + component: WaffleFamily {} + } + + + // Shortcuts IpcHandler { target: "panelFamily" diff --git a/dots/.config/quickshell/ii/translations/de_DE.json b/dots/.config/quickshell/ii/translations/de_DE.json new file mode 100644 index 000000000..4feb36449 --- /dev/null +++ b/dots/.config/quickshell/ii/translations/de_DE.json @@ -0,0 +1,608 @@ +{ + "Material cookie": "Material Cookie", + "Style: Blurred": "Stil: Verschwommen", + "Unknown device": "Unbekanntes Gerät", + "Change any time later with /dark, /light, /wallpaper in the launcher\nIf the shell's colors aren't changing:\n 1. Open the right sidebar with Super+N\n 2. Click \"Reload Hyprland & Quickshell\" in the top-right corner": "Jederzeit später ändern mit /dark, /light, /wallpaper im Launcher\nFalls sich die Shell-Farben nicht ändern:\n 1. Öffne die rechte Seitenleiste mit Super+N\n 2. Klicke auf \"Hyprland & Quickshell neu laden\" in der oberen rechten Ecke", + "No pending tasks": "Keine ausstehenden Aufgaben", + "Positioning": "Positionierung", + "Set temperature (randomness) of the model. Values range between 0 to 2 for Gemini, 0 to 1 for other models. Default is 0.5.": "Temperatur (Zufälligkeit) des Modells einstellen. Wertebereich zwischen 0 und 2 für Gemini, 0 bis 1 für andere Modelle. Standard ist 0,5.", + "Critical warning": "Kritische Warnung", + "Unknown Artist": "Unbekannter Künstler", + "Web search": "Websuche", + "Load prompt from %1": "Prompt von %1 laden", + "Attach a file. Only works with Gemini.": "Datei anhängen. Funktioniert nur mit Gemini.", + "Reboot": "Neustart", + "API key:\n\n```txt\n%1\n```": "API-Schlüssel:\n\n```txt\n%1\n```", + "Pinned on startup": "Beim Start angeheftet", + "Right": "Rechts", + "Reboot to firmware settings": "Neustart zu Firmware-Einstellungen", + "Automatically hide": "Automatisch ausblenden", + "Waiting for response...": "Warte auf Antwort...", + "To Do": "Aufgaben", + "Full": "Voll", + "Select Language": "Sprache auswählen", + "Password": "Passwort", + "Bluetooth devices": "Bluetooth-Geräte", + "Enable": "Aktivieren", + "Elements": "Elemente", + "Start": "Start", + "Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers": "Zufälliges SFW-Anime-Hintergrundbild von Konachan\nBild wird in ~/Pictures/Wallpapers gespeichert", + "The popular one | Best quantity, but quality can vary wildly": "Der beliebte | Beste Menge, aber Qualität kann stark variieren", + "System uptime:": "Systemlaufzeit:", + "illogical-impulse Welcome": "illogical-impulse Willkommen", + "Code saved to file": "Code in Datei gespeichert", + "Info": "Info", + "Preferred wallpaper zoom (%)": "Bevorzugter Hintergrundbild-Zoom (%)", + "Time": "Zeit", + "Help & Support": "Hilfe & Support", + "Bubble": "Blase", + "Large images | God tier quality, no NSFW.": "Große Bilder | Höchste Qualität, kein NSFW.", + "Dark": "Dunkel", + "Center clock": "Zentrale Uhr", + "Search, calculate or run": "Suchen, berechnen oder ausführen", + "Region height": "Regionshöhe", + "Load chat": "Chat laden", + "Gives the model search capabilities (immediately)": "Gibt dem Modell Suchfähigkeiten (sofort)", + "Depends on workspace": "Hängt vom Arbeitsbereich ab", + "Blurred style": "Verschwommener Stil", + "Screenshot tool": "Screenshot-Tool", + "Enter password": "Passwort eingeben", + "Search the web": "Im Web suchen", + "Local only": "Nur lokal", + "at": "um", + "Math": "Mathematik", + "Consider plugging in your device": "Erwäge, dein Gerät anzuschließen", + "Workspaces shown": "Angezeigte Arbeitsbereiche", + "Place the corners to trigger at the bottom": "Platziere die Ecken zum Auslösen am unteren Rand", + "No API key\nSet it with /key YOUR_API_KEY": "Kein API-Schlüssel\nSetze ihn mit /key DEIN_API_SCHLUESSEL", + "Auto (System)": "Auto (System)", + "Arrow keys to navigate, Enter to select\nEsc or click anywhere to cancel": "Pfeiltasten zum Navigieren, Enter zum Auswählen\nEsc oder irgendwo klicken zum Abbrechen", + "Critically low battery": "Kritisch niedrige Batterie", + "Open editor": "Editor öffnen", + "%1 notifications": "%1 Benachrichtigungen", + "Region width": "Regionsbreite", + "Max allowed increase": "Maximal erlaubter Anstieg", + "Enable translator": "Übersetzer aktivieren", + "Constantly rotate": "Ständig rotieren", + "Automatically suspends the system when battery is low": "Suspendiert das System automatisch bei niedrigem Batteriestand", + "Cannot find a GPS service. Using the fallback method instead.": "GPS-Dienst nicht gefunden. Verwende stattdessen die Fallback-Methode.", + "Qt apps": "Qt-Apps", + "Color picker": "Farbwähler", + "Interface": "Schnittstelle", + "Tint app icons": "App-Symbole einfärben", + "Select the language for the user interface.\n\"Auto\" will use your system's locale.": "Wähle die Sprache für die Benutzeroberfläche.\n\"Auto\" verwendet deine System-Lokalisierung.", + "Show quote": "Zitat anzeigen", + "Local Ollama model | %1": "Lokales Ollama-Modell | %1", + "Show clock": "Uhr anzeigen", + "Usage: %1superpaste NUM_OF_ENTRIES[i]\nSupply i when you want images\nExamples:\n%1superpaste 4i for the last 4 images\n%1superpaste 7 for the last 7 entries": "Verwendung: %1superpaste ANZAHL[i]\nGib i an, wenn du Bilder möchtest\nBeispiele:\n%1superpaste 4i für die letzten 4 Bilder\n%1superpaste 7 für die letzten 7 Einträge", + "Audio": "Audio", + "Corner style": "Eckenstil", + "No media": "Keine Medien", + "Unknown function call: %1": "Unbekannter Funktionsaufruf: %1", + "Online | %1's model | Delivers fast, responsive and well-formatted answers. Disadvantages: not very eager to do stuff; might make up unknown function calls": "Online | %1s Modell | Liefert schnelle, reaktionsschnelle und gut formatierte Antworten. Nachteile: nicht sehr eifrig, Dinge zu tun; könnte unbekannte Funktionsaufrufe erfinden", + "Volume": "Lautstärke", + "Medium": "Mittel", + "Copy code": "Code kopieren", + "Exceeded max allowed": "Maximaler Wert überschritten", + "Keep right sidebar loaded": "Rechte Seitenleiste geladen halten", + "Left": "Links", + "High": "Hoch", + "Rect": "Rechteck", + "Lap": "Runde", + "Clear": "Löschen", + "Screen snip": "Bildschirmausschnitt", + "Reset": "Zurücksetzen", + "Back": "Zurück", + "Dark/Light toggle": "Dunkel/Hell umschalten", + "12h am/pm": "12h am/pm", + "Download complete": "Download abgeschlossen", + "Enable blur": "Unschärfe aktivieren", + "Second hand": "Sekundenzeiger", + "Bar & screen": "Leiste & Bildschirm", + "Discharging:": "Entladung:", + "Up %1": "Hoch %1", + "Low": "Niedrig", + "Hour hand": "Stundenzeiger", + "Clear chat history": "Chat-Verlauf löschen", + "Fruit Salad": "Fruchtsalat", + "%1 Safe Storage": "%1 Sicherer Speicher", + "Hibernate": "Ruhezustand", + "Delete": "Löschen", + "OK": "OK", + "Settings": "Einstellungen", + "This is usually safe and needed for your browser and AI sidebar anyway\nMostly useful for those who use lock on startup instead of a display manager that does it (GDM, SDDM, etc.)": "Dies ist normalerweise sicher und wird ohnehin für deinen Browser und die AI-Seitenleiste benötigt\nHauptsächlich nützlich für diejenigen, die eine Sperre beim Start verwenden, anstatt eines Display-Managers, der das übernimmt (GDM, SDDM, etc.)", + "Use Hyprlock (instead of Quickshell)": "Hyprlock verwenden (anstatt Quickshell)", + "Crosshair code (in Valorant's format)": "Fadenkreuz-Code (im Valorant-Format)", + "Silent": "Stumm", + "Useless buttons": "Nutzlose Buttons", + "Hover to reveal": "Bewegen zum Anzeigen", + "Wallpaper & Colors": "Hintergrundbild & Farben", + "Auto": "Auto", + "Visibility": "Sichtbarkeit", + "Shell & utilities": "Shell & Utilities", + "Hollow": "Hohl", + "illogical-impulse": "illogical-impulse", + "Use the system file picker instead\nRight-click to make this the default behavior": "System-Dateiauswahl verwenden\nRechtsklick, um dies zum Standardverhalten zu machen", + "On-screen display": "Bildschirmanzeige", + "Dotfiles": "Dotfiles", + "Search wallpapers": "Hintergrundbilder suchen", + "Mic toggle": "Mikrofon umschalten", + "Input": "Eingabe", + "Also unlock keyring": "Auch Schlüsselbund entsperren", + "Configuration": "Konfiguration", + "Keep system awake": "System wach halten", + "Unknown command:": "Unbekannter Befehl:", + "Anime boorus": "Anime-Boorus", + "To Do:": "Aufgaben:", + "Uses Gemini to categorize the wallpaper then picks a preset based on it.\nYou'll need to set Gemini API key on the left sidebar first.\nImages are downscaled for performance, but just to be safe,\ndo not select wallpapers with sensitive information.": "Verwendet Gemini, um das Hintergrundbild zu kategorisieren und wählt dann ein Preset basierend darauf aus.\nDu musst zuerst den Gemini-API-Schlüssel in der linken Seitenleiste festlegen.\nBilder werden für die Leistung herunterskaliert, aber nur zur Sicherheit,\nwähle keine Hintergrundbilder mit sensiblen Informationen aus.", + "Bottom": "Unten", + "Clear the current list of images": "Aktuelle Bilderliste löschen", + "Sunrise": "Sonnenaufgang", + "Show app icons": "App-Symbole anzeigen", + "Format": "Format", + "Make sure your player has MPRIS support\nor try turning off duplicate player filtering": "Stelle sicher, dass dein Player MPRIS-Unterstützung hat\noder versuche, die doppelte Player-Filterung zu deaktivieren", + "Pause": "Pause", + "Desktop": "Desktop", + "Conflicts with the shell's system tray implementation": "Kollidiert mit der System-Tray-Implementierung der Shell", + "Your package manager is running": "Dein Paket-Manager läuft", + "Conflicts with the shell's notification implementation": "Kollidiert mit der Benachrichtigungsimplementierung der Shell", + "Unknown Album": "Unbekanntes Album", + "Pick wallpaper image on your system": "Hintergrundbild auf deinem System auswählen", + "Used:": "Verwendet:", + "Cheat sheet": "Kurzreferenz", + "Clock style": "Uhr-Stil", + "No audio source": "Keine Audioquelle", + "Paired": "Gepaart", + "Documentation": "Dokumentation", + "No": "Nein", + "Pills": "Pillen", + "Thought": "Gedanke", + "When this is off you'll have to click": "Wenn dies aus ist, musst du klicken", + "Select output device": "Ausgabegerät auswählen", + "Logout": "Abmelden", + "Tip: Close a window with Super+Q": "Tipp: Fenster mit Super+Q schließen", + "Finished tasks will go here": "Fertige Aufgaben werden hier angezeigt", + "Terminal: Harmony (%)": "Terminal: Harmonie (%)", + "Corner open": "Ecke öffnen", + "Shell conflicts killer": "Shell-Konflikt-Killer", + "Clean stuff | Excellent quality, no NSFW": "Saubere Sachen | Ausgezeichnete Qualität, kein NSFW", + "Scroll to change volume": "Scrollen zum Ändern der Lautstärke", + "Wind": "Wind", + "API key is set\nChange with /key YOUR_API_KEY": "API-Schlüssel ist gesetzt\nÄndern mit /key DEIN_API_SCHLUESSEL", + "Neutral": "Neutral", + "12h AM/PM": "12h AM/PM", + "Number show delay when pressing Super (ms)": "Anzeigeverzögerung für Zahlen beim Drücken von Super (ms)", + "Fill": "Füllen", + "Always show numbers": "Zahlen immer anzeigen", + "Dot": "Punkt", + "Provider set to": "Anbieter gesetzt auf", + "Unknown Title": "Unbekannter Titel", + "Anime": "Anime", + "Refreshing (manually triggered)": "Aktualisiere (manuell ausgelöst)", + "Dock": "Dock", + "Require password to power off/restart": "Passwort für Ausschalten/Neustart erforderlich", + "Line": "Linie", + "Weather": "Wetter", + "All-rounder | Good quality, decent quantity": "Allrounder | Gute Qualität, anständige Menge", + "Scale (%)": "Skalierung (%)", + "Copy": "Kopieren", + "Usage": "Verwendung", + "Type /key to get started with online models\nCtrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window": "Tippe /key, um mit Online-Modellen zu beginnen\nStrg+O, um die Seitenleiste zu erweitern\nStrg+P, um die Seitenleiste in ein Fenster zu lösen", + "Set the tool to use for the model.": "Das zu verwendende Tool für das Modell festlegen.", + "Disable tools": "Tools deaktivieren", + "Connect": "Verbinden", + "Allow NSFW": "NSFW erlauben", + "Registration failed. Please inspect manually with the warp-cli command": "Registrierung fehlgeschlagen. Bitte manuell mit dem Befehl warp-cli überprüfen", + "Time to full:": "Zeit bis voll:", + "Session": "Sitzung", + "Services": "Dienste", + "Nothing here!": "Nichts hier!", + "Overview": "Übersicht", + "Random: osu! seasonal": "Zufällig: osu! saisonal", + "If you want to somehow use fingerprint unlock...": "Wenn du irgendwie die Fingerabdruck-Entsperrung verwenden möchtest...", + "Minute hand": "Minutenzeiger", + "Notifications": "Benachrichtigungen", + "Enable if you want clocks to show seconds accurately": "Aktivieren, wenn Uhren Sekunden genau anzeigen sollen", + "Timer": "Timer", + "Quote settings": "Zitat-Einstellungen", + "System prompt": "System-Prompt", + "Classic": "Klassisch", + "Close": "Schließen", + "Disconnect": "Trennen", + "Go to source (%1)": "Zur Quelle gehen (%1)", + "EasyEffects | Right-click to configure": "EasyEffects | Rechtsklick zum Konfigurieren", + "Forget": "Vergessen", + "Output": "Ausgabe", + "Date style": "Datums-Stil", + "System": "System", + "Usage: %1tool TOOL_NAME": "Verwendung: %1tool TOOL_NAME", + "Workspaces": "Arbeitsbereiche", + "Calendar": "Kalender", + "**Instructions**: Log into Mistral account, go to Keys on the sidebar, click Create new key": "**Anleitung**: Bei Mistral-Konto anmelden, zu Schlüsseln in der Seitenleiste gehen, auf Neuen Schlüssel erstellen klicken", + "Volume limit": "Lautstärkegrenze", + "Sunset": "Sonnenuntergang", + "Dial style": "Zifferblatt-Stil", + "Hi there! First things first...": "Hallo! Erst mal das Wichtigste...", + "Save chat to %1": "Chat nach %1 speichern", + "Security": "Sicherheit", + "Total token count\nInput: %1\nOutput: %2": "Gesamte Token-Anzahl\nEingabe: %1\nAusgabe: %2", + "Cancel wallpaper selection": "Hintergrundbild-Auswahl abbrechen", + "Please charge!\nAutomatic suspend triggers at %1": "Bitte aufladen!\nAutomatische Suspendierung bei %1", + "Terminal: Harmonize threshold": "Terminal: Harmonisiere Schwellenwert", + "Be patient...": "Geduld...", + "Utility buttons": "Utility-Buttons", + "Tonal Spot": "Tonal Spot", + "Prevents abrupt increments and restricts volume limit": "Verhindert abrupte Erhöhungen und begrenzt die Lautstärkegrenze", + "Set the current API provider": "Aktuellen API-Anbieter festlegen", + "Connection failed. Please inspect manually with the warp-cli command": "Verbindung fehlgeschlagen. Bitte manuell mit dem Befehl warp-cli überprüfen", + "Networking": "Netzwerk", + "Tint icons": "Symbole einfärben", + "Low battery": "Niedrige Batterie", + "Make icons pinned by default": "Symbole standardmäßig anheften", + "Get the next page of results": "Nächste Seite der Ergebnisse abrufen", + "Invalid API provider. Supported: \n-": "Ungültiger API-Anbieter. Unterstützt: \n-", + "Show \"Locked\" text": "\"Gesperrt\"-Text anzeigen", + "**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key": "**Preis**: kostenlos. Datennutzungsrichtlinie variiert je nach deinen OpenRouter-Kontoeinstellungen.\n\n**Anleitung**: Bei OpenRouter-Konto anmelden, zu Schlüsseln im oberen rechten Menü gehen, auf API-Schlüssel erstellen klicken", + "Not visible to model": "Nicht sichtbar für Modell", + "Lock screen": "Bildschirm sperren", + "Save to Downloads": "In Downloads speichern", + "Expressive": "Ausdrucksvoll", + "Suspend at": "Suspendieren bei", + "Jump to current month": "Zum aktuellen Monat springen", + "Bold": "Fett", + "Waifus only | Excellent quality, limited quantity": "Nur Waifus | Ausgezeichnete Qualität, begrenzte Menge", + "Click to toggle light/dark mode\n(applied when wallpaper is chosen)": "Klicken zum Umschalten zwischen Hell/Dunkel-Modus\n(wird angewendet, wenn Hintergrundbild gewählt wird)", + "Visualize region": "Region visualisieren", + "Quote": "Zitat", + "Sleep": "Schlaf", + "Hit \"/\" to search": "Drücke \"/\" zum Suchen", + "Hug": "Umarmung", + "Report a Bug": "Fehler melden", + "Precipitation": "Niederschlag", + "Crosshair": "Fadenkreuz", + "Model set to %1": "Modell gesetzt auf %1", + "Rows": "Zeilen", + "Top": "Oben", + "Long break": "Lange Pause", + "Superpaste": "Superpaste", + "Screen round corner": "Bildschirm abgerundete Ecke", + "Online | Google's model\nNewer model that's slower than its predecessor but should deliver higher quality answers": "Online | Googles Modell\nNeueres Modell, das langsamer ist als sein Vorgänger, aber höhere Qualität liefern sollte", + "Rainbow": "Regenbogen", + "Weeb": "Weeb", + "Large language models": "Große Sprachmodelle", + "Online models disallowed\n\nControlled by `policies.ai` config option": "Online-Modelle nicht erlaubt\n\nKontrolliert durch `policies.ai` Konfigurationsoption", + "Policies": "Richtlinien", + "Temperature must be between 0 and 2": "Temperatur muss zwischen 0 und 2 liegen", + "Automatic suspend": "Automatische Suspendierung", + "Extra wallpaper zoom (%)": "Zusätzlicher Hintergrundbild-Zoom (%)", + "GitHub": "GitHub", + "%1 | Right-click to configure": "%1 | Rechtsklick zum Konfigurieren", + "**Pricing**: Free tier available with limited rates. See https://docs.github.com/en/billing/concepts/product-billing/github-models\n\n**Instructions**: Generate a GitHub personal access token with Models permission, then set as API key here\n\n**Note**: To use this you will have to set the temperature parameter to 1": "**Preis**: Kostenloser Tarif mit begrenzten Raten verfügbar. Siehe https://docs.github.com/en/billing/concepts/product-billing/github-models\n\n**Anleitung**: Erstelle einen GitHub Personal Access Token mit Models-Berechtigung, dann hier als API-Schlüssel festlegen\n\n**Hinweis**: Um dies zu verwenden, musst du den Temperatur-Parameter auf 1 setzen", + "Edit directory": "Verzeichnis bearbeiten", + "Action": "Aktion", + "Search": "Suchen", + "Tip: right-clicking a group\nalso expands it": "Tipp: Rechtsklick auf eine Gruppe\nerweitert sie ebenfalls", + "Bar": "Leiste", + "Show regions of potential interest": "Regionen von potenziellem Interesse anzeigen", + "Clipboard": "Zwischenablage", + "Stopwatch": "Stoppuhr", + "Enter text to translate...": "Text zum Übersetzen eingeben...", + "App": "App", + "Sides": "Seiten", + "No active player": "Kein aktiver Player", + "Not all options are available in this app. You should also check the config file by hitting the \"Config file\" button on the topleft corner or opening %1 manually.": "Nicht alle Optionen sind in dieser App verfügbar. Du solltest auch die Konfigurationsdatei überprüfen, indem du auf den \"Konfigurationsdatei\"-Button in der oberen linken Ecke klickst oder %1 manuell öffnest.", + "There might be a download in progress": "Möglicherweise läuft gerade ein Download", + "Math result": "Mathematik-Ergebnis", + "Fidelity": "Wiedergabetreue", + "Prefixes": "Präfixe", + "Terminal": "Terminal", + "Incorrect password": "Falsches Passwort", + "Line-separated": "Zeilengetrennt", + "Always": "Immer", + "☕ Break: %1 minutes": "☕ Pause: %1 Minuten", + "Depends on sidebars": "Hängt von Seitenleisten ab", + "Tool set to: %1": "Tool gesetzt auf: %1", + "Save chat": "Chat speichern", + "Crosshair overlay": "Fadenkreuz-Overlay", + "Keybinds": "Tastenkürzel", + "Launch": "Starten", + "Could be better if you make a ton of typos,\nbut results can be weird and might not work with acronyms\n(e.g. \"GIMP\" might not give you the paint program)": "Könnte besser sein, wenn du viele Tippfehler machst,\naber Ergebnisse können seltsam sein und funktionieren möglicherweise nicht mit Akronymen\n(z.B. könnte \"GIMP\" dir nicht das Malprogramm geben)", + "Choose model": "Modell wählen", + "Base URL": "Basis-URL", + "Float": "Schweben", + "Wallpaper parallax": "Hintergrundbild-Parallaxe", + "Invalid arguments. Must provide `command`.": "Ungültige Argumente. Muss `command` bereitstellen.", + "Fully charged": "Vollständig geladen", + "Earbang protection": "Ohrschutz", + "Low warning": "Niedrige Warnung", + "Advanced": "Erweitert", + "Scroll to change brightness": "Scrollen zum Ändern der Helligkeit", + "Loaded the following system prompt\n\n---\n\n%1": "Folgenden System-Prompt geladen\n\n---\n\n%1", + "Show next time": "Nächstes Mal anzeigen", + "Current tool: %1\nSet it with %2tool TOOL": "Aktuelles Tool: %1\nSetze es mit %2tool TOOL", + "Unread indicator: show count": "Ungelesen-Indikator: Anzahl anzeigen", + "Press Super+G to toggle appearance": "Drücke Super+G zum Umschalten des Erscheinungsbilds", + "That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "Das hat nicht funktioniert. Tipps:\n- Überprüfe deine Tags und NSFW-Einstellungen\n- Wenn du keinen Tag im Kopf hast, tippe eine Seitennummer", + "Dots": "Dots", + "Cloudflare WARP (1.1.1.1)": "Cloudflare WARP (1.1.1.1)", + "Volume mixer": "Lautstärkemixer", + "Config file": "Konfigurationsdatei", + "API key set for %1": "API-Schlüssel gesetzt für %1", + "Online via %1 | %2's model": "Online über %1 | %2s Modell", + "Shell command": "Shell-Befehl", + "Such regions could be images or parts of the screen that have some containment.\nMight not always be accurate.\nThis is done with an image processing algorithm run locally and no AI is used.": "Solche Regionen könnten Bilder oder Teile des Bildschirms sein, die eine gewisse Eingrenzung haben.\nMöglicherweise nicht immer genau.\nDies wird mit einem lokal ausgeführten Bildverarbeitungsalgorithmus durchgeführt und keine KI wird verwendet.", + "Reload Hyprland & Quickshell": "Hyprland & Quickshell neu laden", + "Resources": "Ressourcen", + "Brightness": "Helligkeit", + "Unknown": "Unbekannt", + "Polling interval (ms)": "Abfrageintervall (ms)", + "Lock": "Sperren", + "Thinking": "Denken", + "Approve": "Genehmigen", + "Unfinished": "Unvollständig", + "Random: Konachan": "Zufällig: Konachan", + "Connected": "Verbunden", + "Wallpaper safety enforced": "Hintergrundbild-Sicherheit erzwungen", + "Invalid arguments. Must provide `key` and `value`.": "Ungültige Argumente. Muss `key` und `value` bereitstellen.", + "24h": "24h", + "Allows you to open sidebars by clicking or hovering screen corners regardless of bar position": "Ermöglicht es, Seitenleisten durch Klicken oder Bewegen über Bildschirmecken zu öffnen, unabhängig von der Leistenposition", + "Bar style": "Leisten-Stil", + "Load:": "Last:", + "Open file link": "Dateilink öffnen", + "Ignored if terminal theming is not enabled": "Ignoriert, wenn Terminal-Theming nicht aktiviert ist", + "Shutdown": "Herunterfahren", + "Hour marks": "Stundenmarkierungen", + "Random osu! seasonal background\nImage is saved to ~/Pictures/Wallpapers": "Zufälliger osu! saisonaler Hintergrund\nBild wird in ~/Pictures/Wallpapers gespeichert", + "Online | Google's model\nFast, can perform searches for up-to-date information": "Online | Googles Modell\nSchnell, kann Suchen nach aktuellen Informationen durchführen", + "Current model: %1\nSet it with %2model MODEL": "Aktuelles Modell: %1\nSetze es mit %2model MODELL", + "Select input device": "Eingabegerät auswählen", + "Connect to Wi-Fi": "Mit WLAN verbinden", + "... and %1 more": "... und %1 weitere", + "Cookie clock settings": "Cookie-Uhr-Einstellungen", + "Brightness and volume": "Helligkeit und Lautstärke", + "Choose file": "Datei wählen", + "Invalid model. Supported: \n```": "Ungültiges Modell. Unterstützt: \n```", + "Task Manager": "Task-Manager", + "Charging:": "Lädt:", + "Illegal increment": "Ungültige Erhöhung", + "Total:": "Gesamt:", + "or": "oder", + "Battery": "Batterie", + "Timeout duration (if not defined by notification) (ms)": "Timeout-Dauer (wenn nicht durch Benachrichtigung definiert) (ms)", + "Cancel": "Abbrechen", + "Locked": "Gesperrt", + "Temperature: %1": "Temperatur: %1", + "Hover to trigger": "Bewegen zum Auslösen", + "Command rejected by user": "Befehl vom Benutzer abgelehnt", + "User agent (for services that require it)": "User-Agent (für Dienste, die es benötigen)", + "Saved to %1": "Gespeichert nach %1", + "Emojis": "Emojis", + "Color generation": "Farbgenerierung", + "Welcome app": "Willkommens-App", + "Humidity": "Luftfeuchtigkeit", + "Page %1": "Seite %1", + "Feels like %1": "Fühlt sich an wie %1", + "Distro": "Distro", + "Transparency": "Transparenz", + "%1 • %2 tasks": "%1 • %2 Aufgaben", + "Markdown test": "Markdown-Test", + "Invalid tool. Supported tools:\n- %1": "Ungültiges Tool. Unterstützte Tools:\n- %1", + "No notifications": "Keine Benachrichtigungen", + "The hentai one | Great quantity, a lot of NSFW, quality varies wildly": "Der Hentai eine | Große Menge, viel NSFW, Qualität variiert stark", + "Bluetooth": "Bluetooth", + "Resume": "Fortsetzen", + "Work safety": "Arbeitssicherheit", + "Temperature\nChange with /temp VALUE": "Temperatur\nÄndern mit /temp WERT", + "Terminal: Foreground boost (%)": "Terminal: Vordergrund-Verstärkung (%)", + "Night Light | Right-click to toggle Auto mode": "Nachtlicht | Rechtsklick zum Umschalten des Auto-Modus", + "Closet": "Schrank", + "Yes": "Ja", + "Columns": "Spalten", + "To set an API key, pass it with the %4 command\n\nTo view the key, pass \"get\" with the command
\n\n### For %1:\n\n**Link**: %2\n\n%3": "Um einen API-Schlüssel zu setzen, übergebe ihn mit dem Befehl %4\n\nUm den Schlüssel anzuzeigen, übergebe \"get\" mit dem Befehl
\n\n### Für %1:\n\n**Link**: %2\n\n%3", + "Kill conflicting programs?": "Konfliktierende Programme beenden?", + "For storing API keys and other sensitive information": "Zum Speichern von API-Schlüsseln und anderen sensiblen Informationen", + "Reject": "Ablehnen", + "Set API key": "API-Schlüssel setzen", + ". Notes for Zerochan:\n- You must enter a color\n- Set your zerochan username in `sidebar.booru.zerochan.username` config option. You [might be banned for not doing so](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!": ". Hinweise für Zerochan:\n- Du musst eine Farbe eingeben\n- Setze deinen Zerochan-Benutzernamen in der Konfigurationsoption `sidebar.booru.zerochan.username`. Du [könntest gesperrt werden, wenn du das nicht tust](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!", + "Content": "Inhalt", + "Pomodoro": "Pomodoro", + "Vertical": "Vertikal", + "Pick a wallpaper": "Hintergrundbild wählen", + "Load chat from %1": "Chat von %1 laden", + "Launch on startup": "Beim Start starten", + "Add": "Hinzufügen", + "Style: general": "Stil: allgemein", + "Use Levenshtein distance-based algorithm instead of fuzzy": "Levenshtein-Distanz-basierten Algorithmus anstatt Fuzzy verwenden", + "Shell & utilities theming must also be enabled": "Shell & Utilities-Theming muss ebenfalls aktiviert sein", + "Workspace": "Arbeitsbereich", + "Translator": "Übersetzer", + "Free:": "Frei:", + "🌿 Long break: %1 minutes": "🌿 Lange Pause: %1 Minuten", + "Value scroll": "Wert-Scrollen", + "Bar position": "Leisten-Position", + "Language": "Sprache", + "Current API endpoint: %1\nSet it with %2mode PROVIDER": "Aktueller API-Endpunkt: %1\nSetze ihn mit %2mode ANBIETER", + "Remember that on most devices one can always hold the power button to force shutdown\nThis only makes it a tiny bit harder for accidents to happen": "Denke daran, dass man auf den meisten Geräten immer die Ein-/Aus-Taste gedrückt halten kann, um einen erzwungenen Ausschaltvorgang durchzuführen\nDies macht es nur ein kleines bisschen schwieriger, dass Unfälle passieren", + "AI": "KI", + "Task description": "Aufgabenbeschreibung", + "Add task": "Aufgabe hinzufügen", + "Donate": "Spenden", + "Disable NSFW content": "NSFW-Inhalt deaktivieren", + "Set the system prompt for the model.": "Den System-Prompt für das Modell festlegen.", + "Done": "Fertig", + "Focus": "Fokus", + "Open the shell config file.\nIf the button doesn't work or doesn't open in your favorite editor,\nyou can manually open ~/.config/illogical-impulse/config.json": "Shell-Konfigurationsdatei öffnen.\nFalls der Button nicht funktioniert oder nicht in deinem bevorzugten Editor öffnet,\nkannst du ~/.config/illogical-impulse/config.json manuell öffnen", + "View Markdown source": "Markdown-Quelle anzeigen", + "Border": "Rahmen", + "Temperature set to %1": "Temperatur gesetzt auf %1", + "Online | Google's model\nGoogle's state-of-the-art multipurpose model that excels at coding and complex reasoning tasks.": "Online | Googles Modell\nGoogles modernstes Mehrzweckmodell, das sich bei Programmierung und komplexen Denkaufgaben auszeichnet.", + "Message the model... \"%1\" for commands": "Nachricht an das Modell... \"%1\" für Befehle", + "Translation goes here...": "Übersetzung kommt hier hin...", + "When enabled keeps the content of the right sidebar loaded to reduce the delay when opening,\nat the cost of around 15MB of consistent RAM usage. Delay significance depends on your system's performance.\nUsing a custom kernel like linux-cachyos might help": "Wenn aktiviert, hält den Inhalt der rechten Seitenleiste geladen, um die Verzögerung beim Öffnen zu reduzieren,\nzum Preis von etwa 15 MB konstantem RAM-Verbrauch. Die Bedeutung der Verzögerung hängt von der Leistung deines Systems ab.\nEin benutzerdefiniertes Kernel wie linux-cachyos könnte helfen", + "For desktop wallpapers | Good quality": "Für Desktop-Hintergrundbilder | Gute Qualität", + "🔴 Focus: %1 minutes": "🔴 Fokus: %1 Minuten", + "The current system prompt is\n\n---\n\n%1": "Der aktuelle System-Prompt ist\n\n---\n\n%1", + "About": "Über", + "Quick": "Schnell", + "General": "Allgemein", + "UV Index": "UV-Index", + "Force dark mode in terminal": "Dunklen Modus im Terminal erzwingen", + "Drag or click a region • LMB: Copy • RMB: Edit": "Region ziehen oder klicken • LMB: Kopieren • RMB: Bearbeiten", + "%1 characters": "%1 Zeichen", + "Cloudflare WARP": "Cloudflare WARP", + "**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key": "**Preis**: kostenlos. Daten werden für Training verwendet.\n\n**Anleitung**: Bei Google-Konto anmelden, AI Studio erlauben, Google Cloud-Projekt zu erstellen oder was auch immer es fragt, zurückgehen und auf API-Schlüssel abrufen klicken", + "Monochrome": "Monochrom", + "Details": "Details", + "Issues": "Probleme", + "Keyboard toggle": "Tastatur umschalten", + "Might look ass. Unsupported.": "Könnte schlecht aussehen. Nicht unterstützt.", + "Download": "Herunterladen", + "%1 does not require an API key": "%1 erfordert keinen API-Schlüssel", + "Style & wallpaper": "Stil & Hintergrundbild", + "Second precision": "Sekunden-Präzision", + "Group style": "Gruppen-Stil", + "Break": "Pause", + "Run": "Ausführen", + "Enjoy! You can reopen the welcome app any time with Super+Shift+Alt+/. To open the settings app, hit Super+I": "Viel Spaß! Du kannst die Willkommens-App jederzeit mit Super+Shift+Alt+/ wieder öffnen. Um die Einstellungs-App zu öffnen, drücke Super+I", + "Interface Language": "Schnittstellen-Sprache", + "Game mode": "Spielmodus", + "Usage: %1save CHAT_NAME": "Verwendung: %1save CHAT_NAME", + "Thin": "Dünn", + "Light": "Hell", + "When not fullscreen": "Wenn nicht Vollbild", + "Commands, edit configs, search.\nTakes an extra turn to switch to search mode if that's needed": "Befehle, Konfigurationen bearbeiten, suchen.\nBenötigt einen zusätzlichen Zug, um in den Suchmodus zu wechseln, falls nötig", + "Privacy Policy": "Datenschutzerklärung", + "Timeout (ms)": "Timeout (ms)", + "Allow NSFW content": "NSFW-Inhalt erlauben", + "Edit": "Bearbeiten", + "Digits in the middle": "Ziffern in der Mitte", + "Online | Google's model\nA Gemini 2.5 Flash model optimized for cost-efficiency and high throughput.": "Online | Googles Modell\nEin Gemini 2.5 Flash-Modell, optimiert für Kosteneffizienz und hohen Durchsatz.", + "Weather Service": "Wetterdienst", + "Background": "Hintergrund", + "Pick random from this folder": "Zufällig aus diesem Ordner wählen", + "Pressure": "Druck", + "Save": "Speichern", + "Run command": "Befehl ausführen", + "Time to empty:": "Zeit bis leer:", + "Place at bottom": "Unten platzieren", + "Switched to search mode. Continue with the user's request.": "Zum Suchmodus gewechselt. Mit der Anfrage des Benutzers fortfahren.", + "Performance Profile toggle": "Leistungsprofil umschalten", + "Sidebars": "Seitenleisten", + "Usage: %1load CHAT_NAME": "Verwendung: %1load CHAT_NAME", + "Auto styling with Gemini": "Automatisches Styling mit Gemini", + "Simple digital": "Einfach digital", + "No API key set for %1": "Kein API-Schlüssel gesetzt für %1", + "Enter tags, or \"%1\" for commands": "Tags eingeben oder \"%1\" für Befehle", + "%1 queries pending": "%1 Abfragen ausstehend", + "Discussions": "Diskussionen", + "Tray": "Tray", + "Numbers": "Zahlen", + "Intelligence": "Intelligenz", + "Open network portal": "Netzwerkportal öffnen", + "No further instruction provided": "Keine weiteren Anweisungen bereitgestellt", + "Language not listed or incomplete translations?\nYou can choose to generate translations for it with Gemini.\n1. Open the left sidebar with Super+A, set model to Gemini (if it isn't already)\n2. Type /key, hit Enter and follow the instructions\n3. Type /key YOUR_API_KEY\n4. Type the locale of your language below and press Generate": "Sprache nicht aufgeführt oder unvollständige Übersetzungen?\nDu kannst wählen, Übersetzungen dafür mit Gemini zu generieren.\n1. Öffne die linke Seitenleiste mit Super+A, setze Modell auf Gemini (falls noch nicht)\n2. Tippe /key, drücke Enter und folge den Anweisungen\n3. Tippe /key DEIN_API_SCHLUESSEL\n4. Tippe den Locale-Code deiner Sprache unten ein und drücke Generieren", + "Locale code, e.g. fr_FR, de_DE, zh_CN...": "Locale-Code, z.B. fr_FR, de_DE, zh_CN...", + "Select language": "Sprache auswählen", + "Generate translation with Gemini": "Übersetzung mit Gemini generieren", + "Generating...\nDon't close this window!": "Generiere...\nSchließe dieses Fenster nicht!", + "Generate\nTypically takes 2 minutes": "Generieren\nDauert typischerweise 2 Minuten", + "Use system file picker": "System-Dateiauswahl verwenden", + "Wallpaper selector": "Hintergrundbild-Auswahl", + "but force at absolute corner": "aber erzwinge an absoluter Ecke", + "When the previous option is off and this is on,\nyou can still hover the corner's end to open sidebar,\nand the remaining area can be used for volume/brightness scroll": "Wenn die vorherige Option aus und diese an ist,\nkannst du immer noch über das Ende der Ecke bewegen, um die Seitenleiste zu öffnen,\nund der verbleibende Bereich kann für Lautstärke/Helligkeit-Scrollen verwendet werden", + "Copy path": "Pfad kopieren", + "Windows": "Fenster", + "Regenerate": "Neu generieren", + "Microphone": "Mikrofon", + "Unmuted": "Nicht stumm", + "System sound": "System-Sound", + "Enable now": "Jetzt aktivieren", + "Night Light": "Nachtlicht", + "Show aim lines": "Ziellinien anzeigen", + "Why this is cool:\nFor non-0 values, it won't trigger when you reach the\nscreen corner along the horizontal edge, but it will when\nyou do along the vertical edge": "Warum das cool ist:\nFür Werte ungleich 0 wird es nicht ausgelöst, wenn du die\nBildschirmecke entlang der horizontalen Kante erreichst, aber es wird ausgelöst, wenn\ndu es entlang der vertikalen Kante tust", + "Please charge!\nAutomatic suspend triggers at %1%": "Bitte aufladen!\nAutomatische Suspendierung bei %1%", + "Example use case: eroge on one workspace, dark Discord window on another": "Beispiel-Anwendungsfall: Eroge auf einem Arbeitsbereich, dunkles Discord-Fenster auf einem anderen", + "Couldn't recognize music": "Musik konnte nicht erkannt werden", + "Automatic": "Automatisch", + "Hint target regions": "Zielregionen-Hinweise", + "Devices": "Geräte", + "Eye protection": "Augenschutz", + "Japanese": "Japanisch", + "Layers": "Ebenen", + "Listening...": "Hören...", + "LMB to enable/disable\nRMB to toggle size\nScroll to swap position": "LMB zum Aktivieren/Deaktivieren\nRMB zum Umschalten der Größe\nScrollen zum Tauschen der Position", + "Identify Music": "Musik erkennen", + "Quick toggles": "Schnellumschalter", + "Hide sussy/anime wallpapers": "Verdächtige/Anime-Hintergrundbilder ausblenden", + "Android": "Android", + "Show": "Anzeigen", + "Muted": "Stumm", + "Audio input | Right-click for volume mixer & device selector": "Audio-Eingabe | Rechtsklick für Lautstärkemixer & Geräteauswahl", + "Region selector (screen snipping/Google Lens)": "Regionsauswahl (Bildschirmausschnitt/Google Lens)", + "Total duration timeout (s)": "Gesamtdauer-Timeout (s)", + "Music Recognition": "Musikerkennung", + "Night Light | Right-click to configure": "Nachtlicht | Rechtsklick zum Konfigurieren", + "Anti-flashbang (experimental)": "Anti-Flashbang (experimentell)", + "Digital clock settings": "Digitale Uhr-Einstellungen", + "Could be images or parts of the screen that have some containment.\nMight not always be accurate.\nThis is done with an image processing algorithm run locally and no AI is used.": "Könnten Bilder oder Teile des Bildschirms sein, die eine gewisse Eingrenzung haben.\nMöglicherweise nicht immer genau.\nDies wird mit einem lokal ausgeführten Bildverarbeitungsalgorithmus durchgeführt und keine KI wird verwendet.", + "Polling interval (m)": "Abfrageintervall (m)", + "Inactive": "Inaktiv", + "Authentication": "Authentifizierung", + "Full warning": "Volle Warnung", + "Power Profile": "Energieprofil", + "Content region": "Inhaltsregion", + "Internet": "Internet", + "Record": "Aufnehmen", + "Circle selection": "Kreisauswahl", + "Edit quick toggles": "Schnellumschalter bearbeiten", + "Virtual Keyboard": "Virtuelle Tastatur", + "Music Recognized": "Musik erkannt", + "EasyEffects": "EasyEffects", + "Make sure you have songrec installed": "Stelle sicher, dass songrec installiert ist", + "Dark Mode": "Dunkler Modus", + "No device": "Kein Gerät", + "Animate time change": "Zeitänderung animieren", + "It may take a few seconds to update": "Es kann einige Sekunden dauern, bis es aktualisiert wird", + "Polling interval (s)": "Abfrageintervall (s)", + "Perhaps what you're listening to is too niche": "Vielleicht ist das, was du hörst, zu speziell", + "Fahrenheit unit": "Fahrenheit-Einheit", + "Sliders": "Schieberegler", + "Roman": "Römisch", + "Number style": "Zahlen-Stil", + "Intensity": "Intensität", + "Google Lens": "Google Lens", + "Circle": "Kreis", + "Hide clipboard images copied from sussy sources": "Zwischenablage-Bilder von verdächtigen Quellen ausblenden", + "Scroll to Bottom": "Nach unten scrollen", + "Enabled": "Aktiviert", + "Nothing": "Nichts", + "Audio input": "Audio-Eingabe", + "with vertical offset": "mit vertikalem Versatz", + "Padding": "Abstand", + "Please unplug the charger": "Bitte Ladegerät abziehen", + "Show notifications": "Benachrichtigungen anzeigen", + "Path copied": "Pfad kopiert", + "On-screen keyboard": "Bildschirmtastatur", + "City name": "Stadtname", + "Click to cycle through power profiles": "Klicken zum Durchwechseln der Energieprofile", + "Recognize music | Right-click to toggle source": "Musik erkennen | Rechtsklick zum Umschalten der Quelle", + "Use old sine wave cookie implementation": "Alte Sinuswellen-Cookie-Implementierung verwenden", + "Rectangular selection": "Rechteckauswahl", + "Audio output": "Audio-Ausgabe", + "Applications": "Anwendungen", + "Circle to Search": "Kreis zum Suchen", + "Audio output | Right-click for volume mixer & device selector": "Audio-Ausgabe | Rechtsklick für Lautstärkemixer & Geräteauswahl", + "Enable GPS based location": "GPS-basierte Standortbestimmung aktivieren", + "You'll need to enter your Gemini API key first.\nType /key on the sidebar for instructions.": "Du musst zuerst deinen Gemini-API-Schlüssel eingeben.\nTippe /key in der Seitenleiste für Anweisungen.", + "Sounds": "Sounds", + "Active": "Aktiv", + "Keep awake": "Wach halten", + "Auto,": "Auto,", + "Normal": "Normal", + "Force hover open at absolute corner": "Hover-Öffnen an absoluter Ecke erzwingen", + "Open the shell config file\nAlternatively right-click to copy path": "Shell-Konfigurationsdatei öffnen\nAlternativ Rechtsklick zum Kopieren des Pfads", + "Recognize music": "Musik erkennen", + "Stroke width": "Strichstärke", + "Use varying shapes for password characters": "Verschiedene Formen für Passwort-Zeichen verwenden", + "Battery full": "Batterie voll" +} diff --git a/dots/.config/quickshell/ii/translations/id_ID.json b/dots/.config/quickshell/ii/translations/id_ID.json new file mode 100644 index 000000000..f36409b1f --- /dev/null +++ b/dots/.config/quickshell/ii/translations/id_ID.json @@ -0,0 +1,492 @@ +{ + "Material cookie": "Material cookie", + "Style: Blurred": "Gaya: Buram", + "Unknown device": "Perangkat tidak dikenal", + "Change any time later with /dark, /light, /wallpaper in the launcher\nIf the shell's colors aren't changing:\n 1. Open the right sidebar with Super+N\n 2. Click \"Reload Hyprland & Quickshell\" in the top-right corner": "Ubah kapan saja dengan /dark, /light, /wallpaper di peluncur\nJika warna shell tidak berubah:\n 1. Buka sidebar kanan dengan Super+N\n 2. Klik \"Muat Ulang Hyprland & Quickshell\" di pojok kanan atas", + "No pending tasks": "Tidak ada tugas tertunda", + "Positioning": "Posisi", + "Set temperature (randomness) of the model. Values range between 0 to 2 for Gemini, 0 to 1 for other models. Default is 0.5.": "Atur temperatur (keacakan) model. Nilai berkisar antara 0 hingga 2 untuk Gemini, 0 hingga 1 untuk model lain. Bawaan adalah 0.5.", + "Critical warning": "Peringatan kritis", + "Unknown Artist": "Artis tidak dikenal", + "Web search": "Pencarian web", + "Load prompt from %1": "Muat prompt dari %1", + "Attach a file. Only works with Gemini.": "Lampirkan file. Hanya berfungsi dengan Gemini.", + "Reboot": "Mulai ulang", + "API key:\n\n```txt\n%1\n```": "Kunci API:\n\n```txt\n%1\n```", + "Pinned on startup": "Disematkan saat startup", + "Right": "Kanan", + "Reboot to firmware settings": "Mulai ulang ke pengaturan firmware", + "Automatically hide": "Sembunyikan otomatis", + "Waiting for response...": "Menunggu respons...", + "To Do": "Tugas", + "Full": "Penuh", + "Select Language": "Pilih Bahasa", + "Password": "Kata sandi", + "Bluetooth devices": "Perangkat Bluetooth", + "Enable": "Aktifkan", + "Elements": "Elemen", + "Start": "Mulai", + "Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers": "Wallpaper Anime SFW acak dari Konachan\nGambar disimpan di ~/Pictures/Wallpapers", + "The popular one | Best quantity, but quality can vary wildly": "Paling populer | Kuantitas tinggi, kualitas bisa sangat bervariasi", + "System uptime:": "Waktu aktif sistem:", + "illogical-impulse Welcome": "Selamat datang di illogical-impulse", + "Code saved to file": "Kode disimpan ke file", + "Info": "Info", + "Preferred wallpaper zoom (%)": "Preferensi perbesaran wallpaper (%)", + "Time": "Waktu", + "Help & Support": "Bantuan & Dukungan", + "Bubble": "Gelembung", + "Large images | God tier quality, no NSFW.": "Gambar besar | Kualitas terbaik, tanpa NSFW.", + "Dark": "Gelap", + "Center clock": "Jam tengah", + "Search, calculate or run": "Cari, hitung atau jalankan", + "Region height": "Tinggi wilayah", + "Load chat": "Muat obrolan", + "Gives the model search capabilities (immediately)": "Memberikan kemampuan pencarian pada model (langsung)", + "Depends on workspace": "Bergantung pada ruang kerja", + "Blurred style": "Gaya buram", + "Screenshot tool": "Alat tangkapan layar", + "Enter password": "Masukkan kata sandi", + "Search the web": "Cari di web", + "Local only": "Lokal saja", + "at": "di", + "Math": "Matematika", + "Consider plugging in your device": "Pertimbangkan untuk mengisi ulang daya perangkat Anda", + "Workspaces shown": "Ruang kerja yang ditampilkan", + "Place the corners to trigger at the bottom": "Tempatkan sudut pemicu di bawah", + "No API key\nSet it with /key YOUR_API_KEY": "Tidak ada kunci API\nAtur dengan /key KUNCI_API_ANDA", + "Auto (System)": "Otomatis (Sistem)", + "Arrow keys to navigate, Enter to select\nEsc or click anywhere to cancel": "Tombol panah untuk navigasi, Enter untuk memilih\nEsc atau klik di mana saja untuk membatalkan", + "Critically low battery": "Baterai sangat rendah", + "Open editor": "Buka editor", + "%1 notifications": "%1 notifikasi", + "Region width": "Lebar wilayah", + "Max allowed increase": "Peningkatan maksimal yang diizinkan", + "Enable translator": "Aktifkan penerjemah", + "Constantly rotate": "Putar terus-menerus", + "Automatically suspends the system when battery is low": "Menangguhkan sistem secara otomatis ketika baterai rendah", + "Cannot find a GPS service. Using the fallback method instead.": "Tidak dapat menemukan layanan GPS. Menggunakan metode cadangan.", + "Qt apps": "Aplikasi Qt", + "Color picker": "Pemilih warna", + "Interface": "Antarmuka", + "Tint app icons": "Warnai ikon aplikasi", + "Select the language for the user interface.\n\"Auto\" will use your system's locale.": "Pilih bahasa untuk antarmuka pengguna.\n\"Otomatis\" akan menggunakan pengaturan lokal sistem Anda.", + "Show quote": "Tampilkan kutipan", + "Local Ollama model | %1": "Model Ollama lokal | %1", + "Show clock": "Tampilkan jam", + "Usage: %1superpaste NUM_OF_ENTRIES[i]\nSupply i when you want images\nExamples:\n%1superpaste 4i for the last 4 images\n%1superpaste 7 for the last 7 entries": "Penggunaan: %1superpaste JUMLAH_ENTRI[i]\nGunakan i jika Anda menginginkan gambar\nContoh:\n%1superpaste 4i untuk 4 gambar terakhir\n%1superpaste 7 untuk 7 entri terakhir", + "Audio": "Audio", + "Corner style": "Gaya sudut", + "No media": "Tidak ada media", + "Unknown function call: %1": "Panggilan fungsi tidak dikenal: %1", + "Online | %1's model | Delivers fast, responsive and well-formatted answers. Disadvantages: not very eager to do stuff; might make up unknown function calls": "Online | Model %1 | Memberikan jawaban cepat, responsif dan terformat dengan baik. Kekurangan: tidak terlalu bersemangat melakukan hal-hal; mungkin membuat panggilan fungsi yang tidak dikenal", + "Volume": "Volume", + "Medium": "Sedang", + "Copy code": "Salin kode", + "Exceeded max allowed": "Melebihi batas maksimal", + "Keep right sidebar loaded": "Pertahankan sidebar kanan dimuat", + "Left": "Kiri", + "High": "Tinggi", + "Rect": "Persegi", + "Lap": "Putaran", + "Clear": "Hapus", + "Screen snip": "Cuplikan layar", + "Reset": "Atur ulang", + "Back": "Kembali", + "Dark/Light toggle": "Toggle Gelap/Terang", + "12h am/pm": "12j am/pm", + "Download complete": "Unduhan selesai", + "Enable blur": "Aktifkan blur", + "Second hand": "Jarum detik", + "Bar & screen": "Bilah & layar", + "Discharging:": "Menguras:", + "Up %1": "Naik %1", + "Low": "Rendah", + "Hour hand": "Jarum jam", + "Clear chat history": "Hapus riwayat obrolan", + "Fruit Salad": "Salad Buah", + "%1 Safe Storage": "Penyimpanan Aman %1", + "Hibernate": "Hibernasi", + "Delete": "Hapus", + "OK": "OK", + "Settings": "Pengaturan", + "This is usually safe and needed for your browser and AI sidebar anyway\nMostly useful for those who use lock on startup instead of a display manager that does it (GDM, SDDM, etc.)": "Ini biasanya aman dan diperlukan untuk browser dan sidebar AI Anda\nSebagian besar berguna bagi mereka yang menggunakan kunci saat startup daripada display manager yang melakukannya (GDM, SDDM, dll.)", + "Use Hyprlock (instead of Quickshell)": "Gunakan Hyprlock (menggantikan Quickshell)", + "Crosshair code (in Valorant's format)": "Kode Crosshair (dalam format Valorant)", + "Silent": "Sunyi", + "Useless buttons": "Tombol tidak berguna", + "Hover to reveal": "Arahkan untuk menampilkan", + "Wallpaper & Colors": "Wallpaper & Warna", + "Auto": "Otomatis", + "Visibility": "Visibilitas", + "Shell & utilities": "Shell & utilitas", + "Hollow": "Berongga", + "illogical-impulse": "illogical-impulse", + "Use the system file picker instead\nRight-click to make this the default behavior": "Gunakan pemilih file sistem\nKlik kanan untuk menjadikan ini perilaku bawaan", + "On-screen display": "Tampilan di layar", + "Dotfiles": "Dotfiles", + "Search wallpapers": "Cari wallpaper", + "Mic toggle": "Toggle mikrofon", + "Input": "Input", + "Also unlock keyring": "Juga buka kunci keyring", + "Configuration": "Konfigurasi", + "Keep system awake": "Jaga sistem tetap terjaga", + "Unknown command:": "Perintah tidak dikenal:", + "Anime boorus": "Booru anime", + "To Do:": "Tugas:", + "Uses Gemini to categorize the wallpaper then picks a preset based on it.\nYou'll need to set Gemini API key on the left sidebar first.\nImages are downscaled for performance, but just to be safe,\ndo not select wallpapers with sensitive information.": "Menggunakan Gemini untuk mengkategorikan wallpaper lalu memilih preset yang sesuai berdasarkan kategori.\nAnda perlu mengatur kunci API Gemini di sidebar kiri terlebih dahulu.\nGambar diperkecil untuk performa, tetapi agar tetap aman,\njangan pilih wallpaper dengan informasi sensitif.", + "Bottom": "Bawah", + "Clear the current list of images": "Hapus daftar gambar saat ini", + "Sunrise": "Matahari terbit", + "Show app icons": "Tampilkan ikon aplikasi", + "Format": "Format", + "Make sure your player has MPRIS support\nor try turning off duplicate player filtering": "Pastikan pemutar Anda memiliki dukungan MPRIS\natau coba matikan penyaringan pemutar duplikat", + "Pause": "Jeda", + "Desktop": "Desktop", + "Conflicts with the shell's system tray implementation": "Konflik dengan implementasi system tray shell", + "Your package manager is running": "Manajer paket Anda sedang berjalan", + "Conflicts with the shell's notification implementation": "Konflik dengan implementasi notifikasi shell", + "Unknown Album": "Album Tidak Dikenal", + "Pick wallpaper image on your system": "Pilih gambar wallpaper di sistem Anda", + "Used:": "Digunakan:", + "Cheat sheet": "Lembar contekan", + "Clock style": "Gaya jam", + "No audio source": "Tidak ada sumber audio", + "Paired": "Dipasangkan", + "Documentation": "Dokumentasi", + "No": "Tidak", + "Pills": "Pil", + "Thought": "Pemikiran", + "When this is off you'll have to click": "Ketika ini dimatikan Anda harus mengklik", + "Select output device": "Pilih perangkat output", + "Logout": "Keluar", + "Tip: Close a window with Super+Q": "Tip: Tutup jendela dengan Super+Q", + "Finished tasks will go here": "Tugas terselesaikan akan ada di sini", + "Terminal: Harmony (%)": "Terminal: Harmoni (%)", + "Corner open": "Sudut pembuka", + "Shell conflicts killer": "Pemecah konflik shell", + "Clean stuff | Excellent quality, no NSFW": "Konten bersih | Kualitas sangat baik, tanpa NSFW", + "Scroll to change volume": "Gulir untuk mengubah volume", + "Wind": "Angin", + "API key is set\nChange with /key YOUR_API_KEY": "Kunci API sudah diatur\nUbah dengan /key KUNCI_API_ANDA", + "Neutral": "Netral", + "12h AM/PM": "12j AM/PM", + "Number show delay when pressing Super (ms)": "Tunda tampilan angka saat menekan Super (ms)", + "Fill": "Isi", + "Always show numbers": "Selalu tampilkan angka", + "Dot": "Titik", + "Provider set to": "Penyedia diatur ke", + "Unknown Title": "Judul tidak dikenal", + "Anime": "Anime", + "Refreshing (manually triggered)": "Menyegarkan (dipicu secara manual)", + "Dock": "Dok", + "Require password to power off/restart": "Memerlukan kata sandi untuk mematikan/mulai ulang", + "Line": "Garis", + "Weather": "Cuaca", + "All-rounder | Good quality, decent quantity": "Serbaguna | Kualitas bagus, kuantitas lumayan", + "Scale (%)": "Skala (%)", + "Copy": "Salin", + "Usage": "Penggunaan", + "Type /key to get started with online models\nCtrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window": "Ketik /key untuk memulai dengan model online\nCtrl+O untuk memperluas sidebar\nCtrl+P untuk melepas sidebar ke jendela", + "Set the tool to use for the model.": "Atur alat untuk digunakan oleh model.", + "Disable tools": "Nonaktifkan alat", + "Connect": "Hubungkan", + "Allow NSFW": "Izinkan NSFW", + "Registration failed. Please inspect manually with the warp-cli command": "Pendaftaran gagal. Silakan periksa secara manual dengan perintah warp-cli", + "Time to full:": "Waktu hingga penuh:", + "Session": "Sesi", + "Services": "Layanan", + "Nothing here!": "Di sini kosong!", + "Overview": "Ikhtisar", + "Random: osu! seasonal": "Acak: osu! musiman", + "If you want to somehow use fingerprint unlock...": "Jika Anda ingin menggunakan buka kunci sidik jari...", + "Minute hand": "Jarum menit", + "Notifications": "Notifikasi", + "Enable if you want clocks to show seconds accurately": "Aktifkan jika Anda ingin jam menampilkan detik dengan akurat", + "Timer": "Timer", + "Quote settings": "Pengaturan kutipan", + "System prompt": "Prompt sistem", + "Classic": "Klasik", + "Close": "Tutup", + "Disconnect": "Putuskan", + "Go to source (%1)": "Pergi ke sumber (%1)", + "EasyEffects | Right-click to configure": "EasyEffects | Klik kanan untuk mengonfigurasi", + "Forget": "Lupakan", + "Output": "Output", + "Date style": "Gaya tanggal", + "System": "Sistem", + "Usage: %1tool TOOL_NAME": "Penggunaan: %1tool NAMA_ALAT", + "Workspaces": "Ruang kerja", + "Calendar": "Kalender", + "**Instructions**: Log into Mistral account, go to Keys on the sidebar, click Create new key": "**Instruksi**: Masuk ke akun Mistral, buka Keys di sidebar, klik Create new key", + "Volume limit": "Batas volume", + "Sunset": "Matahari terbenam", + "Dial style": "Gaya dial", + "Hi there! First things first...": "Hai! Pertama-tama...", + "Save chat to %1": "Simpan obrolan ke %1", + "Security": "Keamanan", + "Total token count\nInput: %1\nOutput: %2": "Total jumlah token\nInput: %1\nOutput: %2", + "Cancel wallpaper selection": "Batalkan pemilihan wallpaper", + "Please charge!\nAutomatic suspend triggers at %1": "Silakan isi ulang daya!\nPenangguhan otomatis terpicu di %1", + "Terminal: Harmonize threshold": "Terminal: Ambang harmonisasi", + "Be patient...": "Harap bersabar...", + "Utility buttons": "Tombol utilitas", + "Tonal Spot": "Titik Tonal", + "Prevents abrupt increments and restricts volume limit": "Mencegah peningkatan mendadak dan membatasi batas volume", + "Set the current API provider": "Atur penyedia API saat ini", + "Connection failed. Please inspect manually with the warp-cli command": "Koneksi gagal. Silakan periksa secara manual dengan perintah warp-cli", + "Networking": "Jaringan", + "Tint icons": "Warnai ikon", + "Low battery": "Baterai rendah", + "Make icons pinned by default": "Jadikan ikon disematkan secara bawaan", + "Get the next page of results": "Dapatkan halaman hasil berikutnya", + "Invalid API provider. Supported: \n-": "Penyedia API tidak valid. Yang didukung: \n-", + "Show \"Locked\" text": "Tampilkan teks \"Terkunci\"", + "**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key": "**Harga**: gratis. Kebijakan penggunaan data bervariasi tergantung pada pengaturan akun OpenRouter Anda.\n\n**Instruksi**: Masuk ke akun OpenRouter, buka Keys di menu kanan atas, klik Create API Key", + "Not visible to model": "Tidak terlihat oleh model", + "Lock screen": "Kunci layar", + "Save to Downloads": "Simpan ke Unduhan", + "Expressive": "Ekspresif", + "Suspend at": "Tangguhkan di", + "Jump to current month": "Lompat ke bulan ini", + "Bold": "Tebal", + "Waifus only | Excellent quality, limited quantity": "Hanya waifu | Kualitas sangat baik, kuantitas terbatas", + "Click to toggle light/dark mode\n(applied when wallpaper is chosen)": "Klik untuk beralih mode terang/gelap\n(diterapkan saat wallpaper dipilih)", + "Visualize region": "Visualisasikan wilayah", + "Quote": "Kutipan", + "Sleep": "Tidur", + "Hit \"/\" to search": "Tekan \"/\" untuk mencari", + "Hug": "Peluk", + "Report a Bug": "Laporkan Bug", + "Precipitation": "Curah hujan", + "Crosshair": "Crosshair", + "Model set to %1": "Model diatur ke %1", + "Rows": "Baris", + "Top": "Atas", + "Long break": "Istirahat panjang", + "Superpaste": "Superpaste", + "Screen round corner": "Lengkungan sudut layar", + "Online | Google's model\nNewer model that's slower than its predecessor but should deliver higher quality answers": "Online | Model Google\nModel lebih baru yang lebih lambat dari pendahulunya tapi memberikan jawaban kualitas lebih tinggi", + "Rainbow": "Pelangi", + "Weeb": "Wibu", + "Large language models": "Model bahasa besar", + "Online models disallowed\n\nControlled by `policies.ai` config option": "Model online tidak diizinkan\n\nDikontrol oleh opsi konfigurasi `policies.ai`", + "Policies": "Kebijakan", + "Temperature must be between 0 and 2": "Temperatur harus antara 0 dan 2", + "Automatic suspend": "Penangguhan otomatis", + "Extra wallpaper zoom (%)": "Zoom wallpaper ekstra (%)", + "GitHub": "GitHub", + "%1 | Right-click to configure": "%1 | Klik kanan untuk mengonfigurasi", + "**Pricing**: Free tier available with limited rates. See https://docs.github.com/en/billing/concepts/product-billing/github-models\n\n**Instructions**: Generate a GitHub personal access token with Models permission, then set as API key here\n\n**Note**: To use this you will have to set the temperature parameter to 1": "**Harga**: Akses gratis tersedia dengan batasan rate. Lihat https://docs.github.com/en/billing/concepts/product-billing/github-models\n\n**Instruksi**: Buat token akses pribadi GitHub dengan izin Models, lalu atur sebagai kunci API di sini\n\n**Catatan**: Untuk menggunakan ini Anda harus mengatur parameter temperatur ke 1", + "Edit directory": "Edit direktori", + "Action": "Aksi", + "Search": "Cari", + "Tip: right-clicking a group\nalso expands it": "Tip: klik kanan grup\njuga memperluasnya", + "Bar": "Bilah", + "Show regions of potential interest": "Tampilkan wilayah yang berpotensi menarik", + "Clipboard": "Papan klip", + "Stopwatch": "Stopwatch", + "Enter text to translate...": "Masukkan teks untuk diterjemahkan...", + "App": "Aplikasi", + "Sides": "Sisi", + "No active player": "Tidak ada pemutar aktif", + "Not all options are available in this app. You should also check the config file by hitting the \"Config file\" button on the topleft corner or opening %1 manually.": "Tidak semua opsi tersedia di aplikasi ini. Anda juga harus memeriksa file konfigurasi dengan menekan tombol \"File konfigurasi\" di pojok kiri atas atau membuka %1 secara manual.", + "There might be a download in progress": "Mungkin ada unduhan yang sedang berlangsung", + "Math result": "Hasil matematika", + "Fidelity": "Fidelitas", + "Prefixes": "Awalan", + "Terminal": "Terminal", + "Incorrect password": "Kata sandi salah", + "Line-separated": "Dipisahkan baris", + "Always": "Selalu", + "☕ Break: %1 minutes": "☕ Istirahat: %1 menit", + "Depends on sidebars": "Bergantung pada sidebar", + "Tool set to: %1": "Alat diatur ke: %1", + "Save chat": "Simpan obrolan", + "Crosshair overlay": "Overlay Crosshair", + "Keybinds": "Pintasan keyboard", + "Launch": "Luncurkan", + "Could be better if you make a ton of typos,\nbut results can be weird and might not work with acronyms\n(e.g. \"GIMP\" might not give you the paint program)": "Hasil bisa lebih baik jika Anda membuat banyak kesalahan ketik,\nnamun hasilnya dapat menjadi aneh dan mungkin tidak bekerja dengan akronim\n(misal: \"GIMP\" mungkin tidak merujuk ke aplikasi pengolah gambar)", + "Choose model": "Pilih model", + "Base URL": "URL Dasar", + "Float": "Mengambang", + "Wallpaper parallax": "Paralaks wallpaper", + "Invalid arguments. Must provide `command`.": "Argumen tidak valid. Harus menyediakan `command`.", + "Fully charged": "Terisi penuh", + "Earbang protection": "Perlindungan suara keras", + "Low warning": "Peringatan rendah", + "Advanced": "Lanjutan", + "Scroll to change brightness": "Gulir untuk mengubah kecerahan", + "Loaded the following system prompt\n\n---\n\n%1": "Memuat prompt sistem berikut\n\n---\n\n%1", + "Show next time": "Tampilkan lain kali", + "Current tool: %1\nSet it with %2tool TOOL": "Alat saat ini: %1\nAtur dengan %2tool ALAT", + "Unread indicator: show count": "Indikator belum dibaca: tampilkan jumlah", + "Press Super+G to toggle appearance": "Tekan Super+G untuk toggle tampilan", + "That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "Itu tidak berhasil. Tips:\n- Periksa tag dan pengaturan NSFW\n- Jika Anda tidak memasukkan tag, masukkan nomor halaman", + "Dots": "Titik", + "Cloudflare WARP (1.1.1.1)": "Cloudflare WARP (1.1.1.1)", + "Volume mixer": "Mixer volume", + "Config file": "File konfigurasi", + "API key set for %1": "Kunci API diatur untuk %1", + "Online via %1 | %2's model": "Online via %1 | Model %2", + "Shell command": "Perintah shell", + "Such regions could be images or parts of the screen that have some containment.\nMight not always be accurate.\nThis is done with an image processing algorithm run locally and no AI is used.": "Wilayah tersebut bisa berupa gambar atau bagian layar yang memiliki kandungan.\nMungkin tidak selalu akurat.\nIni dilakukan dengan algoritma pemrosesan gambar yang berjalan lokal dan tidak ada AI yang digunakan.", + "Reload Hyprland & Quickshell": "Muat Ulang Hyprland & Quickshell", + "Resources": "Sumber daya", + "Brightness": "Kecerahan", + "Unknown": "Tidak dikenal", + "Polling interval (ms)": "Interval polling (ms)", + "Lock": "Kunci", + "Thinking": "Berpikir", + "Approve": "Setujui", + "Unfinished": "Belum selesai", + "Random: Konachan": "Acak: Konachan", + "Connected": "Terhubung", + "Wallpaper safety enforced": "Keamanan wallpaper diberlakukan", + "Invalid arguments. Must provide `key` and `value`.": "Argumen tidak valid. Harus menyediakan `key` dan `value`.", + "24h": "24j", + "Allows you to open sidebars by clicking or hovering screen corners regardless of bar position": "Memungkinkan Anda membuka sidebar dengan mengklik atau mengarahkan ke sudut layar terlepas dari posisi bilah", + "Bar style": "Gaya bilah", + "Load:": "Beban:", + "Open file link": "Buka tautan file", + "Ignored if terminal theming is not enabled": "Diabaikan jika tema terminal tidak diaktifkan", + "Shutdown": "Matikan", + "Hour marks": "Tanda jam", + "Random osu! seasonal background\nImage is saved to ~/Pictures/Wallpapers": "Latar belakang musiman osu! acak\nGambar disimpan ke ~/Pictures/Wallpapers", + "Online | Google's model\nFast, can perform searches for up-to-date information": "Online | Model Google\nCepat, dapat melakukan pencarian untuk informasi terkini", + "Current model: %1\nSet it with %2model MODEL": "Model saat ini: %1\nAtur dengan %2model MODEL", + "Select input device": "Pilih perangkat input", + "Connect to Wi-Fi": "Hubungkan ke Wi-Fi", + "... and %1 more": "... dan %1 lainnya", + "Cookie clock settings": "Pengaturan jam cookie", + "Brightness and volume": "Kecerahan dan volume", + "Choose file": "Pilih file", + "Invalid model. Supported: \n```": "Model tidak valid. Didukung: \n```", + "Task Manager": "Manajer Tugas", + "Charging:": "Mengisi:", + "Illegal increment": "Peningkatan ilegal", + "Total:": "Total:", + "or": "atau", + "Battery": "Baterai", + "Timeout duration (if not defined by notification) (ms)": "Durasi waktu habis (jika tidak ditentukan oleh notifikasi) (ms)", + "Cancel": "Batal", + "Locked": "Terkunci", + "Temperature: %1": "Temperatur: %1", + "Hover to trigger": "Arahkan untuk memicu", + "Command rejected by user": "Perintah ditolak oleh pengguna", + "User agent (for services that require it)": "Agen pengguna (untuk layanan yang memerlukannya)", + "Saved to %1": "Disimpan ke %1", + "Emojis": "Emoji", + "Color generation": "Generasi warna", + "Welcome app": "Aplikasi selamat datang", + "Humidity": "Kelembaban", + "Page %1": "Halaman %1", + "Feels like %1": "Terasa seperti %1", + "Distro": "Distro", + "Transparency": "Transparansi", + "%1 • %2 tasks": "%1 • %2 tugas", + "Markdown test": "Tes Markdown", + "Invalid tool. Supported tools:\n- %1": "Alat tidak valid. Alat yang didukung:\n- %1", + "No notifications": "Tidak ada notifikasi", + "The hentai one | Great quantity, a lot of NSFW, quality varies wildly": "Yang hentai | Kuantitas besar, banyak NSFW, kualitas sangat bervariasi", + "Bluetooth": "Bluetooth", + "Resume": "Lanjutkan", + "Work safety": "Keamanan kerja", + "Temperature\nChange with /temp VALUE": "Temperatur\nUbah dengan /temp NILAI", + "Terminal: Foreground boost (%)": "Terminal: Peningkatan latar depan (%)", + "Night Light | Right-click to toggle Auto mode": "Cahaya Malam | Klik kanan untuk toggle mode Otomatis", + "Closet": "Lemari", + "Yes": "Ya", + "Columns": "Kolom", + "To set an API key, pass it with the %4 command\n\nTo view the key, pass \"get\" with the command
\n\n### For %1:\n\n**Link**: %2\n\n%3": "Untuk mengatur kunci API, gunakan perintah %4\n\nUntuk melihat kunci, gunakan \"get\" dengan perintah
\n\n### Untuk %1:\n\n**Tautan**: %2\n\n%3", + "Kill conflicting programs?": "Matikan program yang berkonflik?", + "For storing API keys and other sensitive information": "Untuk menyimpan kunci API dan informasi sensitif lainnya", + "Reject": "Tolak", + "Set API key": "Atur kunci API", + ". Notes for Zerochan:\n- You must enter a color\n- Set your zerochan username in `sidebar.booru.zerochan.username` config option. You [might be banned for not doing so](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!": ". Catatan untuk Zerochan:\n- Anda harus memasukkan warna\n- Atur nama pengguna zerochan Anda di opsi konfigurasi `sidebar.booru.zerochan.username`. [Akses Anda mungkin akan ditangguhkan jika tidak dilakukan](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!", + "Content": "Konten", + "Pomodoro": "Pomodoro", + "Vertical": "Vertikal", + "Pick a wallpaper": "Pilih wallpaper", + "Load chat from %1": "Muat obrolan dari %1", + "Launch on startup": "Luncurkan saat startup", + "Add": "Tambah", + "Style: general": "Gaya: umum", + "Use Levenshtein distance-based algorithm instead of fuzzy": "Gunakan algoritma berbasis jarak Levenshtein daripada fuzzy", + "Shell & utilities theming must also be enabled": "Tema shell & utilitas juga harus diaktifkan", + "Workspace": "Ruang kerja", + "Translator": "Penerjemah", + "Free:": "Gratis:", + "🌿 Long break: %1 minutes": "🌿 Istirahat panjang: %1 menit", + "Value scroll": "Gulir nilai", + "Bar position": "Posisi bilah", + "Language": "Bahasa", + "Current API endpoint: %1\nSet it with %2mode PROVIDER": "Endpoint API saat ini: %1\nAtur dengan %2mode PENYEDIA", + "Remember that on most devices one can always hold the power button to force shutdown\nThis only makes it a tiny bit harder for accidents to happen": "Ingat bahwa di sebagian besar perangkat Anda selalu dapat menahan tombol daya untuk memaksa mematikan\nIni hanya membuat sedikit lebih sulit untuk kecelakaan terjadi", + "AI": "AI", + "Task description": "Deskripsi tugas", + "Add task": "Tambah tugas", + "Donate": "Donasi", + "Disable NSFW content": "Nonaktifkan konten NSFW", + "Set the system prompt for the model.": "Atur prompt sistem untuk model.", + "Done": "Selesai", + "Focus": "Fokus", + "Open the shell config file.\nIf the button doesn't work or doesn't open in your favorite editor,\nyou can manually open ~/.config/illogical-impulse/config.json": "Buka file konfigurasi shell.\nJika tombol tidak berfungsi atau tidak terbuka di editor favorit Anda,\nAnda dapat membuka ~/.config/illogical-impulse/config.json secara manual", + "View Markdown source": "Lihat sumber Markdown", + "Border": "Bingkai", + "Temperature set to %1": "Temperatur diatur ke %1", + "Online | Google's model\nGoogle's state-of-the-art multipurpose model that excels at coding and complex reasoning tasks.": "Online | Model Google\nModel serbaguna canggih Google yang unggul dalam tugas coding dan penalaran kompleks.", + "Message the model... \"%1\" for commands": "Kirim pesan ke model... \"%1\" untuk perintah", + "Translation goes here...": "Terjemahan ada di sini...", + "When enabled keeps the content of the right sidebar loaded to reduce the delay when opening,\nat the cost of around 15MB of consistent RAM usage. Delay significance depends on your system's performance.\nUsing a custom kernel like linux-cachyos might help": "Ketika diaktifkan membuat konten sidebar kanan tetap dimuat untuk mengurangi keterlambatan saat membuka,\ndengan penambahan penggunaan RAM konsisten sebanyak 15 MB. Signifikansi keterlambatan tergantung pada kinerja sistem Anda.\nMenggunakan kernel kustom seperti linux-cachyos mungkin dapat membantu", + "For desktop wallpapers | Good quality": "Untuk wallpaper desktop | Kualitas bagus", + "🔴 Focus: %1 minutes": "🔴 Fokus: %1 menit", + "The current system prompt is\n\n---\n\n%1": "Prompt sistem saat ini adalah\n\n---\n\n%1", + "About": "Tentang", + "Quick": "Cepat", + "General": "Umum", + "UV Index": "Indeks UV", + "Force dark mode in terminal": "Paksa mode gelap di terminal", + "Drag or click a region • LMB: Copy • RMB: Edit": "Seret atau klik wilayah • Klik Kiri: Salin • Klik Kanan: Edit", + "%1 characters": "%1 karakter", + "Cloudflare WARP": "Cloudflare WARP", + "**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key": "**Harga**: gratis. Data digunakan untuk pelatihan model.\n\n**Instruksi**: Masuk ke akun Google, izinkan AI Studio membuat proyek Google Cloud atau apa pun yang diminta, kembali dan klik Get API key", + "Monochrome": "Monokrom", + "Details": "Detail", + "Issues": "Masalah", + "Keyboard toggle": "Toggle keyboard", + "Might look ass. Unsupported.": "Mungkin terlihat buruk. Tidak didukung.", + "Download": "Unduh", + "%1 does not require an API key": "%1 tidak memerlukan kunci API", + "Style & wallpaper": "Gaya & wallpaper", + "Second precision": "Presisi detik", + "Group style": "Gaya grup", + "Break": "Istirahat", + "Run": "Jalankan", + "Enjoy! You can reopen the welcome app any time with Super+Shift+Alt+/. To open the settings app, hit Super+I": "Enjoy! Anda dapat membuka kembali aplikasi selamat datang kapan saja dengan Super+Shift+Alt+/. Untuk membuka aplikasi pengaturan, tekan Super+I", + "Interface Language": "Bahasa Antarmuka", + "Game mode": "Mode game", + "Usage: %1save CHAT_NAME": "Penggunaan: %1save NAMA_OBROLAN", + "Thin": "Tipis", + "Light": "Terang", + "When not fullscreen": "Ketika tidak layar penuh", + "Commands, edit configs, search.\nTakes an extra turn to switch to search mode if that's needed": "Perintah, edit konfigurasi, cari.\nMemerlukan giliran ekstra untuk beralih ke mode pencarian jika diperlukan", + "Privacy Policy": "Kebijakan Privasi", + "Timeout (ms)": "Waktu habis (ms)", + "Allow NSFW content": "Izinkan konten NSFW", + "Edit": "Edit", + "Digits in the middle": "Angka di tengah", + "Online | Google's model\nA Gemini 2.5 Flash model optimized for cost-efficiency and high throughput.": "Online | Model Google\nModel Gemini 2.5 Flash yang dioptimalkan untuk efisiensi biaya dan throughput tinggi.", + "Never": "Tidak pernah", + "Percentage": "Persentase", + "Expressive colors": "Warna ekspresif", + "No workspaces": "Tidak ada ruang kerja", + "Screen corner actions": "Aksi sudut layar", + "Random color generation": "Generasi warna acak", + "Disable session lock": "Nonaktifkan kunci sesi", + "Short break": "Istirahat pendek", + "Colors": "Warna", + "Monitors": "Monitor", + "Fuzzy": "Fuzzy", + "Discord": "Discord" +} diff --git a/dots/.config/quickshell/ii/translations/pt_BR.json b/dots/.config/quickshell/ii/translations/pt_BR.json new file mode 100644 index 000000000..9ce180272 --- /dev/null +++ b/dots/.config/quickshell/ii/translations/pt_BR.json @@ -0,0 +1,737 @@ +{ + "Attach a file. Only works with Gemini.": "Anexar um arquivo. Funciona apenas com Gemini.", + "%1 • %2 tasks": "%1 • %2 tarefas", + "Large language models": "Grandes modelos de linguagem (LLMs)", + "Open network portal": "Fazer login na rede", + "Digits in the middle": "Dígitos no meio", + "Auto,": "Auto,", + "Select Language": "Selecionar Idioma", + "Expressive": "Expressivo", + "🔴 Focus: %1 minutes": "🔴 Foco: %1 minutos", + "%1 | Right-click to configure": "%1 | Botão direito para configurar", + "Delete": "Excluir", + "Audio output": "Saída de áudio", + "Full warning": "Aviso de carga completa", + "Switched to search mode. Continue with the user's request.": "Alternado para modo de busca. Continue com a solicitação do usuário.", + "Cloudflare WARP": "Cloudflare WARP", + "Lock": "Bloquear", + "Apps": "Apps", + "Generating...\nDon't close this window!": "Gerando...\nNão feche esta janela!", + "Please charge!\nAutomatic suspend triggers at %1%": "Por favor carregue!\nSuspensão automática em %1%", + "Font family name (e.g., Google Sans Flex)": "Nome da família da fonte (ex: Google Sans Flex)", + "Loaded the following system prompt\n\n---\n\n%1": "Prompt de sistema carregado\n\n---\n\n%1", + "Adjust the color temperature": "Ajustar temperatura de cor", + "Numbers": "Números", + "Style: Blurred": "Estilo: Desfocado", + "Usage": "Uso", + "Ignored if terminal theming is not enabled": "Ignorado se o tema do terminal não estiver ativado", + "Save to Downloads": "Salvar em Downloads", + "Open file link": "Abrir link do arquivo", + "Do you want to allow this app to make changes to your device?": "Deseja permitir que este app faça alterações no seu dispositivo?", + "Visibility": "Visibilidade", + "Set the tool to use for the model.": "Definir a ferramenta para o modelo usar.", + "Bar position": "Posição da barra", + "Open": "Abrir", + "Mic toggle": "Alternar Microfone", + "Language not listed or incomplete translations?\nYou can choose to generate translations for it with Gemini.\n1. Open the left sidebar with Super+A, set model to Gemini (if it isn't already)\n2. Type /key, hit Enter and follow the instructions\n3. Type /key YOUR_API_KEY\n4. Type the locale of your language below and press Generate": "Idioma não listado ou traduções incompletas?\nVocê pode gerar traduções com o Gemini.\n1. Abra a barra lateral esquerda com Super+A, defina o modelo para Gemini\n2. Digite /key, aperte Enter e siga as instruções\n3. Digite /key SUA_API_KEY\n4. Digite o código do seu idioma abaixo e clique em Gerar", + "Muted": "Mudo", + "Registration failed. Please inspect manually with the warp-cli command": "Falha no registro. Por favor inspecione manualmente com o comando warp-cli", + "API key is set\nChange with /key YOUR_API_KEY": "Chave API definida\nAltere com /key SUA_CHAVE_API", + "Terminal: Harmonize threshold": "Terminal: Limiar de harmonização", + "%1 characters": "%1 caracteres", + "For desktop wallpapers | Good quality": "Para desktop | Boa qualidade", + "🌿 Long break: %1 minutes": "🌿 Pausa longa: %1 minutos", + "Set FPS limit": "Definir limite de FPS", + "+%1 notifications": "+%1 notificações", + "Shell & utilities theming must also be enabled": "Tema do Shell e utilitários também deve estar ativado", + "Content region": "Região do conteúdo", + "Padding": "Espaçamento (Padding)", + "Session": "Sessão", + "Online | Google's model\nPro-level intelligence at the speed and pricing of Flash.": "Online | Modelo do Google\nInteligência nível Pro na velocidade e preço do Flash.", + "Yes": "Sim", + "Listening...": "Ouvindo...", + "Base URL": "URL Base", + "Load:": "Uso:", + "Path copied": "Caminho copiado", + "Show": "Mostrar", + "Donate": "Doar", + "Keyboard toggle": "Alternar Teclado", + "File Explorer": "Explorador de Arquivos", + "Example use case: eroge on one workspace, dark Discord window on another": "Exemplo de uso: eroge em um workspace, Discord escuro em outro", + "Tooltips": "Dicas", + "Internet": "Internet", + "Keybinds": "Atalhos", + "Feels like %1": "Sensação térmica de %1", + "Wi-Fi": "Wi-Fi", + "Used:": "Usado:", + "Used for reading large blocks of text": "Usado para leitura de grandes blocos de texto", + "Use the system file picker instead\nRight-click to make this the default behavior": "Usar o selecionador de arquivos do sistema\nBotão direito para tornar este o padrão", + "Enter password": "Digite a senha", + "**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key": "**Preço**: grátis. Política de dados varia conforme configurações da conta OpenRouter.\n\n**Instruções**: Logue no OpenRouter, vá em Keys no menu superior direito, clique em Create API Key", + "Monospace font": "Fonte Monoespaçada", + "Positioning": "Posicionamento", + "Search, calculate or run": "Pesquisar, calcular ou executar", + "Message the model... \"%1\" for commands": "Mensagem para o modelo... \"%1\" para comandos", + "Focus": "Foco", + "Font width": "Largura da fonte", + "Enable opening zoom animation": "Ativar animação de zoom ao abrir", + "Logout": "Sair (Logout)", + "Jump to current month": "Pular para mês atual", + "Keep awake": "Manter acordado", + "Show next time": "Mostrar na próxima vez", + "Date style": "Estilo da data", + "GitHub": "GitHub", + "Saved": "Salvo", + "Bottom-up": "De baixo para cima", + "Disable tools": "Desativar ferramentas", + "Copy code": "Copiar código", + "Low warning": "Aviso de bateria fraca", + "Saving...": "Salvando...", + "Interface": "Interface", + "Normal": "Normal", + "Hug": "Arredondado", + "Tint icons": "Colorir ícones", + "Turn on from sunset to sunrise": "Ligar do pôr do sol ao nascer do sol", + "Kill conflicting programs?": "Matar programas conflitantes?", + "Model set to %1": "Modelo definido para %1", + "Desktop %1": "Área de Trabalho %1", + "Waifus only | Excellent quality, limited quantity": "Apenas Waifus | Excelente qualidade, quantidade limitada", + "Google Lens": "Google Lens", + "Battery: %1%2": "Bateria: %1%2", + "Calendar": "Calendário", + "24h": "24h", + "Why this is cool:\nFor non-0 values, it won't trigger when you reach the\nscreen corner along the horizontal edge, but it will when\nyou do along the vertical edge": "Por que isso é legal:\nPara valores não-0, não acionará quando você atingir o\ncanto da tela pela borda horizontal, mas acionará quando\nvocê for pela borda vertical", + "Disable NSFW content": "Desativar conteúdo NSFW", + "Allow NSFW": "Permitir NSFW", + "Workspaces": "Áreas de trabalho", + "When not fullscreen": "Quando não estiver em tela cheia", + "Run": "Executar", + "Sounds": "Sons", + "LMB to enable/disable\nRMB to toggle size\nScroll to swap position": "Botão Esq. p/ ativar/desativar\nBotão Dir. p/ mudar tamanho\nScroll p/ trocar posição", + "☕ Break: %1 minutes": "☕ Pausa: %1 minutos", + "Enter a valid number": "Digite um número válido", + "Split buttons": "Botões divididos", + "Best match": "Melhor resultado", + "Enable": "Ativar", + "Arrow keys to navigate, Enter to select\nEsc or click anywhere to cancel": "Setas para navegar, Enter para selecionar\nEsc ou clique fora para cancelar", + "Intelligence": "Inteligência", + "Launch on startup": "Iniciar no boot", + "New desktop": "Nova área de trabalho", + "Intensity": "Intensidade", + "Paired": "Pareado", + "Desktop": "Área de Trabalho", + "Task View": "Visão de Tarefas", + "Not visible to model": "Invisível para o modelo", + "App": "App", + "Bar": "Barra", + "Show this window on all desktops": "Mostrar esta janela em todas as áreas", + "Online models disallowed\n\nControlled by `policies.ai` config option": "Modelos online não permitidos\n\nControlado pela opção `policies.ai` na config", + "Hit \"/\" to search": "Aperte \"/\" para buscar", + "Font width and roundness settings are only available for some fonts like Google Sans Flex": "Largura e arredondamento só funcionam em fontes como Google Sans Flex", + "Use symbols for mouse": "Usar símbolos para o mouse", + "End session": "Encerrar sessão", + "... and %1 more": "... e mais %1", + "Locale code, e.g. fr_FR, de_DE, zh_CN...": "Código de localidade, ex: pt_BR, en_US...", + "%1 Safe Storage": "Cofre Seguro do %1", + "Format": "Formato", + "Dots": "Dotfiles", + "There might be a download in progress": "Pode haver um download em andamento", + "Medium": "Médio", + "Math": "Matemática", + "Command-line-invoked Action": "Ação invocada por linha de comando", + "Style: general": "Estilo: geral", + "Time to full:": "Tempo até carga total:", + "Classic": "Clássico", + "Battery full": "Bateria cheia", + "Unread indicator: show count": "Indicador não lido: mostrar contagem", + "Online | %1's model | Delivers fast, responsive and well-formatted answers. Disadvantages: not very eager to do stuff; might make up unknown function calls": "Online | Modelo da %1 | Respostas rápidas e bem formatadas. Desvantagens: não muito proativo; pode inventar chamadas de função inexistentes", + "Workspace": "Workspace", + "Power Profile": "Perfil de Energia", + "Quick toggles": "Botões rápidos", + "Speakers (%1): %2": "Alto-falantes (%1): %2", + "Sunrise": "Nascer do sol", + "Clear chat history": "Limpar histórico do chat", + "Auto styling with Gemini": "Estilo automático com Gemini", + "Center clock": "Relógio centralizado", + "Circle": "Círculo", + "Han chars": "Caracteres Han", + "Roman": "Romano", + "Night Light | Right-click to configure": "Luz Noturna | Botão direito para configurar", + "Sliders": "Deslizadores", + "On-screen display": "Exibição na tela (OSD)", + "Show \"Locked\" text": "Mostrar texto \"Bloqueado\"", + "Unfinished": "Inacabado", + "Hour marks": "Marcas de hora", + "Shut down": "Desligar", + "Be patient...": "Seja paciente...", + "Also unlock keyring": "Também destrancar chaveiro", + "Temperature set to %1": "Temperatura definida para %1", + "Choose model": "Escolher modelo", + "Enter text to translate...": "Digite o texto para traduzir...", + "Finished tasks will go here": "Tarefas finalizadas virão para cá", + "Info": "Info", + "Parallax": "Paralaxe", + "Show date": "Mostrar data", + "Pills": "Cápsulas", + "Font family name": "Nome da família da fonte", + "Online | Google's model\nNewer model that's slower than its predecessor but should deliver higher quality answers": "Online | Modelo do Google\nModelo mais novo, mais lento que o antecessor mas com maior qualidade nas respostas", + "View Markdown source": "Ver código Markdown", + "Stroke width": "Largura do traço", + "Least busy": "Menos ocupado", + "Start": "Início", + "Click to cycle through power profiles": "Clique para alternar perfis de energia", + "Dark Mode": "Modo Escuro", + "Closet": "Closet", + "Prevents abrupt increments and restricts volume limit": "Previne aumentos bruscos e restringe o limite de volume", + "Output device": "Dispositivo de saída", + "Stopwatch": "Cronômetro", + "Local only": "Apenas local", + "Set temperature (randomness) of the model. Values range between 0 to 2 for Gemini, 0 to 1 for other models. Default is 0.5.": "Define a temperatura (aleatoriedade) do modelo. Valores entre 0 a 2 para Gemini, 0 a 1 para outros. Padrão é 0.5.", + "The current system prompt is\n\n---\n\n%1": "O prompt de sistema atual é\n\n---\n\n%1", + "with vertical offset": "com deslocamento vertical", + "Utilities & Tools": "Utilitários & Ferramentas", + "Invalid API provider. Supported: \n-": "Provedor de API inválido. Suportados: \n-", + "Font family name (e.g., Readex Pro)": "Nome da família da fonte (ex: Readex Pro)", + "Emojis": "Emojis", + "Auto (System)": "Auto (Sistema)", + "Cheat sheet": "Atalhos", + "General": "Geral", + "Password": "Senha", + "Config file": "Config file", + "Temperature\nChange with /temp VALUE": "Temperatura\nAltere com /temp VALOR", + "Swap": "Swap", + "Up %1": "Ligado há %1", + "Code saved to file": "Código salvo em arquivo", + "Android": "Android", + "Hint target regions": "Dicar regiões alvo", + "Set API key": "Definir chave API", + "Clear the current list of images": "Limpar lista atual de imagens", + "Night Light": "Luz Noturna", + "Font roundness": "Arredondamento da fonte", + "Style & wallpaper": "Estilo & Papel de parede", + "Nothing": "Nada", + "Snipping area": "Área de recorte", + "Exceeded max allowed": "Máximo permitido excedido", + "Sidebars": "Barras laterais", + "Total token count\nInput: %1\nOutput: %2": "Contagem total de tokens\nEntrada: %1\nSaída: %2", + "Off": "Desligado", + "e.g. 󱊫 for F1, 󱊶 for F12": "ex: 󱊫 para F1, 󱊶 para F12", + "Temperature: %1": "Temperatura: %1", + "Open the shell config file\nAlternatively right-click to copy path": "Abrir arquivo de config do shell\nOu botão direito para copiar caminho", + "Action": "Ação", + "Force hover open at absolute corner": "Forçar canto absoluto", + "Fill": "Preencher", + "illogical-impulse": "illogical-impulse", + "Commands": "Comandos", + "No active player": "Nenhum player ativo", + "Show only when locked": "Mostrar apenas quando bloqueado", + "Usage: %1save CHAT_NAME": "Uso: %1save NOME_DO_CHAT", + "Provider set to": "Provedor definido para", + "Brightness": "Brilho", + "Fully charged": "Carga completa", + "Privacy Policy": "Política de Privacidade", + "Clean stuff | Excellent quality, no NSFW": "Coisas limpas (SFW) | Excelente qualidade, sem NSFW", + "UV Index": "Índice UV", + "No": "Não", + "Manage my account": "Gerenciar minha conta", + "Advanced": "Avançado", + "Emoji": "Emoji", + "Shell command": "Comando Shell", + "Transparency": "Transparência", + "Help & Support": "Ajuda & Suporte", + "More Internet settings": "Mais configurações de Internet", + "Generate\nTypically takes 2 minutes": "Gerar\nGeralmente leva 2 minutos", + "Unknown Application": "Aplicativo Desconhecido", + "Scale (%)": "Escala (%)", + "Go to source (%1)": "Ir para a fonte (%1)", + "You can also manually edit cheatsheet.superKey": "Você também pode editar manualmente cheatsheet.superKey", + "Unknown Title": "Título Desconhecido", + "%1\nInternet access": "%1\nAcesso à internet", + "Enable now": "Ativar agora", + "Creativity": "Criatividade", + "Cookie": "Cookie", + "Font size": "Tamanho da fonte", + "Sides": "Lados", + "Full": "Cheio", + "Used for code and terminal": "Usado para código e terminal", + "Unknown Album": "Álbum Desconhecido", + "Recognize music": "Reconhecer música", + "Your package manager is running": "Seu gerenciador de pacotes está rodando", + "Audio output | Right-click for volume mixer & device selector": "Saída de áudio | Botão direito para mixer & seletor", + "Conflicts with the shell's notification implementation": "Conflita com a implementação de notificação do shell", + "Total duration timeout (s)": "Tempo limite total (s)", + "Reading font": "Fonte de leitura", + "It may take a few seconds to update": "Pode levar alguns segundos para atualizar", + "Force dark mode in terminal": "Forçar modo escuro no terminal", + "Show notifications": "Mostrar notificações", + "Web search": "Busca web", + "%1 mins": "%1 min", + "Left to right": "Esquerda para direita", + "%1 does not require an API key": "%1 não requer chave API", + "System": "Sistema", + "Details": "Detalhes", + "Get the next page of results": "Obter próxima página de resultados", + "12h AM/PM": "12h AM/PM", + "Bluetooth devices": "Dispositivos Bluetooth", + "Dark": "Escuro", + "Neutral": "Neutro", + "Dot": "Ponto", + "Fidelity": "Fidelidade", + "Sunset": "Pôr do sol", + "Other": "Outro", + "Terminal: Harmony (%)": "Terminal: Harmonia (%)", + "Record": "Gravar", + "More volume settings": "Mais configurações de volume", + "Audio input | Right-click for volume mixer & device selector": "Entrada de áudio | Botão direito para mixer & seletor", + "Select the language for the user interface.\n\"Auto\" will use your system's locale.": "Selecione o idioma da interface.\n\"Auto\" usará a localidade do sistema.", + "Dock": "Dock", + "About": "Sobre", + "Disconnect": "Desconectar", + "Widget: Weather": "Widget: Clima", + "Enable GPS based location": "Ativar localização por GPS", + "User agent (for services that require it)": "User agent (para serviços que exigem)", + "All-rounder | Good quality, decent quantity": "Geral | Boa qualidade, quantidade decente", + "Set the system prompt for the model.": "Definir prompt de sistema para o modelo.", + "Output": "Saída", + "Reboot to firmware settings": "Reiniciar na BIOS/Firmware", + "Hollow": "Oco", + "Float": "Flutuante", + "Terminal: Foreground boost (%)": "Terminal: Impulso de primeiro plano (%)", + "Bluetooth": "Bluetooth", + "Authentication": "Autenticação", + "Large images | God tier quality, no NSFW.": "Imagens grandes | Qualidade divina, sem NSFW.", + "Region selector (screen snipping/Google Lens)": "Seletor de região (recorte/Google Lens)", + "Show hidden icons": "Mostrar ícones ocultos", + "Sound effects": "Efeitos sonoros", + "Manage accounts": "Gerenciar contas", + "When this is off you'll have to click": "Quando desligado, você terá que clicar", + "of %1": "de %1", + "Monochrome": "Monocromático", + "Use adaptive alignment": "Usar alinhamento adaptativo", + "Close all windows": "Fechar todas as janelas", + "Gives the model search capabilities (immediately)": "Dá capacidade de busca ao modelo (imediatamente)", + "Not all options are available in this app. You should also check the config file by hitting the \"Config file\" button on the topleft corner or opening %1 manually.": "Nem todas as opções estão neste app. Verifique o arquivo de config clicando no botão \"Arquivo de config\" no canto superior esquerdo ou abrindo %1 manualmente.", + "Volume limit": "Limite de volume", + "Click to toggle light/dark mode\n(applied when wallpaper is chosen)": "Clique para alternar modo claro/escuro\n(aplicado ao escolher papel de parede)", + "Local account": "Conta local", + "Text extractor": "Extrator de texto", + "Terminal": "Terminal", + "Font family name (e.g., Space Grotesk)": "Nome da família da fonte (ex: Space Grotesk)", + "Markdown test": "Teste Markdown", + "Most busy": "Mais ocupado", + "Current API endpoint: %1\nSet it with %2mode PROVIDER": "Endpoint de API atual: %1\nDefina com %2mode PROVEDOR", + "Center icons": "Centralizar ícones", + "When enabled keeps the content of the right sidebar loaded to reduce the delay when opening,\nat the cost of around 15MB of consistent RAM usage. Delay significance depends on your system's performance.\nUsing a custom kernel like linux-cachyos might help": "Quando ativado, mantém a barra lateral direita carregada para reduzir atraso ao abrir,\nao custo de ~15MB de RAM. O impacto depende do seu sistema.\nUsar um kernel customizado como linux-cachyos pode ajudar", + "Tip: Close a window with Super+Q": "Dica: Feche uma janela com Super+Q", + "Overview": "Visão Geral", + "Unknown device": "Dispositivo desconhecido", + "Volume": "Volume", + "Dark/Light toggle": "Alternar Escuro/Claro", + "Place the corners to trigger at the bottom": "Colocar cantos de ativação na parte inferior", + "Shell conflicts killer": "Matador de conflitos do Shell", + "No media": "Sem mídia", + "Keep right sidebar loaded": "Manter barra direita carregada", + "Virtual Keyboard": "Teclado Virtual", + "Visualize region": "Visualizar região", + "Bar & screen": "Barra & tela", + "RAM": "RAM", + "Timeout (ms)": "Tempo limite (ms)", + "Automatic": "Automático", + "Humidity": "Umidade", + "All": "Todos", + "Connection failed. Please inspect manually with the warp-cli command": "Conexão falhou. Inspecione manualmente com o comando warp-cli", + "Clipboard": "Área de transferência", + "Incorrect password": "Senha incorreta", + "Documentation": "Documentação", + "Used for decorative/expressive text": "Usado para texto decorativo/expressivo", + "Qt apps": "Apps Qt", + "To Do:": "A Fazer:", + "Content": "Adaptativo", + "Thin": "Fino", + "illogical-impulse Welcome": "Boas-vindas ao illogical-impulse", + "Timer": "Timer", + "Widgets": "Widgets", + "Performance Profile toggle": "Alternar Perfil de Desempenho", + "Description font size": "Tamanho da fonte da descrição", + "Show app icons": "Mostrar ícones de apps", + "Preferred wallpaper zoom (%)": "Zoom preferido do papel de parede (%)", + "Remember that on most devices one can always hold the power button to force shutdown\nThis only makes it a tiny bit harder for accidents to happen": "Lembre-se que na maioria dos dispositivos pode-se segurar o botão power para forçar desligamento\nIsso só torna um pouco mais difícil acontecer acidentes", + "Translator": "Tradutor", + "Cannot find a GPS service. Using the fallback method instead.": "Serviço de GPS não encontrado. Usando método alternativo.", + "Image search": "Busca de imagem", + "Digital clock settings": "Configurações do relógio digital", + "Download complete": "Download completo", + "Download": "Baixar", + "Issues": "Problemas (Issues)", + ". Notes for Zerochan:\n- You must enter a color\n- Set your zerochan username in `sidebar.booru.zerochan.username` config option. You [might be banned for not doing so](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!": ". Notas para Zerochan:\n- Você deve inserir uma cor\n- Defina seu usuário zerochan na config `sidebar.booru.zerochan.username`. Você [pode ser banido se não fizer isso](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!", + "Input": "Entrada", + "Notes": "Notas", + "Pin to Start": "Fixar no Iniciar", + "Border": "Borda", + "Tray": "Bandeja (Tray)", + "AI": "IA", + "Recognize music | Right-click to toggle source": "Reconhecer música | Botão direito p/ alternar fonte", + "Random: Konachan": "Aleatório: Konachan", + "Region width": "Largura da região", + "Bubble": "Bolha", + "No new notifications": "Nenhuma notificação nova", + "Quote": "Citação", + "Total:": "Total:", + "Snip": "Recortar", + "Battery": "Bateria", + "Pick wallpaper image on your system": "Escolher imagem no sistema", + "Invalid tool. Supported tools:\n- %1": "Ferramenta inválida. Suportadas:\n- %1", + "Reboot": "Reiniciar", + "The popular one | Best quantity, but quality can vary wildly": "O popular | Melhor quantidade, mas qualidade varia muito", + "Screen snip": "Captura de tela", + "Search": "Buscar", + "Connect to Wi-Fi": "Conectar ao Wi-Fi", + "This is usually safe and needed for your browser and AI sidebar anyway\nMostly useful for those who use lock on startup instead of a display manager that does it (GDM, SDDM, etc.)": "Geralmente seguro e necessário para navegador e IA\nÚtil principalmente para quem usa bloqueio na inicialização em vez de um gerenciador de display (GDM, SDDM, etc.)", + "Darken screen": "Escurecer tela", + "Anti-flashbang (experimental)": "Anti-clarão (experimental)", + "Sign out": "Sair", + "Usage: %1load CHAT_NAME": "Uso: %1load NOME_DO_CHAT", + "Color generation": "Geração de cores", + "Usage: %1superpaste NUM_OF_ENTRIES[i]\nSupply i when you want images\nExamples:\n%1superpaste 4i for the last 4 images\n%1superpaste 7 for the last 7 entries": "Uso: %1superpaste NUM_ENTRADAS[i]\nUse i quando quiser imagens\nExemplos:\n%1superpaste 4i para as últimas 4 imagens\n%1superpaste 7 para as últimas 7 entradas", + "Search wallpapers": "Buscar papéis de parede", + "For storing API keys and other sensitive information": "Para armazenar chaves API e outras informações sensíveis", + "Cancel": "Cancelar", + "Polling interval (s)": "Intervalo de atualização (s)", + "Expressive font": "Fonte expressiva", + "EasyEffects": "EasyEffects", + "Rectangular selection": "Seleção retangular", + "Hi there! First things first...": "Olá! Primeiramente...", + "Saved to %1": "Salvo em %1", + "API key:\n\n```txt\n%1\n```": "Chave API:\n\n```txt\n%1\n```", + "Line": "Linha", + "Number show delay when pressing Super (ms)": "Atraso numérico ao pressionar Super (ms)", + "Change any time later with /dark, /light, /wallpaper in the launcher\nIf the shell's colors aren't changing:\n 1. Open the right sidebar with Super+N\n 2. Click \"Reload Hyprland & Quickshell\" in the top-right corner": "Mude depois com /dark, /light, /wallpaper no launcher\nSe as cores não mudarem:\n 1. Abra a barra direita com Super+N\n 2. Clique em \"Recarregar Hyprland & Quickshell\" no canto superior direito", + "Columns": "Colunas", + "Animate time change": "Animar mudança de hora", + "Audio": "Áudio", + "Weeb": "Otaku", + "Image source": "Fonte da imagem", + "Work safety": "Segurança no trabalho (SFW)", + "Super key symbol": "Símbolo da tecla Super", + "Select language": "Selecionar idioma", + "Free:": "Livre:", + "Anime": "Anime", + "Wallpaper & Colors": "Papel de Parede & Cores", + "Tip: right-clicking a group\nalso expands it": "Dica: clicar com botão direito num grupo\ntambém o expande", + "System prompt": "Prompt do sistema", + "Automatic suspend": "Suspensão automática", + "Thought": "Pensamento", + "Depends on sidebars": "Depende das barras laterais", + "Hour hand": "Ponteiro das horas", + "Security": "Segurança", + "Report a Bug": "Reportar um Bug", + "Reset": "Resetar", + "Command": "Comando", + "Active": "Ativo", + "Current tool: %1\nSet it with %2tool TOOL": "Ferramenta atual: %1\nDefina com %2tool FERRAMENTA", + "Used for headings and titles": "Usado para cabeçalhos e títulos", + "Left": "Esquerda", + "Keybind font size": "Tamanho da fonte de atalhos", + "Region height": "Altura da região", + "Unpin from taskbar": "Desafixar da barra de tarefas", + "Search for apps": "Buscar apps", + "Use symbols for function keys": "Usar símbolos para teclas de função", + "Hover to reveal": "Passar mouse para revelar", + "Perhaps what you're listening to is too niche": "Talvez o que você está ouvindo seja muito nichado", + "at": "em", + "Value scroll": "Scroll de valor", + "Used for general UI text": "Usado para texto geral da UI", + "Use Hyprlock (instead of Quickshell)": "Usar Hyprlock (em vez de Quickshell)", + "Anime boorus": "Anime boorus", + "Sleep": "Dormir", + "Overlay: Floating Image": "Overlay: Imagem Flutuante", + "Time to empty:": "Tempo para descarregar:", + "Last refresh: %1": "Última atualização: %1", + "Music Recognized": "Música Reconhecida", + "Music Recognition": "Reconhecimento de Música", + "Consider plugging in your device": "Considere conectar seu dispositivo", + "Load chat from %1": "Carregar chat de %1", + "Edit directory": "Editar diretório", + "To set an API key, pass it with the %4 command\n\nTo view the key, pass \"get\" with the command
\n\n### For %1:\n\n**Link**: %2\n\n%3": "Para definir chave API, use o comando %4\n\nPara ver a chave, use \"get\" com o comando
\n\n### Para %1:\n\n**Link**: %2\n\n%3", + "Polling interval (ms)": "Intervalo de atualização (ms)", + "or": "ou", + "Timeout duration (if not defined by notification) (ms)": "Duração do tempo limite (se não definido pela notificação) (ms)", + "Low battery": "Bateria fraca", + "Reload Hyprland & Quickshell": "Recarregar Hyprland & Quickshell", + "Input device": "Dispositivo de entrada", + "Make sure you have songrec installed": "Certifique-se de ter o songrec instalado", + "Hover to trigger": "Passar mouse para ativar", + "Please unplug the charger": "Por favor desconecte o carregador", + "Tool set to: %1": "Ferramenta definida para: %1", + "Unmuted": "Com som", + "Rectangle": "Retângulo", + "Invalid arguments. Must provide `key` and `value`.": "Argumentos inválidos. Deve fornecer `key` e `value`.", + "Corner style": "Estilo do canto", + "Screenshot Path (leave empty to just copy)": "Caminho do Print (vazio para apenas copiar)", + "Generate translation with Gemini": "Gerar tradução com Gemini", + "Widget: Clock": "Widget: Relógio", + "Clock style (locked)": "Estilo do relógio (bloqueado)", + "Thinking": "Pensando", + "Cancel wallpaper selection": "Cancelar seleção de papel de parede", + "Superpaste": "Supercolar", + "Bold": "Negrito", + "Brightness and volume": "Brilho e volume", + "Close window": "Fechar janela", + "Overlay: General": "Overlay: Geral", + "Sound output": "Saída de som", + "Conflicts with the shell's system tray implementation": "Conflita com a implementação de bandeja do sistema do shell", + "Elements": "Elementos", + "Audio input": "Entrada de áudio", + "Rows": "Linhas", + "No API key\nSet it with /key YOUR_API_KEY": "Sem chave API\nDefina com /key SUA_CHAVE_API", + "CPU": "CPU", + "Time": "Hora", + "Cookie clock settings": "Configurações do relógio Cookie", + "Resources": "Recursos", + "Enable blur": "Ativar desfoque (blur)", + "Set the current API provider": "Definir provedor de API atual", + "Bar style": "Estilo da barra", + "Configuration": "Configuração", + "Tonal Spot": "Tonal Spot", + "Dial style": "Estilo do mostrador", + "API key set for %1": "Chave API definida para %1", + "Pinned": "Fixado", + "Make sure your player has MPRIS support\nor try turning off duplicate player filtering": "Verifique se seu player tem suporte MPRIS\nou tente desligar o filtro de player duplicado", + "Discussions": "Discussões", + "Precipitation": "Precipitação", + "Pick a wallpaper": "Escolha um papel de parede", + "Random osu! seasonal background\nImage is saved to ~/Pictures/Wallpapers": "Fundo aleatório sazonal de osu!\nImagem salva em ~/Pictures/Wallpapers", + "Press Super+G to open the overlay and pin the crosshair": "Aperte Super+G para abrir o overlay e fixar a mira", + "Save": "Salvar", + "Crosshair code (in Valorant's format)": "Código da mira (formato Valorant)", + "Windows": "Janelas", + "Copy path": "Copiar caminho", + "Make icons pinned by default": "Fixar ícones por padrão", + "Extra wallpaper zoom (%)": "Zoom extra do papel de parede (%)", + "Task Manager": "Gerenciador de Tarefas", + "Font weight": "Peso da fonte", + "On": "Ligado", + "Used for displaying numbers": "Usado para exibir números", + "Unknown command:": "Comando desconhecido:", + "Networking": "Rede", + "Scroll to Bottom": "Rolar para o fundo", + "If you want to somehow use fingerprint unlock...": "Se você quiser usar desbloqueio por digital de alguma forma...", + "Use system file picker": "Usar selecionador de arquivo do sistema", + "Fruit Salad": "Fruit Salad", + "Line-separated": "Separado por linha", + "Pin to taskbar": "Fixar na barra de tarefas", + "Use old sine wave cookie implementation": "Usar implementação antiga de onda senoidal cookie", + "Pinned on startup": "Fixado na inicialização", + "Welcome app": "App de Boas-vindas", + "Unknown function call: %1": "Chamada de função desconhecida: %1", + "Shell & utilities": "Shell & utilitários", + "Require password to power off/restart": "Exigir senha para desligar/reiniciar", + "Scroll to change brightness": "Rolar para mudar brilho", + "Lap": "Volta", + "Health:": "Saúde:", + "When the previous option is off and this is on,\nyou can still hover the corner's end to open sidebar,\nand the remaining area can be used for volume/brightness scroll": "Se a opção anterior estiver desligada e esta ligada,\nvocê ainda pode passar o mouse no fim do canto para abrir a barra lateral,\ne a área restante pode ser usada para rolagem de volume/brilho", + "Approve": "Aprovar", + "Font family name (e.g., JetBrains Mono NF)": "Nome da família da fonte (ex: JetBrains Mono NF)", + "Quick": "Básico", + "Max allowed increase": "Aumento máximo permitido", + "Video Recording Path": "Caminho de Gravação de Vídeo", + "Corner open": "Abrir pelo canto", + "Enjoy your empty sidebar...": "Aproveite sua barra lateral vazia...", + "Cloudflare WARP (1.1.1.1)": "Cloudflare WARP (1.1.1.1)", + "Type /key to get started with online models\nCtrl+O to expand sidebar\nCtrl+P to pin sidebar\nCtrl+D to detach sidebar": "Digite /key para começar com modelos online\nCtrl+O para expandir barra lateral\nCtrl+P para fixar\nCtrl+D para destacar", + "Digital": "Digital", + "Balance brightness based on content": "Balancear brilho baseado no conteúdo", + "Open editor": "Abrir editor", + "Replace 󱕐 for \"Scroll ↓\", 󱕑 \"Scroll ↑\", L󰍽 \"LMB\", R󰍽 \"RMB\", 󱕒 \"Scroll ↑/↓\" and ⇞/⇟ for \"Page_↑/↓\"": "Substitui 󱕐 por \"Scroll ↓\", 󱕑 \"Scroll ↑\", L󰍽 \"Botão Esq.\", R󰍽 \"Botão Dir.\", 󱕒 \"Scroll ↑/↓\" e ⇞/⇟ por \"Page_↑/↓\"", + "Window": "Janela", + "EasyEffects | Right-click to configure": "EasyEffects | Botão direito para configurar", + "Refreshing (manually triggered)": "Atualizando (acionado manualmente)", + "Distro": "Distro", + "Prefixes": "Prefixos", + "e.g. 󰘴 for Ctrl, 󰘵 for Alt, 󰘶 for Shift, etc": "ex: 󰘴 para Ctrl, 󰘵 para Alt, 󰘶 para Shift, etc", + "Pressure": "Pressão", + "Second hand": "Ponteiro de segundos", + "To Do": "A Fazer", + "Silent": "Silencioso", + "Uses Gemini to categorize the wallpaper then picks a preset based on it.\nYou'll need to set Gemini API key on the left sidebar first.\nImages are downscaled for performance, but just to be safe,\ndo not select wallpapers with sensitive information.": "Usa Gemini para categorizar o papel de parede e escolhe um preset.\nVocê precisa definir a chave API do Gemini na barra esquerda antes.\nImagens são reduzidas para desempenho, mas por segurança,\nnão selecione papéis de parede com informações sensíveis.", + "Move right": "Mover para direita", + "Auto": "Auto", + "Move to front": "Mover para frente", + "Top": "Topo", + "Pomodoro": "Pomodoro", + "Copy": "Copiar", + "Weather": "Clima", + "Couldn't recognize music": "Não foi possível reconhecer a música", + "More comfortable viewing at night": "Visualização mais confortável à noite", + "That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "Não funcionou. Dicas:\n- Verifique suas tags e configurações NSFW\n- Se não tiver uma tag em mente, digite um número de página", + "Polling interval (m)": "Intervalo de atualização (m)", + "%1 notifications": "%1 notificações", + "Long break": "Pausa longa", + "Keep system awake": "Manter sistema acordado", + "Place at bottom": "Colocar no fundo", + "Wallpaper safety enforced": "Segurança de papel de parede forçada", + "Shutdown": "Desligar", + "Game mode": "Modo de jogo", + "No further instruction provided": "Nenhuma instrução adicional fornecida", + "Add task": "Adicionar tarefa", + "Policies": "Políticas", + "Discharging:": "Descarregando:", + "Translation goes here...": "Tradução vai aqui...", + "Depends on workspace": "Depende do workspace", + "Open recordings folder": "Abrir pasta de gravações", + "Vertical": "Vertical", + "Hide sussy/anime wallpapers": "Esconder wallpapers suspeitos/anime", + "Enjoy! You can reopen the welcome app any time with Super+Shift+Alt+/. To open the settings app, hit Super+I": "Aproveite! Você pode reabrir o app de boas-vindas a qualquer momento com Super+Shift+Alt+/. Para abrir as configurações, aperte Super+I", + "Math result": "Resultado matemático", + "Nothing here!": "Nada aqui!", + "Font used for Nerd Font icons": "Fonte usada para ícones Nerd Font", + "Illegal increment": "Incremento ilegal", + "Microphone": "Microfone", + "System uptime:": "Tempo de atividade:", + "Automatically suspends the system when battery is low": "Suspende automaticamente o sistema quando a bateria está fraca", + "Notifications": "Notificações", + "Pick random from this folder": "Escolher aleatório desta pasta", + "Interface Language": "Idioma da Interface", + "Reject": "Rejeitar", + "Clock style": "Estilo do relógio", + "Click to show": "Clique para mostrar", + "Hide clipboard images copied from sussy sources": "Esconder imagens da área de transferência de fontes suspeitas", + "Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers": "Wallpaper Anime SFW aleatório do Konachan\nImagem salva em ~/Pictures/Wallpapers", + "Wind": "Vento", + "Bottom": "Inferior", + "Background": "Fundo", + "Enter tags, or \"%1\" for commands": "Digite tags, ou \"%1\" para comandos", + "Usage: %1tool TOOL_NAME": "Uso: %1tool NOME_FERRAMENTA", + "Automatically hide": "Ocultar automaticamente", + "Focusing": "Focando", + "Charging:": "Carregando:", + "Task description": "Descrição da tarefa", + "See fewer": "Ver menos", + "Enable translator": "Ativar tradutor", + "Weather Service": "Serviço de Clima", + "Settings": "Configurações", + "Allow NSFW content": "Permitir conteúdo NSFW", + "Always show numbers": "Sempre mostrar números", + "Could be images or parts of the screen that have some containment.\nMight not always be accurate.\nThis is done with an image processing algorithm run locally and no AI is used.": "Podem ser imagens ou partes da tela com alguma contenção.\nPode não ser sempre preciso.\nIsso é feito com um algoritmo de processamento de imagem local, sem IA.", + "Current model: %1\nSet it with %2model MODEL": "Modelo atual: %1\nDefina com %2model MODELO", + "System sound": "Som do sistema", + "City name": "Nome da cidade", + "Write something here...\nUse '-' to create copyable bullet points, like this:\n\nSheep fricker\n- 4x Slab\n- 1x Boat\n- 4x Redstone Dust\n- 1x Sticky Piston\n- 1x End Rod\n- 4x Redstone Repeater\n- 1x Redstone Torch\n- 1x Sheep": "Escreva algo aqui...\nUse '-' para criar marcadores copiáveis, assim:\n\nFarm de Ovelhas\n- 4x Laje\n- 1x Barco\n- 4x Pó de Redstone\n- 1x Pistão Aderente\n- 1x Lâmpada do End\n- 4x Repetidor de Redstone\n- 1x Tocha de Redstone\n- 1x Ovelha", + "Right to left": "Direita para esquerda", + "Choose file": "Escolher arquivo", + "Could be better if you make a ton of typos,\nbut results can be weird and might not work with acronyms\n(e.g. \"GIMP\" might not give you the paint program)": "Pode ser melhor se você cometer muitos erros de digitação,\nmas os resultados podem ser estranhos e falhar com siglas\n(ex: \"GIMP\" pode não retornar o editor de imagem)", + "Commands, edit configs, search.\nTakes an extra turn to switch to search mode if that's needed": "Comandos, editar configs, buscar.\nLeva um turno extra para mudar para modo de busca se necessário", + "Media": "Mídia", + "Constantly rotate": "Girar constantemente", + "No applications": "Nenhum aplicativo", + "Not connected": "Não conectado", + "Use macOS-like symbols for mods keys": "Usar símbolos estilo macOS para mods de teclas", + "Network": "Rede", + "Useless buttons": "Botões inúteis", + "Top-down": "De cima para baixo", + "Sound input": "Entrada de som", + "Close (Esc)": "Fechar (Esc)", + "No pending tasks": "Nenhuma tarefa pendente", + "Save chat": "Salvar chat", + "Clear all": "Limpar tudo", + "Unpin from Start": "Desafixar do Iniciar", + "Rect": "Retângulo", + "Aligns the date and quote to left, center or right depending on its position on the screen.": "Alinha data e citação à esquerda, centro ou direita dependendo da posição na tela.", + "Overlay: Crosshair": "Overlay: Mira", + "Circle to Search": "Circular para Pesquisar", + "Online via %1 | %2's model": "Online via %1 | Modelo de %2", + "(Plugged in)": "(Conectado)", + "Earbang protection": "Proteção auditiva", + "Edit quick toggles": "Editar botões rápidos", + "Numbers font": "Fonte dos números", + "Temperature must be between 0 and 2": "Temperatura deve ser entre 0 e 2", + "Productivity": "Produtividade", + "Eye protection": "Proteção ocular", + "You'll need to enter your Gemini API key first.\nType /key on the sidebar for instructions.": "Você precisa inserir sua chave API Gemini primeiro.\nDigite /key na barra lateral para instruções.", + "Minute hand": "Ponteiro de minutos", + "Second precision": "Precisão de segundos", + "Wallpaper selector": "Seletor de papel de parede", + "Close": "Fechar", + "Critical warning": "Aviso crítico", + "Connect": "Conectar", + "Polkit": "Polkit", + "Locked": "Bloqueado", + "Connected": "Conectado", + "Allows you to open sidebars by clicking or hovering screen corners regardless of bar position": "Permite abrir barras laterais clicando ou passando mouse nos cantos, independente da posição da barra", + "Volume mixer": "Mixer de volume", + "Done": "Feito", + "12h am/pm": "12h am/pm", + "**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key": "**Preço**: grátis. Dados usados para treinamento.\n\n**Instruções**: Logue na conta Google, permita o AI Studio criar projeto Google Cloud, volte e clique em Get API key", + "Regenerate": "Regenerar", + "Screen round corner": "Canto arredondado da tela", + "Nerd font icons": "Ícones Nerd Font", + "Unknown": "Desconhecido", + "Critically low battery": "Bateria criticamente fraca", + "Enable if you want clocks to show seconds accurately": "Ative se quiser que relógios mostrem segundos com precisão", + "Break": "Pausa", + "Quick markup (Ctrl+E)": "Marcação rápida (Ctrl+E)", + "Use Levenshtein distance-based algorithm instead of fuzzy": "Usar algoritmo baseado em distância Levenshtein em vez de fuzzy", + "Identify Music": "Identificar Música", + "Fahrenheit unit": "Unidade Fahrenheit", + "Scroll to change volume": "Rolar para mudar volume", + "Number style": "Estilo do número", + "Circle selection": "Seleção circular", + "Workspaces shown": "Workspaces mostrados", + "Actions": "Ações", + "OK": "OK", + "The hentai one | Great quantity, a lot of NSFW, quality varies wildly": "O de hentai | Grande quantidade, muito NSFW, qualidade varia muito", + "Rainbow": "Arco-íris", + "Back": "Voltar", + "Layers": "Camadas", + "Random: osu! seasonal": "Aleatório: osu! seasonal", + "Pause": "Pausar", + "Dotfiles": "Dotfiles", + "Services": "Serviços", + "Title font": "Fonte do título", + "Use varying shapes for password characters": "Usar formas variadas para caracteres de senha", + "Group style": "Estilo de grupo", + "Main font": "Fonte principal", + "Show aim lines": "Mostrar linhas de mira", + "**Instructions**: Log into Mistral account, go to Keys on the sidebar, click Create new key": "**Instruções**: Logue na conta Mistral, vá em Keys na barra lateral, clique em Create new key", + "Change password": "Alterar senha", + "Get the latest features and security improvements with\nthe newest feature update.\n\n%1 packages": "Obtenha os últimos recursos e melhorias de segurança com\na atualização mais recente.\n\n%1 pacotes", + "Load prompt from %1": "Carregar prompt de %1", + "On-screen keyboard": "Teclado virtual", + "No API key set for %1": "Nenhuma chave API definida para %1", + "Invalid model. Supported: \n```": "Modelo inválido. Suportados: \n```", + "Command rejected by user": "Comando rejeitado pelo usuário", + "Language": "Idioma", + "Add": "Adicionar", + "Save paths": "Caminhos de salvamento", + "Lock screen": "Tela de bloqueio", + "Light": "Claro", + "Right": "Direita", + "Edit": "Editar", + "Draggable": "Arrastável", + "Local Ollama model | %1": "Modelo local Ollama | %1", + "Display modifiers and keys in multiple keycap (e.g., \"Ctrl + A\" instead of \"Ctrl A\" or \"󰘴 + A\" instead of \"󰘴 A\")": "Mostrar modificadores e teclas em teclas múltiplas (ex: \"Ctrl + A\" em vez de \"Ctrl A\")", + "Restart": "Reiniciar", + "Move left": "Mover para esquerda", + "Forget": "Esquecer", + "More Bluetooth settings": "Mais configurações de Bluetooth", + "Fonts": "Fontes", + "Secured": "Seguro", + "Utility buttons": "Botões utilitários", + "Hibernate": "Hibernar", + "Unknown Artist": "Artista Desconhecido", + "Load chat": "Carregar chat", + "Anti-flashbang": "Proteção de brilho", + "Enabled": "Ativado", + "Always": "Sempre", + "Not secured": "Não seguro", + "Invalid arguments. Must provide `command`.": "Argumentos inválidos. Deve fornecer `command`.", + "Font family": "Família da fonte", + "Page %1": "Página %1", + "Resume": "Resumir", + "Web": "Web", + "Save chat to %1": "Salvar chat em %1", + "Tint app icons": "Colorir ícones de app", + "Color picker": "Seletor de cores", + "Night Light | Right-click to toggle Auto mode": "Luz Noturna | Botão direito para alternar modo Auto", + "Inactive": "Inativo" +} diff --git a/dots/.config/zshrc.d/auto-Hypr.sh b/dots/.config/zshrc.d/auto-Hypr.sh index 55eca5e02..db893c13c 100644 --- a/dots/.config/zshrc.d/auto-Hypr.sh +++ b/dots/.config/zshrc.d/auto-Hypr.sh @@ -1,5 +1,5 @@ # Auto start Hyprland on tty1 if [ -z "$DISPLAY" ] && [ "$XDG_VTNR" -eq 1 ]; then mkdir -p ~/.cache - exec Hyprland > ~/.cache/hyprland.log 2>&1 + exec start-hyprland > ~/.cache/hyprland.log 2>&1 fi diff --git a/sdata/deps-info.md b/sdata/deps-info.md index 9a1e4e826..b28e96033 100644 --- a/sdata/deps-info.md +++ b/sdata/deps-info.md @@ -94,7 +94,7 @@ Tips: - Font name: `Rubik`, `Rubik Light` - Used in Hyprland, kdeglobals, matugen, qt5ct, qt6ct and Quickshell config. - `ttf-twemoji` - - Not explicitly used, but it may help as fallback for displaying emoji charaters. + - Not explicitly used, but it may help as fallback for displaying emoji characters. ## illogical-impulse-hyprland - `hyprland` @@ -106,13 +106,13 @@ Tips: ## illogical-impulse-kde - `bluedevil` - - Seems not being used anywhere, maybe a part of KDE settings panel. + - Provide command `kcmshell6 kcm_bluetooth` used by Quickshell bluetooth functionality. - `gnome-keyring` - Provide executable `gnome-keyring-daemon`, used in Hyprland and Quickshell config. - `networkmanager` - Basic component. - `plasma-nm` - - Seems not being used anywhere, maybe a part of KDE settings panel. + - Provide command `kcmshell6 kcm_networkmanagement` used by Quickshell network functionality. - `polkit-kde-agent` - Basic component. - `dolphin` diff --git a/sdata/dist-arch/illogical-impulse-fonts-themes/PKGBUILD b/sdata/dist-arch/illogical-impulse-fonts-themes/PKGBUILD index b5e37ef28..91e61af82 100644 --- a/sdata/dist-arch/illogical-impulse-fonts-themes/PKGBUILD +++ b/sdata/dist-arch/illogical-impulse-fonts-themes/PKGBUILD @@ -1,6 +1,6 @@ pkgname=illogical-impulse-fonts-themes pkgver=1.0 -pkgrel=4 +pkgrel=5 pkgdesc='Illogical Impulse Fonts and Theming Dependencies' arch=(any) license=(None) @@ -13,7 +13,7 @@ depends=( fish fontconfig kitty - matugen-bin + matugen otf-space-grotesk starship ttf-jetbrains-mono-nerd diff --git a/sdata/dist-arch/illogical-impulse-quickshell-git/PKGBUILD b/sdata/dist-arch/illogical-impulse-quickshell-git/PKGBUILD index 690b73123..3828b9838 100644 --- a/sdata/dist-arch/illogical-impulse-quickshell-git/PKGBUILD +++ b/sdata/dist-arch/illogical-impulse-quickshell-git/PKGBUILD @@ -1,4 +1,4 @@ -_commit='db1777c20b936a86528c1095cbcb1ebd92801402' +_commit='6e17efab83d3a5ad5d6e59bc08d26095c6660502' # Useful links: # https://git.outfoxxed.me/quickshell/quickshell/commits/branch/master # https://aur.archlinux.org/packages/quickshell-git @@ -52,6 +52,7 @@ makedepends=( 'ninja' 'cmake' 'git' + 'vulkan-headers' ) provides=("$_pkgname") conflicts=("$_pkgname") diff --git a/sdata/dist-arch/install-deps.sh b/sdata/dist-arch/install-deps.sh index ecbb3b907..6cdb8d6f9 100644 --- a/sdata/dist-arch/install-deps.sh +++ b/sdata/dist-arch/install-deps.sh @@ -18,6 +18,7 @@ remove_deprecated_dependencies(){ list+=(illogical-impulse-{microtex,pymyc-aur}) list+=(hyprland-qtutils) list+=({quickshell,hyprutils,hyprpicker,hyprlang,hypridle,hyprland-qt-support,hyprland-qtutils,hyprlock,xdg-desktop-portal-hyprland,hyprcursor,hyprwayland-scanner,hyprland}-git) + list+=(matugen-bin) for i in ${list[@]};do try sudo pacman --noconfirm -Rdd $i;done } # NOTE: `implicitize_old_dependencies()` was for the old days when we just switch from dependencies.conf to local PKGBUILDs. diff --git a/sdata/dist-fedora/README.md b/sdata/dist-fedora/README.md index 5643296b4..d47c843fd 100644 --- a/sdata/dist-fedora/README.md +++ b/sdata/dist-fedora/README.md @@ -24,3 +24,4 @@ Note: + "bluetooth": "kcmshell6 kcm_bluetooth", + "network": "plasmawindowed org.kde.plasma.networkmanagement", ``` + diff --git a/sdata/dist-fedora/SPECS/quickshell-git.spec b/sdata/dist-fedora/SPECS/quickshell-git.spec index e90bdebe2..5167a1ae3 100644 --- a/sdata/dist-fedora/SPECS/quickshell-git.spec +++ b/sdata/dist-fedora/SPECS/quickshell-git.spec @@ -63,8 +63,6 @@ Wayland and X11. %endif -DBUILD_SHARED_LIBS=OFF \ -DCMAKE_BUILD_TYPE=Release \ - -DDISTRIBUTOR="Fedora COPR (errornointernet/quickshell)" \ - -DDISTRIBUTOR_DEBUGINFO_AVAILABLE=YES \ -DGIT_REVISION=%{commit} \ -DINSTALL_QML_PREFIX=%{_lib}/qt6/qml %cmake_build diff --git a/sdata/dist-fedora/feddeps.toml b/sdata/dist-fedora/feddeps.toml index 367626ced..aa8091ec1 100644 --- a/sdata/dist-fedora/feddeps.toml +++ b/sdata/dist-fedora/feddeps.toml @@ -1,3 +1,14 @@ +# COPR repositories list +# "solopasha/hyprland" is not up to date to the current Hyprland version, replaced with the fork "sdegler/hyprland" +[copr] +repos = [ + "ririko66z/dots-hyprland", + "sdegler/hyprland", + "deltacopy/darkly", + "alternateved/eza", + "atim/starship" +] + # Fedora dependencies list # Audio @@ -17,6 +28,7 @@ packages = [ "brightnessctl", "ddcutil" ] +install_opts = ["--setopt=install_weak_deps=False"] # Basic [groups.basic] @@ -72,7 +84,7 @@ packages = [ [groups.hyprland] packages = [ "hyprland", - "hyprland-qtutils", + "hyprland-guiutils", "hyprsunset", "wl-clipboard" ] diff --git a/sdata/dist-fedora/install-deps.sh b/sdata/dist-fedora/install-deps.sh index 20e1d1328..18da73e06 100644 --- a/sdata/dist-fedora/install-deps.sh +++ b/sdata/dist-fedora/install-deps.sh @@ -1,9 +1,15 @@ # This script is meant to be sourced. # It's not for directly running. +# ------------------------- +# CONFIG +# ------------------------- +user_config="${REPO_ROOT}/sdata/dist-fedora/user_data.yaml" +rpmbuildroot="${REPO_ROOT}/cache/rpmbuild" +deps_data_file="${REPO_ROOT}/sdata/dist-fedora/feddeps.toml" - -# Initialize the user configuration file -user_config=${REPO_ROOT}/sdata/dist-fedora/user_data.yaml +# ------------------------- +# FUNCTIONS +# ------------------------- # Recording DNF Transaction ID function r() { @@ -16,6 +22,42 @@ function r() { [ "$original_id" == "$last_id" ] || yq -i ".dnf.transaction_ids += [ $last_id ]" "$user_config" || : } +# Start building and install the missing RPM package locally. +function install_RPMS() { + local local_specs local_rpms + rpmbuildroot="${rpmbuildroot:-${REPO_ROOT}/cache/rpmbuild}" + + x mkdir -p "$rpmbuildroot"/{BUILD,RPMS,SOURCES} + x cp -r "${REPO_ROOT}/sdata/dist-fedora/SPECS" "$rpmbuildroot/" + + x cd $rpmbuildroot/SPECS + + mapfile -t -d '' local_specs < <(find "$rpmbuildroot/SPECS" -maxdepth 1 -type f -name "*.spec" -print0) + for spec_file in ${local_specs[@]}; do + # Download sources + x spectool -g -C "$rpmbuildroot/SOURCES" "$spec_file" + # Install build dependencies + r x sudo dnf builddep -y "$spec_file" + # Build the RPM package locally. If it fails, download it from COPR. + if ! rpmbuild -bb --define "_topdir $rpmbuildroot" --define "debug_package %{nil}" "$spec_file"; then + printf "${STY_RED}Local build encountered an issue. Downloading $(basename "$spec_file" .spec) from COPR. Report the issue to Discussions pls.${STY_RST}\n" + sudo dnf install -y $(basename "$spec_file" .spec) + nolock_qs=true + fi + done + + mapfile -t -d '' local_rpms < <(find "$rpmbuildroot/RPMS" -maxdepth 2 -type f -name '*.rpm' -not -name '*debug*' -print0) + if [[ ${#local_rpms[@]} -ge 1 ]]; then + echo -e "${STY_BLUE}Next command:${STY_RST} sudo dnf install ${local_rpms[@]} -y" + r x sudo dnf install "${local_rpms[@]}" -y + fi + x cd ${REPO_ROOT} +} + +# ------------------------- +# MAIN +# ------------------------- + if ! command -v dnf >/dev/null 2>&1; then printf "${STY_RED}[$0]: dnf not found, it seems that the system is not Fedora 42 or later distros. Aborting...${STY_RST}\n" exit 1 @@ -33,61 +75,38 @@ v sudo dnf versionlock delete quickshell-git 2>/dev/null # Install yq for parsing config files v sudo dnf install yq -y -# Development-tools -r v sudo dnf install @development-tools fedora-packager rpmdevtools fonts-rpm-macros qt6-rpm-macros -y +# Install development tools +r v sudo dnf install @development-tools fedora-packager -y -# COPR repositories -v sudo dnf copr enable ririko66z/dots-hyprland -y -v sudo dnf copr enable solopasha/hyprland -y -v sudo dnf copr enable deltacopy/darkly -y -v sudo dnf copr enable alternateved/eza -y -v sudo dnf copr enable atim/starship -y - -# Start building and install the missing RPM package locally. -install_RPMS() { - rpmbuildroot=${REPO_ROOT}/cache/rpmbuild - x mkdir -p $rpmbuildroot/{BUILD,RPMS,SOURCES} - x cp -r ${REPO_ROOT}/sdata/dist-fedora/SPECS $rpmbuildroot/ - x cd $rpmbuildroot/SPECS - mapfile -t -d '' local_specs < <(find "$rpmbuildroot/SPECS" -maxdepth 1 -type f -name "*.spec" -print0) - for spec_file in ${local_specs[@]}; do - x spectool -g -C "$rpmbuildroot/SOURCES" $spec_file - r x sudo dnf builddep -y $spec_file - x rpmbuild -bb --define "_topdir $rpmbuildroot" $spec_file - done - mapfile -t -d '' local_rpms < <(find "$rpmbuildroot/RPMS" -maxdepth 2 -type f -name '*.rpm' -not -name '*debug*' -print0) - echo -e "${STY_BLUE}Next command:${STY_RST} sudo dnf install ${local_rpms[@]} -y" - r x sudo dnf install "${local_rpms[@]}" -y - x cd ${REPO_ROOT} -} +# Install COPR repositories +copr_repos_json=$(yq -o=j '.copr.repos // []' "$deps_data_file") +eval "$(jq -r '@sh "copr_repos_array+=(\(.[]))"' <<<"$copr_repos_json")" # Fedora distro contains jq +for copr in ${copr_repos_array[@]}; do + v sudo dnf copr enable "$copr" -y +done +# Build and install locally RPMS showfun install_RPMS v install_RPMS -deps_data_file="${REPO_ROOT}/sdata/dist-fedora/feddeps.toml" +# Install packages from toml file deps_data=$(yq -o=j '.' "$deps_data_file") echo "Starting to install packages from $deps_data_file ..." while IFS= read -r deps_list_key; do - echo "Installing package list: $deps_list_key" + install_opts=$(echo $deps_data | yq ".groups.\"$deps_list_key\" | select(has(\"install_opts\")) | .install_opts[]") package_list=$(echo $deps_data | yq ".groups.\"$deps_list_key\".packages | unique | .[]") r v sudo dnf install -y $install_opts $package_list 0) -') +done < <(echo "$deps_data" | yq '.groups | keys[]? | select(length > 0)') # Add back versionlock at the end -v sudo dnf versionlock add quickshell-git +[ -n $nolock_qs ] || v sudo dnf versionlock add quickshell-git || true echo -e "\n========================================" -echo "All installations are complete." +echo "All installations are completed." echo "========================================" - diff --git a/sdata/dist-gentoo/hyprland-qtutils-private.patch b/sdata/dist-gentoo/hyprland-qtutils-private.patch new file mode 100644 index 000000000..6ca55104f --- /dev/null +++ b/sdata/dist-gentoo/hyprland-qtutils-private.patch @@ -0,0 +1,33 @@ +--- a/utils/dialog/CMakeLists.txt ++++ b/utils/dialog/CMakeLists.txt +@@ -8,7 +8,7 @@ + set(CMAKE_CXX_STANDARD 23) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + +-find_package(Qt6 6.5 REQUIRED COMPONENTS Widgets Quick QuickControls2 WaylandClient) ++find_package(Qt6 6.5 REQUIRED COMPONENTS Widgets Quick QuickControls2 WaylandClient WaylandClientPrivate) + find_package(PkgConfig REQUIRED) + + pkg_check_modules(hyprutils REQUIRED IMPORTED_TARGET hyprutils) +--- a/utils/donate-screen/CMakeLists.txt ++++ b/utils/donate-screen/CMakeLists.txt +@@ -8,7 +8,7 @@ + set(CMAKE_CXX_STANDARD 23) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + +-find_package(Qt6 6.5 REQUIRED COMPONENTS Widgets Quick QuickControls2 WaylandClient) ++find_package(Qt6 6.5 REQUIRED COMPONENTS Widgets Quick QuickControls2 WaylandClient WaylandClientPrivate) + find_package(PkgConfig REQUIRED) + + pkg_check_modules(hyprutils REQUIRED IMPORTED_TARGET hyprutils) +--- a/utils/update-screen/CMakeLists.txt ++++ b/utils/update-screen/CMakeLists.txt +@@ -8,7 +8,7 @@ + set(CMAKE_CXX_STANDARD 23) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + +-find_package(Qt6 6.5 REQUIRED COMPONENTS Widgets Quick QuickControls2 WaylandClient) ++find_package(Qt6 6.5 REQUIRED COMPONENTS Widgets Quick QuickControls2 WaylandClient WaylandClientPrivate) + find_package(PkgConfig REQUIRED) + + pkg_check_modules(hyprutils REQUIRED IMPORTED_TARGET hyprutils) diff --git a/sdata/dist-gentoo/illogical-impulse-hyprland/illogical-impulse-hyprland-1.0-r3.ebuild b/sdata/dist-gentoo/illogical-impulse-hyprland/illogical-impulse-hyprland-1.0-r3.ebuild index 07330f63b..ac270cb23 100644 --- a/sdata/dist-gentoo/illogical-impulse-hyprland/illogical-impulse-hyprland-1.0-r3.ebuild +++ b/sdata/dist-gentoo/illogical-impulse-hyprland/illogical-impulse-hyprland-1.0-r3.ebuild @@ -14,6 +14,6 @@ RESTRICT="strip" DEPEND="" RDEPEND=" gui-apps/hyprsunset - gui-wm/hyprland + >=gui-wm/hyprland-0.53.3:= gui-apps/wl-clipboard " diff --git a/sdata/dist-gentoo/illogical-impulse-quickshell-git/illogical-impulse-quickshell-git-9999-r4.ebuild b/sdata/dist-gentoo/illogical-impulse-quickshell-git/illogical-impulse-quickshell-git-9999-r5.ebuild similarity index 97% rename from sdata/dist-gentoo/illogical-impulse-quickshell-git/illogical-impulse-quickshell-git-9999-r4.ebuild rename to sdata/dist-gentoo/illogical-impulse-quickshell-git/illogical-impulse-quickshell-git-9999-r5.ebuild index 710a9c0a1..f7bc4a155 100644 --- a/sdata/dist-gentoo/illogical-impulse-quickshell-git/illogical-impulse-quickshell-git-9999-r4.ebuild +++ b/sdata/dist-gentoo/illogical-impulse-quickshell-git/illogical-impulse-quickshell-git-9999-r5.ebuild @@ -9,7 +9,7 @@ DESCRIPTION="Toolkit for building desktop widgets using QtQuick" HOMEPAGE="https://quickshell.org/" EGIT_REPO_URI="https://github.com/quickshell-mirror/quickshell.git" -EGIT_COMMIT="db1777c20b936a86528c1095cbcb1ebd92801402" +EGIT_COMMIT="6e17efab83d3a5ad5d6e59bc08d26095c6660502" KEYWORDS="~amd64 ~arm64 ~x86" LICENSE="LGPL-3" @@ -67,7 +67,7 @@ BDEPEND=" dev-vcs/git virtual/pkgconfig breakpad? ( dev-util/breakpad ) - + dev-util/vulkan-headers " src_configure(){ diff --git a/sdata/dist-gentoo/install-deps.sh b/sdata/dist-gentoo/install-deps.sh index 0aadbf81b..f763fdceb 100644 --- a/sdata/dist-gentoo/install-deps.sh +++ b/sdata/dist-gentoo/install-deps.sh @@ -1,23 +1,12 @@ printf "${STY_YELLOW}" printf "============WARNING/NOTE (1)============\n" -printf "GCC in use: $(which gcc)\n" -printf "GCC version info: $(gcc --version | grep gcc)\n" -printf "GCC version number: $(gcc --version | grep gcc | awk '{print $3}')\n" -printf "GCC-15>= is required for Hyprland\n" -printf "If you have GCC-15>= and it's currently set then you can safely ignore this\n" -printf "If not, you must ensure you are using the correct GCC version and set it (gcc-config )\n" -printf "It is heavily recommended to re-emerge @world with an empty tree after changing GCC version (emerge -e @world)\n\n" -printf "${STY_RST}" -pause - -printf "${STY_YELLOW}" -printf "============WARNING/NOTE (2)============\n" printf "Ensure you have a global use flag for elogind or systemd in your make.conf for simplicity\n" printf "Or you can manually add the use flags for each package that requires it\n" printf "${STY_RST}" pause printf "${STY_YELLOW}" +printf "============WARNING/NOTE (2)============\n" printf "https://github.com/end-4/dots-hyprland/blob/main/sdata/dist-gentoo/README.md\n" printf "Checkout the above README for potential bug fixes or additional information\n\n" printf "${STY_RST}" @@ -37,6 +26,10 @@ if [[ -z $(eselect repository list | grep -E ".*guru \*.*") ]]; then v sudo eselect repository enable guru fi +if [[ -z $(eselect repository list | grep -E ".*hyproverlay \*.*") ]]; then + v sudo eselect repository enable hyproverlay +fi + arch=$(portageq envvar ACCEPT_KEYWORDS) # Exclude hyprland, will deal with that separately @@ -51,15 +44,6 @@ x sudo cp ./sdata/dist-gentoo/keywords ./sdata/dist-gentoo/keywords-user x sed -i "s/$/ ~${arch}/" ./sdata/dist-gentoo/keywords-user v sudo cp ./sdata/dist-gentoo/keywords-user /etc/portage/package.accept_keywords/illogical-impulse -# QT -x sudo cp ./sdata/dist-gentoo/qt-keywords ./sdata/dist-gentoo/qt-keywords-user -x sed -i "s/$/ ~${arch}/" ./sdata/dist-gentoo/qt-keywords-user -v sudo cp ./sdata/dist-gentoo/qt-keywords-user /etc/portage/package.accept_keywords/qt - -########## IMPORT UNMASKS -sudo mkdir -p /etc/portage/package.unmask/ -v sudo cp ./sdata/dist-gentoo/qt-unmasks /etc/portage/package.unmask/qt - ########## IMPORT USEFLAGS v sudo cp ./sdata/dist-gentoo/useflags /etc/portage/package.use/illogical-impulse v sudo sh -c 'cat ./sdata/dist-gentoo/additional-useflags >> /etc/portage/package.use/illogical-impulse' diff --git a/sdata/dist-gentoo/keywords b/sdata/dist-gentoo/keywords index 5ac87921e..238200763 100644 --- a/sdata/dist-gentoo/keywords +++ b/sdata/dist-gentoo/keywords @@ -19,10 +19,12 @@ app-misc/brightnessctl app-misc/cliphist gui-apps/hypridle gui-apps/hyprlock +dev-libs/hyprlang gui-apps/hyprpicker gui-apps/hyprsunset gui-libs/xdg-desktop-portal-hyprland gui-apps/hyprshot +gui-libs/hyprcursor gui-apps/wf-recorder gui-apps/wtype gui-apps/fuzzel @@ -30,15 +32,15 @@ gui-apps/quickshell ** gui-apps/wlogout dev-cpp/sdbus-c++ dev-libs/hyprland-protocols -gui-libs/aquamarine gui-libs/hyprutils +gui-libs/hyprwire dev-util/hyprwayland-scanner -dev-libs/hyprlang -dev-libs/hyprgraphics -gui-libs/hyprcursor gui-libs/hyprland-qt-support -gui-libs/hyprland-qtutils +gui-libs/hyprland-guiutils +gui-libs/hyprtoolkit gui-wm/hyprland +dev-libs/hyprgraphics +gui-libs/aquamarine x11-libs/libxkbcommon dev-util/breakpad dev-libs/linux-syscall-support @@ -51,3 +53,4 @@ media-fonts/material-symbols-variable ** media-fonts/readex-pro media-fonts/rubik-vf app-misc/songrec +dev-cpp/glaze diff --git a/sdata/dist-gentoo/qt-keywords b/sdata/dist-gentoo/qt-keywords deleted file mode 100644 index 71e812c54..000000000 --- a/sdata/dist-gentoo/qt-keywords +++ /dev/null @@ -1,27 +0,0 @@ -dev-qt/qtbase -dev-qt/qtdeclarative -dev-qt/qtlanguageserver -dev-qt/qtsvg -dev-qt/qtshadertools -dev-qt/qttranslations -dev-qt/qttools -dev-qt/qt5compat -dev-qt/qtcore -dev-qt/qtdbus -dev-qt/qtgraphicaleffects -dev-qt/qtgui -dev-qt/qtimageformats -dev-qt/qtlocation -dev-qt/qtmultimedia -dev-qt/qtnetwork -dev-qt/qtpositioning -dev-qt/qtquick3d -dev-qt/qtquickcontrols2 -dev-qt/qtquicktimeline -dev-qt/qtsensors -dev-qt/qtspeech -dev-qt/qttest -dev-qt/qtvirtualkeyboard -dev-qt/qtwayland -dev-qt/qtwayland -dev-qt/qtwidgets diff --git a/sdata/dist-gentoo/qt-unmasks b/sdata/dist-gentoo/qt-unmasks deleted file mode 100644 index 71e812c54..000000000 --- a/sdata/dist-gentoo/qt-unmasks +++ /dev/null @@ -1,27 +0,0 @@ -dev-qt/qtbase -dev-qt/qtdeclarative -dev-qt/qtlanguageserver -dev-qt/qtsvg -dev-qt/qtshadertools -dev-qt/qttranslations -dev-qt/qttools -dev-qt/qt5compat -dev-qt/qtcore -dev-qt/qtdbus -dev-qt/qtgraphicaleffects -dev-qt/qtgui -dev-qt/qtimageformats -dev-qt/qtlocation -dev-qt/qtmultimedia -dev-qt/qtnetwork -dev-qt/qtpositioning -dev-qt/qtquick3d -dev-qt/qtquickcontrols2 -dev-qt/qtquicktimeline -dev-qt/qtsensors -dev-qt/qtspeech -dev-qt/qttest -dev-qt/qtvirtualkeyboard -dev-qt/qtwayland -dev-qt/qtwayland -dev-qt/qtwidgets diff --git a/sdata/dist-gentoo/useflags b/sdata/dist-gentoo/useflags index e7c9755fc..e94832d36 100644 --- a/sdata/dist-gentoo/useflags +++ b/sdata/dist-gentoo/useflags @@ -39,7 +39,7 @@ media-fonts/twemoji X ################### HYPRLAND ################### #gui-apps/hypridle (no use flags) #gui-libs/hyprcursor (no use flags) -gui-wm/hyprland X qtutils +gui-wm/hyprland::hyproverlay X guiutils #gui-libs/hyprland-qtutils (no use flags) #gui-libs/hyprland-qt-support (no use flags) #dev-libs/hyprlang (no use flags) diff --git a/sdata/lib/functions.sh b/sdata/lib/functions.sh index 0fe24f0ed..bc8f08400 100644 --- a/sdata/lib/functions.sh +++ b/sdata/lib/functions.sh @@ -64,7 +64,7 @@ function showfun(){ function pause(){ if [ ! "$ask" == "false" ];then printf "${STY_FAINT}${STY_SLANT}" - local p; read -p "(Ctrl-C to abort, others to proceed)" p + local p; read -p "(Ctrl-C to abort, Enter to proceed)" p printf "${STY_RST}" fi } @@ -119,8 +119,8 @@ function sudo_init_keepalive(){ # Stop the sudo keepalive background process function sudo_stop_keepalive(){ if [[ -n "$SUDO_KEEPALIVE_PID" ]] && kill -0 "$SUDO_KEEPALIVE_PID" 2>/dev/null; then - kill "$SUDO_KEEPALIVE_PID" 2>/dev/null - wait "$SUDO_KEEPALIVE_PID" 2>/dev/null + kill "$SUDO_KEEPALIVE_PID" 2>/dev/null || true + wait "$SUDO_KEEPALIVE_PID" 2>/dev/null || true SUDO_KEEPALIVE_PID="" fi } diff --git a/sdata/subcmd-exp-update/0.run.sh b/sdata/subcmd-exp-update/0.run.sh index 2ab21379c..a4bc8022b 100644 --- a/sdata/subcmd-exp-update/0.run.sh +++ b/sdata/subcmd-exp-update/0.run.sh @@ -31,9 +31,7 @@ # set -euo pipefail -# TODO: For Arch(-Linux) specific part please check if pacman exists first, if not it should be skipped. - -# TODO: Is this really needed? `git pull` should do a full upgrade, not partially, which means this script will be updated along with the folder structure together. +# Note: The detect_repo_structure function below auto-detects the folder layout # Try to find the packages directory (different names in different versions) if which pacman &>/dev/null; then if [[ -d "${REPO_ROOT}/dist-arch" ]]; then @@ -47,7 +45,9 @@ if which pacman &>/dev/null; then fi fi UPDATE_IGNORE_FILE="${REPO_ROOT}/.updateignore" -HOME_UPDATE_IGNORE_FILE="${HOME}/.updateignore" +XDG_UPDATE_IGNORE_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/illogical-impulse/updateignore" +#TODO: remove in future and add script to migrate to XDG path +HOME_UPDATE_IGNORE_FILE="${HOME}/.updateignore" # Legacy support # Global arrays for cached ignore patterns (performance optimization) declare -a IGNORE_PATTERNS=() @@ -56,7 +56,6 @@ declare -a IGNORE_SUBSTRING_PATTERNS=() # Track created directories to avoid redundant mkdir calls declare -A CREATED_DIRS -# TODO: Is this really needed? `git pull` should do a full upgrade, not partially, which means this script will be updated along with the folder structure together. # Auto-detect repository structure detect_repo_structure() { local found_dirs=() @@ -114,7 +113,16 @@ safe_read() { echo -n "$prompt" - # Try to read from terminal with better detection + # First, try reading from stdin (supports piped input like "yes 1 |") + if read -r -t 0.1 input_value 2>/dev/null; then + # Successfully read from stdin (piped input) + if [[ -n "$input_value" ]]; then + printf -v "$varname" '%s' "$input_value" + return 0 + fi + fi + + # If stdin had no data, try interactive terminal if [[ -t 0 ]]; then # stdin is a terminal read -r input_value @@ -159,7 +167,7 @@ load_ignore_patterns() { IGNORE_PATTERNS=() IGNORE_SUBSTRING_PATTERNS=() - for ignore_file in "$UPDATE_IGNORE_FILE" "$HOME_UPDATE_IGNORE_FILE"; do + for ignore_file in "$UPDATE_IGNORE_FILE" "$XDG_UPDATE_IGNORE_FILE" "$HOME_UPDATE_IGNORE_FILE"; do [[ ! -f "$ignore_file" ]] && continue while IFS= read -r pattern || [[ -n "$pattern" ]]; do @@ -340,168 +348,166 @@ handle_file_conflict() { local home_file="$2" local filename=$(basename "$home_file") local dirname=$(dirname "$home_file") + local choice="" + local default_val="${DEFAULT_CHOICE:-6}" # Use DEFAULT_CHOICE or 6 (skip) as fallback - echo -e "\n${STY_YELLOW}Conflict detected:${STY_RST} $home_file" - echo "Repository version differs from your local version." - echo - echo "Choose an action:" - echo "1) Replace local file with repository version" - echo "2) Keep local file unchanged" - echo "3) Backup local file as ${filename}.old, use repository version" - echo "4) Save repository version as ${filename}.new, keep local file" - echo "5) Show diff and decide" - echo "6) Skip this file" - echo "7) Add to ignore and skip" - echo "8) Backup to .update-backups/ and replace with repository version" - echo + # In non-interactive mode, use default directly (acts like pressing Enter) + if [[ "$NON_INTERACTIVE" == true ]]; then + choice="$default_val" + log_info "Using choice $choice for: $home_file" + else + echo -e "\n${STY_YELLOW}Conflict detected:${STY_RST} $home_file" + echo "Repository version differs from your local version." + echo + echo "Choose an action:" + echo "1) Replace local file with repository version" + echo "2) Keep local file unchanged" + echo "3) Backup local file as ${filename}.old, use repository version" + echo "4) Save repository version as ${filename}.new, keep local file" + echo "5) Show diff and decide" + echo "6) Skip this file" + echo "7) Add to ignore and skip" + echo "8) Backup to .update-backups/ and replace with repository version" + echo - while true; do - if ! safe_read "Enter your choice (1-8): " choice "6"; then + while true; do + if ! safe_read "Enter your choice (1-8 or name) [${default_val}]: " choice "$default_val"; then + echo + log_warning "Failed to read input. Skipping file." + return + fi + + # Validate choice + if [[ "$choice" =~ ^[1-8]$ ]] || [[ "$choice" =~ ^(replace|keep|old|new|diff|skip|ignore|backup)$ ]]; then + break + else + echo "Invalid choice. Please enter 1-8 or a valid name (replace, keep, old ...)." + fi + done + fi + + case $choice in + 1|replace) + if [[ "$DRY_RUN" == true ]]; then + log_info "[DRY-RUN] Would replace $home_file with repository version" + else + cp -p "$repo_file" "$home_file" + log_success "Replaced $home_file with repository version" + fi + ;; + 2|keep) + log_info "Keeping local version of $home_file" + ;; + 3|old) + if [[ "$DRY_RUN" == true ]]; then + log_info "[DRY-RUN] Would backup local file to ${filename}.old and update with repository version" + else + mv "$home_file" "${dirname}/${filename}.old" + cp -p "$repo_file" "$home_file" + log_success "Backed up local file to ${filename}.old and updated with repository version" + fi + ;; + 4|new) + if [[ "$DRY_RUN" == true ]]; then + log_info "[DRY-RUN] Would save repository version as ${filename}.new, keep local file" + else + cp -p "$repo_file" "${dirname}/${filename}.new" + log_success "Saved repository version as ${filename}.new, kept local file" + fi + ;; + 5|diff) + show_diff "$home_file" "$repo_file" + echo + echo "After reviewing the diff, choose:" + echo "r) Replace with repository version" + echo "k) Keep local version" + echo "b) Backup local and use repository version" + echo "n) Save repository version as .new" + echo "s) Skip this file" + echo "i) Add to ignore and skip" + echo "B) Backup to .update-backups/ and replace" + + if ! safe_read "Enter your choice (r/k/b/n/s/i/B): " subchoice "s"; then echo log_warning "Failed to read input. Skipping file." return fi - case $choice in - 1) + case $subchoice in + r) if [[ "$DRY_RUN" == true ]]; then log_info "[DRY-RUN] Would replace $home_file with repository version" else cp -p "$repo_file" "$home_file" log_success "Replaced $home_file with repository version" fi - break ;; - 2) + k) log_info "Keeping local version of $home_file" - break ;; - 3) + b) if [[ "$DRY_RUN" == true ]]; then - log_info "[DRY-RUN] Would backup local file to ${filename}.old and update with repository version" + log_info "[DRY-RUN] Would backup local file to ${filename}.old and update" else mv "$home_file" "${dirname}/${filename}.old" cp -p "$repo_file" "$home_file" - log_success "Backed up local file to ${filename}.old and updated with repository version" + log_success "Backed up local file to ${filename}.old and updated" fi - break ;; - 4) + n) if [[ "$DRY_RUN" == true ]]; then - log_info "[DRY-RUN] Would save repository version as ${filename}.new, keep local file" + log_info "[DRY-RUN] Would save repository version as ${filename}.new" else cp -p "$repo_file" "${dirname}/${filename}.new" - log_success "Saved repository version as ${filename}.new, kept local file" + log_success "Saved repository version as ${filename}.new" fi - break ;; - 5) - show_diff "$home_file" "$repo_file" - echo - echo "After reviewing the diff, choose:" - echo "r) Replace with repository version" - echo "k) Keep local version" - echo "b) Backup local and use repository version" - echo "n) Save repository version as .new" - echo "s) Skip this file" - echo "i) Add to ignore and skip" - echo "B) Backup to .update-backups/ and replace" - - if ! safe_read "Enter your choice (r/k/b/n/s/i/B): " subchoice "s"; then - echo - log_warning "Failed to read input. Skipping file." - return - fi - - case $subchoice in - r) - if [[ "$DRY_RUN" == true ]]; then - log_info "[DRY-RUN] Would replace $home_file with repository version" - else - cp -p "$repo_file" "$home_file" - log_success "Replaced $home_file with repository version" - fi - break - ;; - k) - log_info "Keeping local version of $home_file" - break - ;; - b) - if [[ "$DRY_RUN" == true ]]; then - log_info "[DRY-RUN] Would backup local file to ${filename}.old and update" - else - mv "$home_file" "${dirname}/${filename}.old" - cp -p "$repo_file" "$home_file" - log_success "Backed up local file to ${filename}.old and updated" - fi - break - ;; - n) - if [[ "$DRY_RUN" == true ]]; then - log_info "[DRY-RUN] Would save repository version as ${filename}.new" - else - cp -p "$repo_file" "${dirname}/${filename}.new" - log_success "Saved repository version as ${filename}.new" - fi - break - ;; - s) - log_info "Skipping $home_file" - break - ;; - i) - local relative_path_to_home="${home_file#$HOME/}" - if [[ "$DRY_RUN" == true ]]; then - log_info "[DRY-RUN] Would add '$relative_path_to_home' to $HOME_UPDATE_IGNORE_FILE" - else - echo "$relative_path_to_home" >>"$HOME_UPDATE_IGNORE_FILE" - log_success "Added '$relative_path_to_home' to $HOME_UPDATE_IGNORE_FILE and skipped." - fi - break - ;; - B) - if backup_file "$home_file"; then - if [[ "$DRY_RUN" != true ]]; then - cp -p "$repo_file" "$home_file" - log_success "Replaced $home_file with repository version" - fi - fi - break - ;; - *) - echo "Invalid choice. Please try again." - ;; - esac - ;; - 6) + s) log_info "Skipping $home_file" - break ;; - 7) + i) local relative_path_to_home="${home_file#$HOME/}" if [[ "$DRY_RUN" == true ]]; then - log_info "[DRY-RUN] Would add '$relative_path_to_home' to $HOME_UPDATE_IGNORE_FILE" + log_info "[DRY-RUN] Would add '$relative_path_to_home' to $XDG_UPDATE_IGNORE_FILE" else - echo "$relative_path_to_home" >>"$HOME_UPDATE_IGNORE_FILE" - log_success "Added '$relative_path_to_home' to $HOME_UPDATE_IGNORE_FILE and skipped." + echo "$relative_path_to_home" >>"$XDG_UPDATE_IGNORE_FILE" + log_success "Added '$relative_path_to_home' to $XDG_UPDATE_IGNORE_FILE and skipped." fi - break ;; - 8) + B) if backup_file "$home_file"; then if [[ "$DRY_RUN" != true ]]; then cp -p "$repo_file" "$home_file" log_success "Replaced $home_file with repository version" fi fi - break ;; *) - echo "Invalid choice. Please enter 1-8." + log_info "Skipping $home_file" ;; esac - done + ;; + 6|skip) + log_info "Skipping $home_file" + ;; + 7|ignore) + local relative_path_to_home="${home_file#$HOME/}" + if [[ "$DRY_RUN" == true ]]; then + log_info "[DRY-RUN] Would add '$relative_path_to_home' to $XDG_UPDATE_IGNORE_FILE" + else + echo "$relative_path_to_home" >>"$XDG_UPDATE_IGNORE_FILE" + log_success "Added '$relative_path_to_home' to $XDG_UPDATE_IGNORE_FILE and skipped." + fi + ;; + 8|backup) + if backup_file "$home_file"; then + if [[ "$DRY_RUN" != true ]]; then + cp -p "$repo_file" "$home_file" + log_success "Replaced $home_file with repository version" + fi + fi + ;; + esac } # Function to check if PKGBUILD has changed @@ -644,23 +650,41 @@ build_packages() { log_info "Building package: $pkg_name" if [[ "$DRY_RUN" == true ]]; then - log_info "[DRY-RUN] Would build package in directory: $pkg_dir" + log_info "[DRY-RUN] Would build package in temp directory and clean up after" continue fi - cd "$pkg_dir" || { - log_error "Failed to change to package directory: $pkg_dir" + # Create temp build directory to avoid polluting the repo + local build_tmp_dir + build_tmp_dir=$(mktemp -d "/tmp/pkgbuild-${pkg_name}-XXXXXX") + + # Copy package files to temp directory (using /. to include hidden files) + cp -r "$pkg_dir"/. "$build_tmp_dir/" || { + log_error "Failed to copy package files to temp directory" + rm -rf "$build_tmp_dir" continue } - if makepkg -si --noconfirm; then + cd "$build_tmp_dir" || { + log_error "Failed to change to temp build directory: $build_tmp_dir" + rm -rf "$build_tmp_dir" + continue + } + + if makepkg -sCi --noconfirm; then log_success "Successfully built and installed $pkg_name" - ((rebuilt_packages++)) + ((rebuilt_packages++)) || true else log_error "Failed to build package $pkg_name" fi + # Clean up temp build directory cd "$REPO_ROOT" || log_die "Failed to return to repository directory" + rm -rf "$build_tmp_dir" + log_info "Cleaned up temp build directory" + + # Also clean any old build artifacts in the original package directory + rm -rf "${pkg_dir}/src" "${pkg_dir}/pkg" "${pkg_dir}"/*.pkg.tar.* 2>/dev/null || true done if [[ $rebuilt_packages -eq 0 ]]; then @@ -852,8 +876,12 @@ if git remote get-url origin &>/dev/null; then fi fi else - log_warning "Failed to pull changes from remote. Continuing with local repository..." - log_info "You may need to resolve conflicts manually later." + log_warning "Failed to pull changes from remote." + log_warning "This could be due to:" + log_warning " - Network issues" + log_warning " - Uncommitted local changes (use 'git stash' first)" + log_warning " - Diverged history (may need 'git pull --rebase')" + log_info "Continuing with local repository state..." fi fi else @@ -879,68 +907,70 @@ if [[ "$CHECK_PACKAGES" == true ]]; then if [[ "$PKG_TOOLS_AVAILABLE" == true ]]; then if [[ ! -d "$ARCH_PACKAGES_DIR" ]]; then - log_warning "No packages directory found (tried: dist-arch, arch-packages, sdata/dist-arch). Skipping package management." + log_warning "No packages directory found (tried: dist-arch, arch-packages, sdata/dist-arch)." + log_warning "Skipping package management." else - changed_pkgbuilds=() - for pkg_dir in "$ARCH_PACKAGES_DIR"/*/; do - if [[ -f "${pkg_dir}/PKGBUILD" ]]; then - pkg_name=$(basename "$pkg_dir") - if check_pkgbuild_changed "$pkg_dir"; then - changed_pkgbuilds+=("$pkg_name") - fi - fi - done - - if [[ ${#changed_pkgbuilds[@]} -gt 0 ]]; then - log_info "Found ${#changed_pkgbuilds[@]} package(s) with changed PKGBUILDs: ${changed_pkgbuilds[*]}" - echo - echo "Package build options:" - echo "1) Build only packages with changed PKGBUILDs" - echo "2) List all packages and select which to build" - echo "3) Build all packages" - echo "4) Skip package building" - echo - - if [[ "$NON_INTERACTIVE" == true ]]; then - pkg_choice="1" - log_info "Non-interactive mode: Using default package option: $pkg_choice" - elif safe_read "Choose an option (1-4): " pkg_choice "1"; then - if [[ "$VERBOSE" == true ]]; then - log_info "User selected package option: $pkg_choice" - fi - else - log_warning "Failed to read input. Skipping package building." - pkg_choice="" - fi - - if [[ -n "$pkg_choice" ]]; then - case $pkg_choice in - 1) build_packages "changed" ;; - 2) - if list_packages; then - build_packages "select" + # Scan for changed PKGBUILDs + changed_pkgbuilds=() + for pkg_dir in "$ARCH_PACKAGES_DIR"/*/; do + if [[ -f "${pkg_dir}/PKGBUILD" ]]; then + pkg_name=$(basename "$pkg_dir") + if check_pkgbuild_changed "$pkg_dir"; then + changed_pkgbuilds+=("$pkg_name") fi - ;; - 3) build_packages "all" ;; - 4 | *) log_info "Skipping package building" ;; - esac - fi - else - log_info "No PKGBUILDs have changed since last update." - echo - if [[ "$NON_INTERACTIVE" == true ]]; then - check_anyway="N" - log_info "Non-interactive mode: Using default for check packages anyway: $check_anyway" - elif safe_read "Do you want to check and build packages anyway? (y/N): " check_anyway "N"; then - if [[ "$VERBOSE" == true ]]; then - log_info "User chose to check packages anyway: $check_anyway" + fi + done + + if [[ ${#changed_pkgbuilds[@]} -gt 0 ]]; then + log_info "Found ${#changed_pkgbuilds[@]} package(s) with changed PKGBUILDs: ${changed_pkgbuilds[*]}" + echo + echo "Package build options:" + echo "1) Build only packages with changed PKGBUILDs" + echo "2) List all packages and select which to build" + echo "3) Build all packages" + echo "4) Skip package building" + echo + + if [[ "$NON_INTERACTIVE" == true ]]; then + pkg_choice="1" + log_info "Non-interactive mode: Using default package option: $pkg_choice" + elif safe_read "Choose an option (1-4): " pkg_choice "1"; then + if [[ "$VERBOSE" == true ]]; then + log_info "User selected package option: $pkg_choice" + fi + else + log_warning "Failed to read input. Skipping package building." + pkg_choice="" + fi + + if [[ -n "$pkg_choice" ]]; then + case $pkg_choice in + 1) build_packages "changed" ;; + 2) + if list_packages; then + build_packages "select" + fi + ;; + 3) build_packages "all" ;; + 4|*) log_info "Skipping package building" ;; + esac fi else - log_warning "Failed to read input. Skipping package management." - check_anyway="" - fi + log_info "No PKGBUILDs have changed since last update." + echo + if [[ "$NON_INTERACTIVE" == true ]]; then + check_anyway="N" + log_info "Non-interactive mode: Using default for check packages anyway: $check_anyway" + elif safe_read "Do you want to check and build packages anyway? (y/N): " check_anyway "N"; then + if [[ "$VERBOSE" == true ]]; then + log_info "User chose to check packages anyway: $check_anyway" + fi + else + log_warning "Failed to read input. Skipping package management." + check_anyway="" + fi - if [[ -n "$check_anyway" && "$check_anyway" =~ ^[Yy]$ ]]; then + if [[ -n "$check_anyway" && "$check_anyway" =~ ^[Yy]$ ]]; then if list_packages; then echo echo "Package build options:" @@ -950,9 +980,9 @@ if [[ "$CHECK_PACKAGES" == true ]]; then if safe_read "Choose an option (1-3): " build_choice "3"; then case $build_choice in - 1) build_packages "select" ;; - 2) build_packages "all" ;; - 3 | *) log_info "Skipping package building" ;; + 1) build_packages "select" ;; + 2) build_packages "all" ;; + 3|*) log_info "Skipping package building" ;; esac else log_info "Skipping package building" @@ -963,10 +993,10 @@ if [[ "$CHECK_PACKAGES" == true ]]; then fi fi fi - else - log_header "Package Management" - log_info "Package checking disabled. Use -p or --packages flag to enable package management." fi +else + log_header "Package Management" + log_info "Package checking disabled. Use -p or --packages flag to enable package management." fi # Step 3: Update configuration files @@ -1023,7 +1053,7 @@ if [[ "$process_files" == true ]]; then ensure_directory "$home_dir_path" || continue - while IFS= read -r -d '' repo_file; do + while IFS= read -r -d '' -u 9 repo_file; do # Calculate relative path from the repo source directory rel_path="${repo_file#$repo_dir_path/}" home_file="${home_dir_path}/${rel_path}" @@ -1077,7 +1107,7 @@ if [[ "$process_files" == true ]]; then fi ((files_created++)) fi - done < <(get_changed_files "$repo_dir_path") || true + done 9< <(get_changed_files "$repo_dir_path") || true echo done @@ -1137,11 +1167,11 @@ if [[ "$process_files" == true ]]; then echo "- New files created: $files_created" fi -if [[ ! -f "$HOME_UPDATE_IGNORE_FILE" && ! -f "$UPDATE_IGNORE_FILE" ]]; then +if [[ ! -f "$XDG_UPDATE_IGNORE_FILE" && ! -f "$UPDATE_IGNORE_FILE" ]]; then echo log_info "Tip: Create ignore files to exclude files from updates:" echo " - Repository ignore: ${REPO_ROOT}/.updateignore" - echo " - User ignore: ~/.updateignore" + echo " - User ignore: ${XDG_UPDATE_IGNORE_FILE}" echo echo "Example patterns:" echo " *.log # Ignore all .log files" diff --git a/sdata/subcmd-exp-update/exp-update-tester.sh b/sdata/subcmd-exp-update/exp-update-tester.sh index 15e93f150..e0a02ff55 100755 --- a/sdata/subcmd-exp-update/exp-update-tester.sh +++ b/sdata/subcmd-exp-update/exp-update-tester.sh @@ -469,7 +469,7 @@ test_shellcheck() { return 0 fi - if shellcheck -e SC1090,SC1091,SC2148,SC2034,SC2155,SC2164 setup then + if shellcheck -e SC1090,SC1091,SC2148,SC2034,SC2155,SC2164 setup; then log_pass "shellcheck passed" return 0 else diff --git a/sdata/subcmd-exp-update/options.sh b/sdata/subcmd-exp-update/options.sh index 30198299c..79acbbcce 100644 --- a/sdata/subcmd-exp-update/options.sh +++ b/sdata/subcmd-exp-update/options.sh @@ -15,7 +15,10 @@ Options: -h, --help Show this help message -s, --skip-notice Skip notice about script being untested --non-interactive - Run without prompting for user input + Set default choice for file conflicts + replace: Replace local keep: Keep local old: Backup as .old + new: Save as .new diff: Show diff skip: Skip + ignore: Add to ignore backup: Backup and replace This script updates your dotfiles by: 1. Auto-detecting repository structure (dots/ prefix or direct) @@ -35,7 +38,7 @@ Ignore file patterns support: # `man getopt` to see more para=$(getopt \ -o hfpnvs \ - -l help,force,packages,dry-run,verbose,skip-notice,non-interactive \ + -l help,force,packages,dry-run,verbose,skip-notice,non-interactive,default-choice: \ -n "$0" -- "$@") [ $? != 0 ] && echo "$0: Error when getopt, please recheck parameters." && exit 1 ##################################################################################### @@ -58,6 +61,7 @@ DRY_RUN=false VERBOSE=false SKIP_NOTICE=false NON_INTERACTIVE=false +DEFAULT_CHOICE="" eval set -- "$para" while true ; do @@ -81,6 +85,25 @@ while true ; do --non-interactive) NON_INTERACTIVE=true;shift log_info "Non-interactive mode enabled" ;; + --default-choice) + case "$2" in + replace) DEFAULT_CHOICE="1" ;; + keep) DEFAULT_CHOICE="2" ;; + old) DEFAULT_CHOICE="3" ;; + new) DEFAULT_CHOICE="4" ;; + diff) DEFAULT_CHOICE="5" ;; + skip) DEFAULT_CHOICE="6" ;; + ignore) DEFAULT_CHOICE="7" ;; + backup) DEFAULT_CHOICE="8" ;; + *) + log_error "Invalid --default-choice value: $2" + log_error "Valid values: replace, keep, old, new, diff, skip, ignore, backup" + exit 1 + ;; + esac + shift 2 + log_info "Default conflict choice set to: $DEFAULT_CHOICE" + ;; ## Ending --) break ;; diff --git a/sdata/subcmd-install/3.files.sh b/sdata/subcmd-install/3.files.sh index d0e537aa3..956ee4859 100644 --- a/sdata/subcmd-install/3.files.sh +++ b/sdata/subcmd-install/3.files.sh @@ -118,7 +118,7 @@ function install_dir(){ if [ -d $t ];then warning_overwrite fi - rsync_dir $s $t + v rsync_dir $s $t } function install_dir__sync(){ # NOTE: Do not add prefix `v` or `x` when using this function @@ -127,7 +127,7 @@ function install_dir__sync(){ if [ -d $t ];then warning_overwrite fi - rsync_dir__sync $s $t + v rsync_dir__sync $s $t } function install_dir__skip_existed(){ # NOTE: Do not add prefix `v` or `x` when using this function @@ -150,7 +150,7 @@ function install_dir__sync_exclude(){ if [ -d $t ];then warning_overwrite fi - rsync_dir__sync_exclude $s $t "$@" + v rsync_dir__sync_exclude $s $t "$@" } function install_google_sans_flex(){ local font_name="Google Sans Flex" @@ -170,7 +170,7 @@ function install_google_sans_flex(){ x fc-cache -fv x cd $REPO_ROOT x mkdir -p "$(dirname ${INSTALLED_LISTFILE})" - realpath -se "$2" >> "${INSTALLED_LISTFILE}" + realpath -se "$target_dir" >> "${INSTALLED_LISTFILE}" } ##################################################################################### diff --git a/sdata/subcmd-uninstall/0.run.sh b/sdata/subcmd-uninstall/0.run.sh index 380da51a7..ce66cf554 100644 --- a/sdata/subcmd-uninstall/0.run.sh +++ b/sdata/subcmd-uninstall/0.run.sh @@ -45,7 +45,12 @@ function delete_targets(){ printf "${STY_YELLOW}Target \"$path\" inexists, skipping...${STY_RST}\n" continue elif [[ "$path" == "$HOME"* ]]; then - x rm -- "$path" + if [[ -d "$path" ]]; then + x rm -r -- "$path" + else + x rm -- "$path" + fi + else while true; do printf "WARNING: Target \"$path\" is not under \$HOME. Still delete it?\ny=Yes, delete it;\nn=No, skip this one\n" @@ -53,7 +58,11 @@ function delete_targets(){ echo case "$ans" in y|Y) - x rm -- "$path" + if [[ -d "$path" ]]; then + x rm -r -- "$path" + else + x rm -- "$path" + fi break 1 ;; n|N)