Merge branch 'end-4:main' into parallax

This commit is contained in:
Ivan Rosinskii
2026-01-02 15:47:43 +01:00
committed by GitHub
49 changed files with 977 additions and 676 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
# Auto start Hyprland on tty1 # Auto start Hyprland on tty1
if test -z "$DISPLAY" ;and test "$XDG_VTNR" -eq 1 if test -z "$DISPLAY" ;and test "$XDG_VTNR" -eq 1
mkdir -p ~/.cache mkdir -p ~/.cache
exec Hyprland > ~/.cache/hyprland.log 2>&1 exec start-hyprland > ~/.cache/hyprland.log 2>&1
end end
+2 -2
View File
@@ -5,7 +5,7 @@
# ######## Window rules ######## # ######## Window rules ########
# Uncomment to apply global transparency to all windows: # 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 # Disable blur for all xwayland apps
# windowrulev2 = noblur, xwayland:1 # windowrule = no_blur on, match:xwayland 1
+1 -1
View File
@@ -31,4 +31,4 @@ plugin {
} }
} }
windowrulev2 = bordercolor rgba(FFB2BCAA) rgba(FFB2BC77),pinned:1 windowrule = border_color rgba(FFB2BCAA) rgba(FFB2BC77), match:pin 1
+14 -11
View File
@@ -22,7 +22,7 @@ general {
gaps_workspaces = 50 gaps_workspaces = 50
border_size = 1 border_size = 1
col.active_border = rgba(0DB7D4FF) col.active_border = rgba(0DB7D455)
col.inactive_border = rgba(31313600) col.inactive_border = rgba(31313600)
resize_on_border = true resize_on_border = true
@@ -31,10 +31,10 @@ general {
allow_tearing = true # This just allows the `immediate` window rule to work allow_tearing = true # This just allows the `immediate` window rule to work
snap { snap {
enabled = true enabled = true
window_gap = 4 window_gap = 4
monitor_gap = 5 monitor_gap = 5
respect_gaps = true respect_gaps = true
} }
} }
@@ -46,6 +46,9 @@ dwindle {
} }
decoration { 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.4
rounding = 18 rounding = 18
blur { blur {
@@ -69,15 +72,15 @@ decoration {
shadow { shadow {
enabled = true enabled = true
ignore_window = true ignore_window = true
range = 30 range = 50
offset = 0 2 offset = 0 4
render_power = 4 render_power = 10
color = rgba(00000010) color = rgba(00000027)
} }
# Dim # Dim
dim_inactive = true dim_inactive = true
dim_strength = 0.025 dim_strength = 0.05
dim_special = 0.07 dim_special = 0.07
} }
@@ -142,7 +145,7 @@ misc {
animate_mouse_windowdragging = false animate_mouse_windowdragging = false
enable_swallow = false enable_swallow = false
swallow_regex = (foot|kitty|allacritty|Alacritty) swallow_regex = (foot|kitty|allacritty|Alacritty)
new_window_takes_over_fullscreen = 2 on_focus_under_fullscreen = 2
allow_session_lock_restore = true allow_session_lock_restore = true
session_lock_xray = true session_lock_xray = true
initial_workspace_tracking = false initial_workspace_tracking = false
+132 -132
View File
@@ -1,165 +1,165 @@
# ######## Window rules ######## # ######## Window rules ########
# Disable blur for xwayland context menus # Disable blur for xwayland context menus
windowrulev2 = noblur,class:^()$,title:^()$ windowrule = match:class ^()$, match:title ^()$, no_blur on
# Disable blur for every window # Disable blur for every window
windowrulev2 = noblur, class:.* windowrule = match:class .*, no_blur on
# Floating # Floating
windowrulev2 = center, title:^(Open File)(.*)$ windowrule = match:title ^(Open File)(.*)$, center on
windowrulev2 = float, title:^(Open File)(.*)$ windowrule = match:title ^(Open File)(.*)$, float on
windowrulev2 = center, title:^(Select a File)(.*)$ windowrule = match:title ^(Select a File)(.*)$, center on
windowrulev2 = float, title:^(Select a File)(.*)$ windowrule = match:title ^(Select a File)(.*)$, float on
windowrulev2 = center, title:^(Choose wallpaper)(.*)$ windowrule = match:title ^(Choose wallpaper)(.*)$, center on
windowrulev2 = float, title:^(Choose wallpaper)(.*)$ windowrule = match:title ^(Choose wallpaper)(.*)$, float on
windowrulev2 = size 60% 65%, title:^(Choose wallpaper)(.*)$ windowrule = match:title ^(Choose wallpaper)(.*)$, size (monitor_w*.60) (monitor_h*.65)
windowrulev2 = center, title:^(Open Folder)(.*)$ windowrule = match:title ^(Open Folder)(.*)$, center on
windowrulev2 = float, title:^(Open Folder)(.*)$ windowrule = match:title ^(Open Folder)(.*)$, float on
windowrulev2 = center, title:^(Save As)(.*)$ windowrule = match:title ^(Save As)(.*)$, center on
windowrulev2 = float, title:^(Save As)(.*)$ windowrule = match:title ^(Save As)(.*)$, float on
windowrulev2 = center, title:^(Library)(.*)$ windowrule = match:title ^(Library)(.*)$, center on
windowrulev2 = float, title:^(Library)(.*)$ windowrule = match:title ^(Library)(.*)$, float on
windowrulev2 = center, title:^(File Upload)(.*)$ windowrule = match:title ^(File Upload)(.*)$, center on
windowrulev2 = float, title:^(File Upload)(.*)$ windowrule = match:title ^(File Upload)(.*)$, float on
windowrulev2 = center, title:^(.*)(wants to save)$ windowrule = match:title ^(.*)(wants to save)$, center on
windowrulev2 = float, title:^(.*)(wants to save)$ windowrule = match:title ^(.*)(wants to save)$, float on
windowrulev2 = center, title:^(.*)(wants to open)$ windowrule = match:title ^(.*)(wants to open)$, center on
windowrulev2 = float, title:^(.*)(wants to open)$ windowrule = match:title ^(.*)(wants to open)$, float on
windowrulev2 = float, class:^(blueberry\.py)$ windowrule = match:class ^(blueberry\.py)$, float on
windowrulev2 = float, class:^(guifetch)$ # FlafyDev/guifetch windowrule = match:class ^(guifetch)$ , float on # FlafyDev/guifetch
windowrulev2 = float, class:^(pavucontrol)$ windowrule = match:class ^(pavucontrol)$, float on
windowrulev2 = size 45%, class:^(pavucontrol)$ windowrule = match:class ^(pavucontrol)$, size (monitor_w*.45) (monitor_h*.45)
windowrulev2 = center, class:^(pavucontrol)$ windowrule = match:class ^(pavucontrol)$, center on
windowrulev2 = float, class:^(org.pulseaudio.pavucontrol)$ windowrule = match:class ^(org.pulseaudio.pavucontrol)$, float on
windowrulev2 = size 45%, class:^(org.pulseaudio.pavucontrol)$ windowrule = match:class ^(org.pulseaudio.pavucontrol)$, size (monitor_w*.45) (monitor_h*.45)
windowrulev2 = center, class:^(org.pulseaudio.pavucontrol)$ windowrule = match:class ^(org.pulseaudio.pavucontrol)$, center on
windowrulev2 = float, class:^(nm-connection-editor)$ windowrule = match:class ^(nm-connection-editor)$, float on
windowrulev2 = size 45%, class:^(nm-connection-editor)$ windowrule = match:class ^(nm-connection-editor)$, size (monitor_w*.45) (monitor_h*.45)
windowrulev2 = center, class:^(nm-connection-editor)$ windowrule = match:class ^(nm-connection-editor)$, center on
windowrulev2 = float, class:.*plasmawindowed.* windowrule = match:class .*plasmawindowed.*, float on
windowrulev2 = float, class:kcm_.* windowrule = match:class kcm_.*, float on
windowrulev2 = float, class:.*bluedevilwizard windowrule = match:class .*bluedevilwizard, float on
windowrulev2 = float, title:.*Welcome windowrule = match:title .*Welcome, float on
windowrulev2 = float, title:^(illogical-impulse Settings)$ windowrule = match:title ^(illogical-impulse Settings)$, float on
windowrulev2 = float, title:.*Shell conflicts.* windowrule = match:title .*Shell conflicts.*, float on
windowrulev2 = float, class:org.freedesktop.impl.portal.desktop.kde windowrule = match:class org.freedesktop.impl.portal.desktop.kde, float on
windowrulev2 = size 60% 65%, class:org.freedesktop.impl.portal.desktop.kde windowrule = match:class org.freedesktop.impl.portal.desktop.kde, size (monitor_w*.60) (monitor_h*.65)
windowrulev2 = float, class:^(Zotero)$ windowrule = match:class ^(Zotero)$, float on
windowrulev2 = size 45%, class:^(Zotero)$ windowrule = match:class ^(Zotero)$, size (monitor_w*.45) (monitor_h*.45)
# Move # Move
# kde-material-you-colors spawns a window when changing dark/light theme. This is to make sure it doesn't interfere at all. # 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)$ windowrule = match:class ^(plasma-changeicons)$, float on
windowrulev2 = noinitialfocus, class:^(plasma-changeicons)$ windowrule = match:class ^(plasma-changeicons)$, no_initial_focus on
windowrulev2 = move 999999 999999, class:^(plasma-changeicons)$ windowrule = match:class ^(plasma-changeicons)$, move 999999 999999
# stupid dolphin copy # stupid dolphin copy
windowrulev2 = move 40 80, title:^(Copying — Dolphin)$ windowrule = match:title ^(Copying — Dolphin)$, move 40 80
# Tiling # Tiling
windowrulev2 = tile, class:^dev\.warp\.Warp$ windowrule = match:class ^dev\.warp\.Warp$, tile on
# Picture-in-Picture # Picture-in-Picture
windowrulev2 = float, title:^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$ windowrule = match:title ^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$, float on
windowrulev2 = keepaspectratio, title:^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$ windowrule = match:title ^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$, keep_aspect_ratio on
windowrulev2 = move 73% 72%, title:^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$ windowrule = match:title ^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$, move (monitor_w*.73) (monitor_h*.72)
windowrulev2 = size 25%, title:^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$ windowrule = match:title ^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$, size (monitor_w*.25) (monitor_h*.25)
windowrulev2 = float, title:^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$ windowrule = match:title ^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$, float on
windowrulev2 = pin, title:^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$ windowrule = match:title ^([Pp]icture[-\s]?[Ii]n[-\s]?[Pp]icture)(.*)$, pin on
# --- Tearing --- # --- Tearing ---
windowrulev2 = immediate, title:.*\.exe windowrule = match:title .*\.exe, immediate on
windowrulev2 = immediate, title:.*minecraft.* windowrule = match:title .*minecraft.*, immediate on
windowrulev2 = immediate, class:^(steam_app).* windowrule = match:class ^(steam_app).*, immediate on
# Fix Jetbrain IDEs focus/rerendering problem # 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). # 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 rules ########
workspace = special:special, gapsout:30 workspace = special:special, gapsout:30
# ######## Layer rules ######## # ######## Layer rules ########
layerrule = xray 1, .* layerrule = match:namespace .*, xray on
# layerrule = noanim, .* # layerrule = match:namespace .*, no_anim on
layerrule = noanim, walker layerrule = match:namespace walker, no_anim on
layerrule = noanim, selection layerrule = match:namespace selection, no_anim on
layerrule = noanim, overview layerrule = match:namespace overview, no_anim on
layerrule = noanim, anyrun layerrule = match:namespace anyrun, no_anim on
layerrule = noanim, indicator.* layerrule = match:namespace indicator.*, no_anim on
layerrule = noanim, osk layerrule = match:namespace osk, no_anim on
layerrule = noanim, hyprpicker layerrule = match:namespace hyprpicker, no_anim on
layerrule = noanim, noanim layerrule = match:namespace noanim, no_anim on
layerrule = blur, gtk-layer-shell layerrule = match:namespace gtk-layer-shell, blur on
layerrule = ignorezero, gtk-layer-shell layerrule = match:namespace gtk-layer-shell, ignore_alpha 0
layerrule = blur, launcher layerrule = match:namespace launcher, blur on
layerrule = ignorealpha 0.5, launcher layerrule = match:namespace launcher, ignore_alpha 0.5
layerrule = blur, notifications layerrule = match:namespace notifications, blur on
layerrule = ignorealpha 0.69, notifications layerrule = match:namespace notifications, ignore_alpha 0.69
layerrule = blur, logout_dialog # wlogout layerrule = match:namespace logout_dialog # wlogout, blur on
# ags # ags
layerrule = animation slide left, sideleft.* layerrule = match:namespace sideleft.*, animation slide left
layerrule = animation slide right, sideright.* layerrule = match:namespace sideright.*, animation slide right
layerrule = blur, session[0-9]* layerrule = match:namespace session[0-9]*, blur on
layerrule = blur, bar[0-9]* layerrule = match:namespace bar[0-9]*, blur on
layerrule = ignorealpha 0.6, bar[0-9]* layerrule = match:namespace bar[0-9]*, ignore_alpha 0.6
layerrule = blur, barcorner.* layerrule = match:namespace barcorner.*, blur on
layerrule = ignorealpha 0.6, barcorner.* layerrule = match:namespace barcorner.*, ignore_alpha 0.6
layerrule = blur, dock[0-9]* layerrule = match:namespace dock[0-9]*, blur on
layerrule = ignorealpha 0.6, dock[0-9]* layerrule = match:namespace dock[0-9]*, ignore_alpha 0.6
layerrule = blur, indicator.* layerrule = match:namespace indicator.*, blur on
layerrule = ignorealpha 0.6, indicator.* layerrule = match:namespace indicator.*, ignore_alpha 0.6
layerrule = blur, overview[0-9]* layerrule = match:namespace overview[0-9]*, blur on
layerrule = ignorealpha 0.6, overview[0-9]* layerrule = match:namespace overview[0-9]*, ignore_alpha 0.6
layerrule = blur, cheatsheet[0-9]* layerrule = match:namespace cheatsheet[0-9]*, blur on
layerrule = ignorealpha 0.6, cheatsheet[0-9]* layerrule = match:namespace cheatsheet[0-9]*, ignore_alpha 0.6
layerrule = blur, sideright[0-9]* layerrule = match:namespace sideright[0-9]*, blur on
layerrule = ignorealpha 0.6, sideright[0-9]* layerrule = match:namespace sideright[0-9]*, ignore_alpha 0.6
layerrule = blur, sideleft[0-9]* layerrule = match:namespace sideleft[0-9]*, blur on
layerrule = ignorealpha 0.6, sideleft[0-9]* layerrule = match:namespace sideleft[0-9]*, ignore_alpha 0.6
layerrule = blur, indicator.* layerrule = match:namespace indicator.*, blur on
layerrule = ignorealpha 0.6, indicator.* layerrule = match:namespace indicator.*, ignore_alpha 0.6
layerrule = blur, osk[0-9]* layerrule = match:namespace osk[0-9]*, blur on
layerrule = ignorealpha 0.6, osk[0-9]* layerrule = match:namespace osk[0-9]*, ignore_alpha 0.6
# Quickshell # Quickshell
layerrule = blurpopups, quickshell:.* layerrule = match:namespace quickshell:.*, blur_popups on
layerrule = blur, quickshell:.* layerrule = match:namespace quickshell:.*, blur on
layerrule = ignorealpha 0.79, quickshell:.* layerrule = match:namespace quickshell:.*, ignore_alpha 0.79
layerrule = animation slide, quickshell:bar layerrule = match:namespace quickshell:bar, animation slide
layerrule = noanim, quickshell:actionCenter layerrule = match:namespace quickshell:actionCenter, no_anim on
layerrule = animation slide bottom, quickshell:cheatsheet layerrule = match:namespace quickshell:cheatsheet, animation slide bottom
layerrule = animation slide bottom, quickshell:dock layerrule = match:namespace quickshell:dock, animation slide bottom
layerrule = animation popin 120%, quickshell:screenCorners layerrule = match:namespace quickshell:screenCorners, animation popin 120%
layerrule = noanim, quickshell:lockWindowPusher layerrule = match:namespace quickshell:lockWindowPusher, no_anim on
layerrule = animation fade, quickshell:notificationPopup layerrule = match:namespace quickshell:notificationPopup, animation fade
layerrule = noanim, quickshell:overlay layerrule = match:namespace quickshell:overlay, no_anim on
layerrule = ignorealpha 1, quickshell:overlay layerrule = match:namespace quickshell:overlay, ignore_alpha 1
layerrule = noanim, quickshell:overview layerrule = match:namespace quickshell:overview, no_anim on
layerrule = animation slide bottom, quickshell:osk layerrule = match:namespace quickshell:osk, animation slide bottom
layerrule = noanim, quickshell:polkit layerrule = match:namespace quickshell:polkit, no_anim on
layerrule = xray 0, quickshell:popup # No weird color for bar tooltips (this in theory should suffice) layerrule = match:namespace quickshell:popup, xray off # 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 = match:namespace quickshell:popup, ignore_alpha 1 # No weird color for bar tooltips (but somehow this is necessary)
layerrule = ignorealpha 1, quickshell:mediaControls # Same as above layerrule = match:namespace quickshell:mediaControls, ignore_alpha 1 # Same as above
layerrule = animation slide, quickshell:reloadPopup layerrule = match:namespace quickshell:reloadPopup, animation slide
layerrule = noanim, quickshell:regionSelector layerrule = match:namespace quickshell:regionSelector, no_anim on
layerrule = noanim, quickshell:screenshot layerrule = match:namespace quickshell:screenshot, no_anim on
layerrule = blur, quickshell:session layerrule = match:namespace quickshell:session, blur on
layerrule = noanim, quickshell:session layerrule = match:namespace quickshell:session, no_anim on
layerrule = ignorealpha 0, quickshell:session layerrule = match:namespace quickshell:session, ignore_alpha 0
layerrule = animation slide right, quickshell:sidebarRight layerrule = match:namespace quickshell:sidebarRight, animation slide right
layerrule = animation slide left, quickshell:sidebarLeft layerrule = match:namespace quickshell:sidebarLeft, animation slide left
layerrule = animation slide, quickshell:verticalBar layerrule = match:namespace quickshell:verticalBar, animation slide
layerrule = animation slide top, quickshell:wallpaperSelector layerrule = match:namespace quickshell:wallpaperSelector, animation slide top
layerrule = noanim, quickshell:wNotificationCenter layerrule = match:namespace quickshell:wNotificationCenter, no_anim on
layerrule = noanim, quickshell:wOnScreenDisplay layerrule = match:namespace quickshell:wOnScreenDisplay, no_anim on
layerrule = noanim, quickshell:wStartMenu layerrule = match:namespace quickshell:wStartMenu, no_anim on
layerrule = ignorealpha 0, quickshell:wTaskView layerrule = match:namespace quickshell:wTaskView, ignore_alpha 0
layerrule = noanim, quickshell:wTaskView layerrule = match:namespace quickshell:wTaskView, no_anim on
# Launchers need to be FAST # Launchers need to be FAST
layerrule = noanim, gtk4-layer-shell layerrule = match:namespace gtk4-layer-shell, no_anim on
@@ -1,6 +1,6 @@
general { general {
col.active_border = rgba({{colors.outline.default.hex_stripped}}AA) col.active_border = rgba({{colors.outline.default.hex_stripped}}77)
col.inactive_border = rgba({{colors.outline_variant.default.hex_stripped}}AA) col.inactive_border = rgba({{colors.outline_variant.default.hex_stripped}}55)
} }
misc { 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
@@ -28,7 +28,7 @@ Singleton {
property real autoBackgroundTransparency: { // y = 0.5768x^2 - 0.759x + 0.2896 property real autoBackgroundTransparency: { // y = 0.5768x^2 - 0.759x + 0.2896
let x = wallpaperVibrancy let x = wallpaperVibrancy
let y = 0.5768 * (x * x) - 0.759 * (x) + 0.2896 let y = 0.5768 * (x * x) - 0.759 * (x) + 0.2896
return Math.max(0, Math.min(0.22, y)) return Math.max(0, Math.min(0.22, y)) - 0.12 * (m3colors.darkmode ? 0 : 1)
} }
property real autoContentTransparency: 0.9 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 backgroundTransparency: Config?.options.appearance.transparency.enable ? Config?.options.appearance.transparency.automatic ? autoBackgroundTransparency : Config?.options.appearance.transparency.backgroundTransparency : 0
@@ -78,10 +78,7 @@ Singleton {
JsonAdapter { JsonAdapter {
id: configOptionsJsonAdapter id: configOptionsJsonAdapter
property list<string> enabledPanels: [ property string panelFamily: "ii" // "ii", "waffle"
"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 JsonObject policies: JsonObject { property JsonObject policies: JsonObject {
property int ai: 1 // 0: No | 1: Yes | 2: Local property int ai: 1 // 0: No | 1: Yes | 2: Local
@@ -189,7 +186,17 @@ Singleton {
property bool useSineCookie: false property bool useSineCookie: false
} }
property JsonObject digital: JsonObject { property JsonObject digital: JsonObject {
property bool adaptiveAlignment: true
property bool showDate: true
property bool animateChange: 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 JsonObject quote: JsonObject {
property bool enable: false property bool enable: false
@@ -59,7 +59,7 @@ Singleton {
property string hyprlandInstanceSignature: "" property string hyprlandInstanceSignature: ""
property JsonObject ai: JsonObject { property JsonObject ai: JsonObject {
property string model property string model: "gemini-2.5-flash"
property real temperature: 0.5 property real temperature: 0.5
} }
@@ -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
}
}
@@ -32,7 +32,7 @@ Item {
Rectangle { // The dialog Rectangle { // The dialog
id: dialog id: dialog
color: Appearance.colors.colSurfaceContainerHigh color: Appearance.m3colors.m3surfaceContainerHigh
radius: Appearance.rounding.normal radius: Appearance.rounding.normal
anchors.fill: parent anchors.fill: parent
anchors.margins: dialogMargin anchors.margins: dialogMargin
@@ -192,7 +192,7 @@ ComboBox {
id: popupBackground id: popupBackground
anchors.fill: parent anchors.fill: parent
radius: Appearance.rounding.normal radius: Appearance.rounding.normal
color: Appearance.colors.colSurfaceContainerHigh color: Appearance.m3colors.m3surfaceContainerHigh
} }
} }
@@ -46,7 +46,8 @@ Slider {
property real handleWidth: root.pressed ? handlePressedWidth : handleDefaultWidth property real handleWidth: root.pressed ? handlePressedWidth : handleDefaultWidth
property real handleMargins: 4 property real handleMargins: 4
property real trackDotSize: 3 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 wavy: configuration === StyledSlider.Configuration.Wavy // If true, the progress bar will have a wavy fill effect
property bool animateWave: true property bool animateWave: true
property real waveAmplitudeMultiplier: wavy ? 0.5 : 0 property real waveAmplitudeMultiplier: wavy ? 0.5 : 0
@@ -59,7 +59,8 @@ AbstractWidget {
function onReadyChanged() { refreshPlacementIfNeeded() } function onReadyChanged() { refreshPlacementIfNeeded() }
} }
function 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.wallpaperPath = root.wallpaperPath;
leastBusyRegionProc.running = false; leastBusyRegionProc.running = false;
leastBusyRegionProc.running = true; leastBusyRegionProc.running = true;
@@ -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
}
@@ -26,7 +26,7 @@ AbstractBackgroundWidget {
visibleWhenLocked: true visibleWhenLocked: true
property var textHorizontalAlignment: { 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; return Text.AlignHCenter;
if (root.x < root.scaledScreenWidth / 3) if (root.x < root.scaledScreenWidth / 3)
return Text.AlignLeft; return Text.AlignLeft;
@@ -63,32 +63,9 @@ AbstractBackgroundWidget {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
shown: root.clockStyle === "digital" && (root.shouldShow) shown: root.clockStyle === "digital" && (root.shouldShow)
fade: false fade: false
sourceComponent: ColumnLayout { sourceComponent: DigitalClock {
id: clockColumn colText: root.colText
spacing: 6 textHorizontalAlignment: root.textHorizontalAlignment
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
}
} }
} }
StatusRow { 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 { component ClockStatusText: Row {
id: statusTextRow id: statusTextRow
property alias statusIcon: statusIconWidget.text property alias statusIcon: statusIconWidget.text
@@ -190,6 +154,7 @@ AbstractBackgroundWidget {
ClockText { ClockText {
id: statusTextWidget id: statusTextWidget
color: statusTextRow.textColor color: statusTextRow.textColor
horizontalAlignment: root.textHorizontalAlignment
anchors.verticalCenter: statusTextRow.verticalCenter anchors.verticalCenter: statusTextRow.verticalCenter
font { font {
pixelSize: Appearance.font.pixelSize.large pixelSize: Appearance.font.pixelSize.large
@@ -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
}
}
@@ -1,3 +1,5 @@
pragma ComponentBehavior: Bound
import QtQuick import QtQuick
import Quickshell import Quickshell
import Quickshell.Io import Quickshell.Io
@@ -60,6 +62,7 @@ Scope {
} }
color: "transparent" color: "transparent"
// Positioning
anchors { anchors {
top: !Config.options.bar.bottom top: !Config.options.bar.bottom
bottom: Config.options.bar.bottom bottom: Config.options.bar.bottom
@@ -72,6 +75,14 @@ Scope {
bottom: (Config.options.interactions.deadPixelWorkaround.enable && barRoot.anchors.bottom) * -1 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 { MouseArea {
id: hoverRegion id: hoverRegion
hoverEnabled: true hoverEnabled: true
@@ -54,13 +54,16 @@ Scope { // Scope
item: cheatsheetBackground item: cheatsheetBackground
} }
HyprlandFocusGrab { // Click outside to close Component.onCompleted: {
id: grab GlobalFocusGrab.addDismissable(cheatsheetRoot);
windows: [cheatsheetRoot] }
active: cheatsheetRoot.visible Component.onDestruction: {
onCleared: () => { GlobalFocusGrab.removeDismissable(cheatsheetRoot);
if (!active) }
cheatsheetRoot.hide(); Connections {
target: GlobalFocusGrab
function onDismissed() {
cheatsheetRoot.hide();
} }
} }
@@ -6,8 +6,6 @@ import qs.modules.common.functions
import qs.modules.common.panels.lock import qs.modules.common.panels.lock
import QtQuick import QtQuick
import Quickshell import Quickshell
import Quickshell.Io
import Quickshell.Wayland
import Quickshell.Hyprland import Quickshell.Hyprland
LockScreen { LockScreen {
@@ -18,24 +16,7 @@ LockScreen {
} }
// Push everything down // Push everything down
property var windowData: [] property var lastWorkspaceId: 1
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"]);
}
Variants { Variants {
model: Quickshell.screens model: Quickshell.screens
delegate: Scope { delegate: Scope {
@@ -46,11 +27,11 @@ LockScreen {
property int horizontalSqueeze: modelData.width * 0.2 property int horizontalSqueeze: modelData.width * 0.2
onShouldPushChanged: { onShouldPushChanged: {
if (shouldPush) { if (shouldPush) {
root.saveWindowPositionAndTile(); root.lastWorkspaceId = HyprlandData.activeWorkspace.id;
Quickshell.execDetached(["bash", "-c", `hyprctl keyword monitor ${targetMonitorName}, addreserved, ${verticalMovementDistance}, ${-verticalMovementDistance}, ${horizontalSqueeze}, ${horizontalSqueeze}`]); // Set anim to vertical and move to very very big workspace for a sliding up effect
Quickshell.execDetached(["hyprctl", "--batch", "keyword animation workspaces,1,7,menu_decel,slidevert; dispatch workspace 2147483647"]);
} else { } else {
Quickshell.execDetached(["bash", "-c", `hyprctl keyword monitor ${targetMonitorName}, addreserved, 0, 0, 0, 0`]); Quickshell.execDetached(["hyprctl", "--batch", `dispatch workspace ${root.lastWorkspaceId}; reload`]);
root.restoreWindowPositionAndTile();
} }
} }
} }
@@ -81,7 +81,7 @@ Scope {
} }
sourceComponent: PanelWindow { sourceComponent: PanelWindow {
id: mediaControlsRoot id: panelWindow
visible: true visible: true
exclusionMode: ExclusionMode.Ignore exclusionMode: ExclusionMode.Ignore
@@ -98,9 +98,9 @@ Scope {
right: Config.options.bar.vertical && Config.options.bar.bottom right: Config.options.bar.vertical && Config.options.bar.bottom
} }
margins { 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 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 right: Appearance.sizes.barHeight
} }
@@ -108,13 +108,16 @@ Scope {
item: playerColumnLayout item: playerColumnLayout
} }
HyprlandFocusGrab { Component.onCompleted: {
windows: [mediaControlsRoot] GlobalFocusGrab.addDismissable(panelWindow);
active: mediaControlsLoader.active }
onCleared: () => { Component.onDestruction: {
if (!active) { GlobalFocusGrab.removeDismissable(panelWindow);
GlobalStates.mediaControlsOpen = false; }
} Connections {
target: GlobalFocusGrab
function onDismissed() {
GlobalStates.mediaControlsOpen = false;
} }
} }
@@ -137,10 +140,13 @@ Scope {
} }
} }
Item { // No player placeholder Item {
// No player placeholder
Layout.alignment: { Layout.alignment: {
if (mediaControlsRoot.anchors.left) return Qt.AlignLeft; if (panelWindow.anchors.left)
if (mediaControlsRoot.anchors.right) return Qt.AlignRight; return Qt.AlignLeft;
if (panelWindow.anchors.right)
return Qt.AlignRight;
return Qt.AlignHCenter; return Qt.AlignHCenter;
} }
Layout.leftMargin: Appearance.sizes.hyprlandGapsOut Layout.leftMargin: Appearance.sizes.hyprlandGapsOut
@@ -57,6 +57,13 @@ Scope { // Scope
item: oskBackground item: oskBackground
} }
// Make it usable with other panels
Component.onCompleted: {
GlobalFocusGrab.addPersistent(oskRoot);
}
Component.onDestruction: {
GlobalFocusGrab.removePersistent(oskRoot);
}
// Background // Background
StyledRectangularShadow { StyledRectangularShadow {
@@ -14,116 +14,96 @@ import Quickshell.Hyprland
Scope { Scope {
id: overviewScope id: overviewScope
property bool dontAutoCancelSearch: false property bool dontAutoCancelSearch: false
Variants {
id: overviewVariants PanelWindow {
model: Quickshell.screens id: panelWindow
PanelWindow { property string searchingText: ""
id: root readonly property HyprlandMonitor monitor: Hyprland.monitorFor(panelWindow.screen)
required property var modelData property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor?.id)
property string searchingText: "" visible: GlobalStates.overviewOpen
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen)
property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor?.id) WlrLayershell.namespace: "quickshell:overview"
screen: modelData 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 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 { anchors {
top: true horizontalCenter: parent.horizontalCenter
bottom: true top: parent.top
left: true
right: true
} }
spacing: -8
HyprlandFocusGrab { Keys.onPressed: event => {
id: grab if (event.key === Qt.Key_Escape) {
windows: [root] GlobalStates.overviewOpen = false;
property bool canBeActive: root.monitorIsFocused } else if (event.key === Qt.Key_Left) {
active: false if (!panelWindow.searchingText)
onCleared: () => { Hyprland.dispatch("workspace r-1");
if (!active) } else if (event.key === Qt.Key_Right) {
GlobalStates.overviewOpen = false; if (!panelWindow.searchingText)
Hyprland.dispatch("workspace r+1");
} }
} }
Connections { SearchWidget {
target: GlobalStates id: searchWidget
function onOverviewOpenChanged() { anchors.horizontalCenter: parent.horizontalCenter
if (!GlobalStates.overviewOpen) { Synchronizer on searchingText {
searchWidget.disableExpandAnimation(); property alias source: panelWindow.searchingText
overviewScope.dontAutoCancelSearch = false;
} else {
if (!overviewScope.dontAutoCancelSearch) {
searchWidget.cancelSearch();
}
delayedGrabTimer.start();
}
} }
} }
Timer { Loader {
id: delayedGrabTimer id: overviewLoader
interval: Config.options.hacks.arbitraryRaceConditionDelay anchors.horizontalCenter: parent.horizontalCenter
repeat: false active: GlobalStates.overviewOpen && (Config?.options.overview.enable ?? true)
onTriggered: { sourceComponent: OverviewWidget {
if (!grab.canBeActive) screen: panelWindow.screen
return; visible: (panelWindow.searchingText == "")
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 == "")
}
} }
} }
} }
@@ -134,15 +114,9 @@ Scope {
GlobalStates.overviewOpen = false; GlobalStates.overviewOpen = false;
return; return;
} }
for (let i = 0; i < overviewVariants.instances.length; i++) { overviewScope.dontAutoCancelSearch = true;
let panelWindow = overviewVariants.instances[i]; panelWindow.setSearchingText(Config.options.search.prefix.clipboard);
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { GlobalStates.overviewOpen = true;
overviewScope.dontAutoCancelSearch = true;
panelWindow.setSearchingText(Config.options.search.prefix.clipboard);
GlobalStates.overviewOpen = true;
return;
}
}
} }
function toggleEmojis() { function toggleEmojis() {
@@ -150,15 +124,9 @@ Scope {
GlobalStates.overviewOpen = false; GlobalStates.overviewOpen = false;
return; return;
} }
for (let i = 0; i < overviewVariants.instances.length; i++) { overviewScope.dontAutoCancelSearch = true;
let panelWindow = overviewVariants.instances[i]; panelWindow.setSearchingText(Config.options.search.prefix.emojis);
if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { GlobalStates.overviewOpen = true;
overviewScope.dontAutoCancelSearch = true;
panelWindow.setSearchingText(Config.options.search.prefix.emojis);
GlobalStates.overviewOpen = true;
return;
}
}
} }
IpcHandler { IpcHandler {
@@ -13,8 +13,8 @@ import Quickshell.Hyprland
Item { Item {
id: root id: root
required property var panelWindow required property var screen
readonly property HyprlandMonitor monitor: Hyprland.monitorFor(panelWindow.screen) readonly property HyprlandMonitor monitor: Hyprland.monitorFor(screen)
readonly property var toplevels: ToplevelManager.toplevels readonly property var toplevels: ToplevelManager.toplevels
readonly property int workspacesShown: Config.options.overview.rows * Config.options.overview.columns 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((monitor.activeWorkspace?.id - 1) / workspacesShown)
@@ -13,11 +13,15 @@ import Quickshell.Io
Item { // Wrapper Item { // Wrapper
id: root id: root
readonly property string xdgConfigHome: Directories.config 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 string searchingText: LauncherSearch.query
property bool showResults: searchingText != "" property bool showResults: searchingText != ""
implicitWidth: searchWidgetContent.implicitWidth + Appearance.sizes.elevationMargin * 2 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() { function focusFirstItem() {
appResults.currentIndex = 0; appResults.currentIndex = 0;
@@ -178,30 +182,35 @@ Item { // Wrapper
} }
} }
model: ScriptModel { Timer {
id: model id: debounceTimer
objectProp: "key" interval: root.typingDebounceInterval
values: LauncherSearch.results onTriggered: {
onValuesChanged: { resultModel.values = LauncherSearch.results ?? [];
root.focusFirstItem();
} }
} }
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 { delegate: SearchItem {
// The selectable item for each search result // The selectable item for each search result
required property var modelData required property var modelData
anchors.left: parent?.left anchors.left: parent?.left
anchors.right: parent?.right anchors.right: parent?.right
entry: modelData entry: modelData
query: StringUtils.cleanOnePrefix(root.searchingText, [ 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])
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
])
} }
} }
} }
@@ -84,11 +84,11 @@ Scope { // Scope
active: true active: true
sourceComponent: PanelWindow { // Window sourceComponent: PanelWindow { // Window
id: sidebarRoot id: panelWindow
visible: GlobalStates.sidebarLeftOpen visible: GlobalStates.sidebarLeftOpen
property bool extend: false 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 property var contentParent: sidebarLeftBackground
function hide() { function hide() {
@@ -113,15 +113,17 @@ Scope { // Scope
item: sidebarLeftBackground item: sidebarLeftBackground
} }
HyprlandFocusGrab { // Click outside to close onVisibleChanged: {
id: grab if (visible) {
windows: [ sidebarRoot ] GlobalFocusGrab.addDismissable(panelWindow);
active: sidebarRoot.visible && !root.pin } else {
onActiveChanged: { // Focus the selected tab GlobalFocusGrab.removeDismissable(panelWindow);
if (active) sidebarLeftBackground.children[0].focusActiveItem()
} }
onCleared: () => { }
if (!active) sidebarRoot.hide() Connections {
target: GlobalFocusGrab
function onDismissed() {
panelWindow.hide();
} }
} }
@@ -136,7 +138,7 @@ Scope { // Scope
anchors.left: parent.left anchors.left: parent.left
anchors.topMargin: Appearance.sizes.hyprlandGapsOut anchors.topMargin: Appearance.sizes.hyprlandGapsOut
anchors.leftMargin: 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 height: parent.height - Appearance.sizes.hyprlandGapsOut * 2
color: Appearance.colors.colLayer0 color: Appearance.colors.colLayer0
border.width: 1 border.width: 1
@@ -149,11 +151,11 @@ Scope { // Scope
Keys.onPressed: (event) => { Keys.onPressed: (event) => {
if (event.key === Qt.Key_Escape) { if (event.key === Qt.Key_Escape) {
sidebarRoot.hide(); panelWindow.hide();
} }
if (event.modifiers === Qt.ControlModifier) { if (event.modifiers === Qt.ControlModifier) {
if (event.key === Qt.Key_O) { if (event.key === Qt.Key_O) {
sidebarRoot.extend = !sidebarRoot.extend; panelWindow.extend = !panelWindow.extend;
} else if (event.key === Qt.Key_D) { } else if (event.key === Qt.Key_D) {
root.toggleDetach(); root.toggleDetach();
} else if (event.key === Qt.Key_P) { } else if (event.key === Qt.Key_P) {
@@ -126,7 +126,7 @@ Button {
opacity: root.showActions ? 1 : 0 opacity: root.showActions ? 1 : 0
visible: opacity > 0 visible: opacity > 0
radius: Appearance.rounding.small radius: Appearance.rounding.small
color: Appearance.colors.colSurfaceContainer color: Appearance.m3colors.m3surfaceContainer
implicitHeight: contextMenuColumnLayout.implicitHeight + radius * 2 implicitHeight: contextMenuColumnLayout.implicitHeight + radius * 2
implicitWidth: contextMenuColumnLayout.implicitWidth implicitWidth: contextMenuColumnLayout.implicitWidth
@@ -12,11 +12,11 @@ Scope {
property int sidebarWidth: Appearance.sizes.sidebarWidth property int sidebarWidth: Appearance.sizes.sidebarWidth
PanelWindow { PanelWindow {
id: sidebarRoot id: panelWindow
visible: GlobalStates.sidebarRightOpen visible: GlobalStates.sidebarRightOpen
function hide() { function hide() {
GlobalStates.sidebarRightOpen = false GlobalStates.sidebarRightOpen = false;
} }
exclusiveZone: 0 exclusiveZone: 0
@@ -32,12 +32,17 @@ Scope {
bottom: true bottom: true
} }
HyprlandFocusGrab { onVisibleChanged: {
id: grab if (visible) {
windows: [ sidebarRoot ] GlobalFocusGrab.addDismissable(panelWindow);
active: GlobalStates.sidebarRightOpen } else {
onCleared: () => { GlobalFocusGrab.removeDismissable(panelWindow);
if (!active) sidebarRoot.hide() }
}
Connections {
target: GlobalFocusGrab
function onDismissed() {
panelWindow.hide();
} }
} }
@@ -53,16 +58,14 @@ Scope {
height: parent.height - Appearance.sizes.hyprlandGapsOut * 2 height: parent.height - Appearance.sizes.hyprlandGapsOut * 2
focus: GlobalStates.sidebarRightOpen focus: GlobalStates.sidebarRightOpen
Keys.onPressed: (event) => { Keys.onPressed: event => {
if (event.key === Qt.Key_Escape) { if (event.key === Qt.Key_Escape) {
sidebarRoot.hide(); panelWindow.hide();
} }
} }
sourceComponent: SidebarRightContent {} sourceComponent: SidebarRightContent {}
} }
} }
IpcHandler { IpcHandler {
@@ -105,5 +108,4 @@ Scope {
GlobalStates.sidebarRightOpen = false; GlobalStates.sidebarRightOpen = false;
} }
} }
} }
@@ -139,7 +139,7 @@ Item {
anchors.margins: root.dialogMargins anchors.margins: root.dialogMargins
implicitHeight: dialogColumnLayout.implicitHeight implicitHeight: dialogColumnLayout.implicitHeight
color: Appearance.colors.colSurfaceContainerHigh color: Appearance.m3colors.m3surfaceContainerHigh
radius: Appearance.rounding.normal radius: Appearance.rounding.normal
function addTask() { function addTask() {
@@ -66,6 +66,7 @@ Scope {
} }
color: "transparent" color: "transparent"
// Positioning
anchors { anchors {
left: !Config.options.bar.bottom left: !Config.options.bar.bottom
right: Config.options.bar.bottom right: Config.options.bar.bottom
@@ -73,6 +74,14 @@ Scope {
bottom: true bottom: true
} }
// Include in focus grab
Component.onCompleted: {
GlobalFocusGrab.addPersistent(barRoot);
}
Component.onDestruction: {
GlobalFocusGrab.removePersistent(barRoot);
}
MouseArea { MouseArea {
id: hoverRegion id: hoverRegion
hoverEnabled: true hoverEnabled: true
@@ -39,12 +39,16 @@ Scope {
implicitHeight: Appearance.sizes.wallpaperSelectorHeight implicitHeight: Appearance.sizes.wallpaperSelectorHeight
implicitWidth: Appearance.sizes.wallpaperSelectorWidth implicitWidth: Appearance.sizes.wallpaperSelectorWidth
HyprlandFocusGrab { // Click outside to close Component.onCompleted: {
id: grab GlobalFocusGrab.addDismissable(panelWindow);
windows: [ panelWindow ] }
active: wallpaperSelectorLoader.active Component.onDestruction: {
onCleared: () => { GlobalFocusGrab.removeDismissable(panelWindow);
if (!active) GlobalStates.wallpaperSelectorOpen = false; }
Connections {
target: GlobalFocusGrab
function onDismissed() {
GlobalStates.wallpaperSelectorOpen = false;
} }
} }
@@ -53,9 +53,9 @@ ContentPage {
} }
ContentSection { ContentSection {
id: settingsClock
icon: "clock_loader_40" icon: "clock_loader_40"
title: Translation.tr("Widget: Clock") title: Translation.tr("Widget: Clock")
id: settingsClock
function stylePresent(styleName) { function stylePresent(styleName) {
if (!Config.options.background.widgets.clock.showOnlyWhenLocked && Config.options.background.widgets.clock.style === styleName) { if (!Config.options.background.widgets.clock.showOnlyWhenLocked && Config.options.background.widgets.clock.style === styleName) {
@@ -120,61 +120,161 @@ ContentPage {
} }
} }
ContentSubsection { ConfigRow {
visible: !Config.options.background.widgets.clock.showOnlyWhenLocked ContentSubsection {
title: Translation.tr("Clock style") visible: !Config.options.background.widgets.clock.showOnlyWhenLocked
ConfigSelectionArray { title: Translation.tr("Clock style")
currentValue: Config.options.background.widgets.clock.style Layout.fillWidth: true
onSelected: newValue => { ConfigSelectionArray {
Config.options.background.widgets.clock.style = newValue; currentValue: Config.options.background.widgets.clock.style
} onSelected: newValue => {
options: [ Config.options.background.widgets.clock.style = newValue;
{
displayName: Translation.tr("Digital"),
icon: "timer_10",
value: "digital"
},
{
displayName: Translation.tr("Cookie"),
icon: "cookie",
value: "cookie"
} }
] options: [
{
displayName: Translation.tr("Digital"),
icon: "timer_10",
value: "digital"
},
{
displayName: Translation.tr("Cookie"),
icon: "cookie",
value: "cookie"
}
]
}
} }
}
ContentSubsection { ContentSubsection {
title: Translation.tr("Clock style (locked)") title: Translation.tr("Clock style (locked)")
ConfigSelectionArray { Layout.fillWidth: false
currentValue: Config.options.background.widgets.clock.styleLocked ConfigSelectionArray {
onSelected: newValue => { currentValue: Config.options.background.widgets.clock.styleLocked
Config.options.background.widgets.clock.styleLocked = newValue; 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"
} }
] options: [
{
displayName: Translation.tr("Digital"),
icon: "timer_10",
value: "digital"
},
{
displayName: Translation.tr("Cookie"),
icon: "cookie",
value: "cookie"
}
]
}
} }
} }
ContentSubsection { ContentSubsection {
visible: settingsClock.digitalPresent visible: settingsClock.digitalPresent
title: Translation.tr("Digital clock settings") 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 { ConfigRow {
buttonIcon: "animation" uniform: true
text: Translation.tr("Animate time change") ConfigSwitch {
checked: Config.options.background.widgets.clock.digital.animateChange buttonIcon: "vertical_distribute"
onCheckedChanged: { text: Translation.tr("Vertical")
Config.options.background.widgets.clock.digital.animateChange = checked; 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: 70
to: 150
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;
} }
} }
} }
@@ -225,9 +225,6 @@ ContentPage {
onCheckedChanged: { onCheckedChanged: {
Config.options.appearance.transparency.enable = checked; Config.options.appearance.transparency.enable = checked;
} }
StyledToolTip {
text: Translation.tr("Might look ass. Unsupported.")
}
} }
} }
@@ -89,10 +89,11 @@ Singleton {
// Special // Special
property color shadow: ColorUtils.transparentize('#161616', 0.62) property color shadow: ColorUtils.transparentize('#161616', 0.62)
property color ambientShadow: ColorUtils.transparentize("#000000", 0.75) property color ambientShadow: ColorUtils.transparentize("#000000", 0.75)
property color bgPanelFooterBase: ColorUtils.transparentize(root.dark ? root.darkColors.bg0 : root.lightColors.bg0, root.panelBackgroundTransparency) property color bgPanelFooterBase: root.dark ? root.darkColors.bg0 : root.lightColors.bg0
property color bgPanelFooter: ColorUtils.transparentize(bgPanelFooterBase, root.panelLayerTransparency) 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 bgPanelBodyBase: root.dark ? root.darkColors.bgPanelBody : root.lightColors.bgPanelBody
property color bgPanelBody: ColorUtils.solveOverlayColor(bgPanelFooterBase,bgPanelBodyBase, 1 - root.panelLayerTransparency) 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) property color bgPanelSeparator: ColorUtils.solveOverlayColor(bgPanelBodyBase, root.dark ? root.darkColors.bgPanelSeparator : root.lightColors.bgPanelSeparator, 1 - root.panelBackgroundTransparency)
// Layer 0 // Layer 0
property color bg0Base: root.dark ? root.darkColors.bg0 : root.lightColors.bg0 property color bg0Base: root.dark ? root.darkColors.bg0 : root.lightColors.bg0
@@ -12,6 +12,7 @@ Item {
id: root id: root
property Item contentItem property Item contentItem
property real radius: Looks.radius.large property real radius: Looks.radius.large
property alias color: contentRect.color
property alias border: borderRect property alias border: borderRect
property alias borderColor: borderRect.border.color property alias borderColor: borderRect.border.color
property alias borderWidth: borderRect.border.width property alias borderWidth: borderRect.border.width
@@ -42,7 +43,7 @@ Item {
anchors.centerIn: parent anchors.centerIn: parent
z: 0 z: 0
color: Looks.colors.bgPanelFooterBase color: Looks.colors.bgPanelFooterBackground
implicitWidth: contentItem.implicitWidth implicitWidth: contentItem.implicitWidth
implicitHeight: contentItem.implicitHeight implicitHeight: contentItem.implicitHeight
layer.enabled: true layer.enabled: true
@@ -135,7 +135,7 @@ Rectangle {
} }
BodyRectangle { BodyRectangle {
implicitHeight: 80 implicitHeight: 80
color: Looks.colors.bgPanelFooterBase color: Looks.colors.bgPanelFooterBackground
RowLayout { RowLayout {
anchors.fill: parent anchors.fill: parent
anchors.margins: 24 anchors.margins: 24
@@ -18,7 +18,7 @@ Item {
Component.onCompleted: overlayColor = ColorUtils.transparentize("#000000", 0.4) Component.onCompleted: overlayColor = ColorUtils.transparentize("#000000", 0.4)
Behavior on overlayColor { Behavior on overlayColor {
ColorAnimation { ColorAnimation {
duration: 250 duration: 150
easing.type: Easing.InOutQuad easing.type: Easing.InOutQuad
} }
} }
@@ -231,7 +231,7 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
anchors.margins: wsBorder.border.width anchors.margins: wsBorder.border.width
radius: wsBorder.radius - wsBorder.border.width radius: wsBorder.radius - wsBorder.border.width
color: Looks.colors.bgPanelFooterBase color: Looks.colors.bgPanelFooterBackground
implicitHeight: 174 implicitHeight: 174
@@ -25,7 +25,7 @@ WMouseAreaButton {
property string iconName: AppSearch.guessIcon(hyprlandClient?.class) property string iconName: AppSearch.guessIcon(hyprlandClient?.class)
color: drag.active ? ColorUtils.transparentize(Looks.colors.bg1Base) : (containsMouse ? Looks.colors.bg1Base : Looks.colors.bgPanelFooterBase) 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) borderColor: ColorUtils.transparentize(Looks.colors.bg2Border, drag.active ? 1 : 0)
radius: Looks.radius.xLarge radius: Looks.radius.xLarge
@@ -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 {} }
}
@@ -0,0 +1,9 @@
import QtQuick
import Quickshell
import qs.modules.common
LazyLoader {
property bool extraCondition: true
active: Config.ready && extraCondition
}
@@ -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 {} }
}
+5 -44
View File
@@ -255,19 +255,6 @@ Singleton {
// - api_format: The API format of the model. Can be "openai" or "gemini". Default is "openai". // - 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. // - extraParams: Extra parameters to be passed to the model. This is a JSON object.
property var models: Config.options.policies.ai === 2 ? {} : { 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, { "gemini-2.5-flash": aiModelComponent.createObject(this, {
"name": "Gemini 2.5 Flash", "name": "Gemini 2.5 Flash",
"icon": "google-gemini-symbolic", "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"), "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", "api_format": "gemini",
}), }),
"gemini-2.5-flash-pro": aiModelComponent.createObject(this, { "gemini-3-flash": aiModelComponent.createObject(this, {
"name": "Gemini 2.5 Pro", "name": "Gemini 3 Flash",
"icon": "google-gemini-symbolic", "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", "homepage": "https://aistudio.google.com",
"endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-pro:streamGenerateContent", "endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-3-flash-preview:streamGenerateContent",
"model": "gemini-2.5-pro", "model": "gemini-3-flash-preview",
"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",
"requires_key": true, "requires_key": true,
"key_id": "gemini", "key_id": "gemini",
"key_get_link": "https://aistudio.google.com/app/apikey", "key_get_link": "https://aistudio.google.com/app/apikey",
@@ -320,19 +294,6 @@ Singleton {
"key_get_description": Translation.tr("**Instructions**: Log into Mistral account, go to Keys on the sidebar, click Create new key"), "key_get_description": Translation.tr("**Instructions**: Log into Mistral account, go to Keys on the sidebar, click Create new key"),
"api_format": "mistral", "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, { "openrouter-deepseek-r1": aiModelComponent.createObject(this, {
"name": "DeepSeek R1", "name": "DeepSeek R1",
"icon": "deepseek-symbolic", "icon": "deepseek-symbolic",
@@ -0,0 +1,64 @@
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<var> persistent: []
property list<var> 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);
}
}
HyprlandFocusGrab {
id: grab
windows: [...root.persistent, ...root.dismissable]
active: root.dismissable.length > 0
onCleared: () => {
root.dismiss();
}
}
}
+24 -82
View File
@@ -6,51 +6,22 @@
// Adjust this to make the shell smaller or larger // Adjust this to make the shell smaller or larger
//@ pragma Env QT_SCALE_FACTOR=1 //@ pragma Env QT_SCALE_FACTOR=1
import qs.modules.common import "modules/common"
import qs.modules.ii.background import "services"
import qs.modules.ii.bar import "panelFamilies"
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.notificationPopup
import qs.modules.waffle.onScreenDisplay
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
import QtQuick import QtQuick
import QtQuick.Window import QtQuick.Window
import Quickshell import Quickshell
import Quickshell.Io import Quickshell.Io
import Quickshell.Hyprland import Quickshell.Hyprland
import qs.services
ShellRoot { ShellRoot {
id: root id: root
// Force initialization of some singletons // Stuff for every panel family
ReloadPopup {}
Component.onCompleted: { Component.onCompleted: {
MaterialThemeLoader.reapplyTheme() MaterialThemeLoader.reapplyTheme()
Hyprsunset.load() Hyprsunset.load()
@@ -61,62 +32,33 @@ ShellRoot {
Updates.load() 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: "wNotificationPopup"; component: WaffleNotificationPopup {} }
PanelLoader { identifier: "wOnScreenDisplay"; component: WaffleOSD {} }
PanelLoader { identifier: "wPolkit"; component: WafflePolkit {} }
PanelLoader { identifier: "wScreenSnip"; component: WScreenSnip {} }
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 // Panel families
property list<string> families: ["ii", "waffle"] property list<string> 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", "wNotificationPopup", "wOnScreenDisplay", "wTaskView", "wPolkit", "wScreenSnip", "wSessionScreen", "wStartMenu", "iiCheatsheet", "iiOnScreenKeyboard", "iiOverlay", "iiWallpaperSelector"],
})
function cyclePanelFamily() { function cyclePanelFamily() {
const currentIndex = families.indexOf(Config.options.panelFamily) const currentIndex = families.indexOf(Config.options.panelFamily)
const nextIndex = (currentIndex + 1) % families.length const nextIndex = (currentIndex + 1) % families.length
Config.options.panelFamily = families[nextIndex] 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 { IpcHandler {
target: "panelFamily" target: "panelFamily"
+1 -1
View File
@@ -1,5 +1,5 @@
# Auto start Hyprland on tty1 # Auto start Hyprland on tty1
if [ -z "$DISPLAY" ] && [ "$XDG_VTNR" -eq 1 ]; then if [ -z "$DISPLAY" ] && [ "$XDG_VTNR" -eq 1 ]; then
mkdir -p ~/.cache mkdir -p ~/.cache
exec Hyprland > ~/.cache/hyprland.log 2>&1 exec start-hyprland > ~/.cache/hyprland.log 2>&1
fi fi
+95 -74
View File
@@ -31,9 +31,7 @@
# #
set -euo pipefail set -euo pipefail
# TODO: For Arch(-Linux) specific part please check if pacman exists first, if not it should be skipped. # Note: The detect_repo_structure function below auto-detects the folder layout
# 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.
# Try to find the packages directory (different names in different versions) # Try to find the packages directory (different names in different versions)
if which pacman &>/dev/null; then if which pacman &>/dev/null; then
if [[ -d "${REPO_ROOT}/dist-arch" ]]; then if [[ -d "${REPO_ROOT}/dist-arch" ]]; then
@@ -56,7 +54,6 @@ declare -a IGNORE_SUBSTRING_PATTERNS=()
# Track created directories to avoid redundant mkdir calls # Track created directories to avoid redundant mkdir calls
declare -A CREATED_DIRS 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 # Auto-detect repository structure
detect_repo_structure() { detect_repo_structure() {
local found_dirs=() local found_dirs=()
@@ -644,23 +641,41 @@ build_packages() {
log_info "Building package: $pkg_name" log_info "Building package: $pkg_name"
if [[ "$DRY_RUN" == true ]]; then 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 continue
fi fi
cd "$pkg_dir" || { # Create temp build directory to avoid polluting the repo
log_error "Failed to change to package directory: $pkg_dir" 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 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" log_success "Successfully built and installed $pkg_name"
((rebuilt_packages++)) ((rebuilt_packages++)) || true
else else
log_error "Failed to build package $pkg_name" log_error "Failed to build package $pkg_name"
fi fi
# Clean up temp build directory
cd "$REPO_ROOT" || log_die "Failed to return to repository 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 done
if [[ $rebuilt_packages -eq 0 ]]; then if [[ $rebuilt_packages -eq 0 ]]; then
@@ -852,8 +867,12 @@ if git remote get-url origin &>/dev/null; then
fi fi
fi fi
else else
log_warning "Failed to pull changes from remote. Continuing with local repository..." log_warning "Failed to pull changes from remote."
log_info "You may need to resolve conflicts manually later." 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
fi fi
else else
@@ -879,68 +898,70 @@ if [[ "$CHECK_PACKAGES" == true ]]; then
if [[ "$PKG_TOOLS_AVAILABLE" == true ]]; then if [[ "$PKG_TOOLS_AVAILABLE" == true ]]; then
if [[ ! -d "$ARCH_PACKAGES_DIR" ]]; 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 else
changed_pkgbuilds=() # Scan for changed PKGBUILDs
for pkg_dir in "$ARCH_PACKAGES_DIR"/*/; do changed_pkgbuilds=()
if [[ -f "${pkg_dir}/PKGBUILD" ]]; then for pkg_dir in "$ARCH_PACKAGES_DIR"/*/; do
pkg_name=$(basename "$pkg_dir") if [[ -f "${pkg_dir}/PKGBUILD" ]]; then
if check_pkgbuild_changed "$pkg_dir"; then pkg_name=$(basename "$pkg_dir")
changed_pkgbuilds+=("$pkg_name") if check_pkgbuild_changed "$pkg_dir"; then
fi changed_pkgbuilds+=("$pkg_name")
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 fi
;; fi
3) build_packages "all" ;; done
4 | *) log_info "Skipping package building" ;;
esac if [[ ${#changed_pkgbuilds[@]} -gt 0 ]]; then
fi log_info "Found ${#changed_pkgbuilds[@]} package(s) with changed PKGBUILDs: ${changed_pkgbuilds[*]}"
else echo
log_info "No PKGBUILDs have changed since last update." echo "Package build options:"
echo echo "1) Build only packages with changed PKGBUILDs"
if [[ "$NON_INTERACTIVE" == true ]]; then echo "2) List all packages and select which to build"
check_anyway="N" echo "3) Build all packages"
log_info "Non-interactive mode: Using default for check packages anyway: $check_anyway" echo "4) Skip package building"
elif safe_read "Do you want to check and build packages anyway? (y/N): " check_anyway "N"; then echo
if [[ "$VERBOSE" == true ]]; then
log_info "User chose to check packages anyway: $check_anyway" 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 fi
else else
log_warning "Failed to read input. Skipping package management." log_info "No PKGBUILDs have changed since last update."
check_anyway="" echo
fi 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 if list_packages; then
echo echo
echo "Package build options:" echo "Package build options:"
@@ -950,9 +971,9 @@ if [[ "$CHECK_PACKAGES" == true ]]; then
if safe_read "Choose an option (1-3): " build_choice "3"; then if safe_read "Choose an option (1-3): " build_choice "3"; then
case $build_choice in case $build_choice in
1) build_packages "select" ;; 1) build_packages "select" ;;
2) build_packages "all" ;; 2) build_packages "all" ;;
3 | *) log_info "Skipping package building" ;; 3|*) log_info "Skipping package building" ;;
esac esac
else else
log_info "Skipping package building" log_info "Skipping package building"
@@ -963,10 +984,10 @@ if [[ "$CHECK_PACKAGES" == true ]]; then
fi fi
fi fi
fi fi
else
log_header "Package Management"
log_info "Package checking disabled. Use -p or --packages flag to enable package management."
fi fi
else
log_header "Package Management"
log_info "Package checking disabled. Use -p or --packages flag to enable package management."
fi fi
# Step 3: Update configuration files # Step 3: Update configuration files
+3 -3
View File
@@ -118,7 +118,7 @@ function install_dir(){
if [ -d $t ];then if [ -d $t ];then
warning_overwrite warning_overwrite
fi fi
rsync_dir $s $t v rsync_dir $s $t
} }
function install_dir__sync(){ function install_dir__sync(){
# NOTE: Do not add prefix `v` or `x` when using this function # NOTE: Do not add prefix `v` or `x` when using this function
@@ -127,7 +127,7 @@ function install_dir__sync(){
if [ -d $t ];then if [ -d $t ];then
warning_overwrite warning_overwrite
fi fi
rsync_dir__sync $s $t v rsync_dir__sync $s $t
} }
function install_dir__skip_existed(){ function install_dir__skip_existed(){
# NOTE: Do not add prefix `v` or `x` when using this function # 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 if [ -d $t ];then
warning_overwrite warning_overwrite
fi fi
rsync_dir__sync_exclude $s $t "$@" v rsync_dir__sync_exclude $s $t "$@"
} }
function install_google_sans_flex(){ function install_google_sans_flex(){
local font_name="Google Sans Flex" local font_name="Google Sans Flex"