Merge branch 'main' into main

This commit is contained in:
end-4
2025-12-02 23:01:36 +01:00
committed by GitHub
64 changed files with 1766 additions and 482 deletions
+8
View File
@@ -1,2 +1,10 @@
# You can put extra environment variables here
# https://wiki.hyprland.org/Configuring/Environment-variables/
# ######### Input method ##########
# See https://fcitx-im.org/wiki/Using_Fcitx_5_on_Wayland
#env = QT_IM_MODULE, fcitx
#env = XMODIFIERS, @im=fcitx
#env = SDL_IM_MODULE, fcitx
#env = GLFW_IM_MODULE, ibus
#env = INPUT_METHOD, fcitx
+4
View File
@@ -1,2 +1,6 @@
# You can make apps auto-start here
# Relevant Hyprland wiki section: https://wiki.hyprland.org/Configuring/Keywords/#executing
# Input method
# exec-once = fcitx5
-8
View File
@@ -1,11 +1,3 @@
# ######### Input method ##########
# See https://fcitx-im.org/wiki/Using_Fcitx_5_on_Wayland
#env = QT_IM_MODULE, fcitx
#env = XMODIFIERS, @im=fcitx
#env = SDL_IM_MODULE, fcitx
#env = GLFW_IM_MODULE, ibus
#env = INPUT_METHOD, fcitx
# ############ Wayland #############
env = ELECTRON_OZONE_PLATFORM_HINT,auto
-3
View File
@@ -3,9 +3,6 @@ exec-once = ~/.config/hypr/hyprland/scripts/start_geoclue_agent.sh
exec-once = qs -c $qsConfig &
exec-once = ~/.config/hypr/custom/scripts/__restore_video_wallpaper.sh
# Input method
# exec-once = fcitx5
# Core components (authentication, lock screen, notification daemon)
exec-once = gnome-keyring-daemon --start --components=secrets
exec-once = hypridle
+16 -16
View File
@@ -4,27 +4,27 @@
#!
##! Shell
# These absolutely need to be on top, or they won't work consistently
bindid = Super, Super_L, Toggle overview, global, quickshell:overviewToggleRelease # Toggle overview/launcher
bindid = Super, Super_R, Toggle overview, global, quickshell:overviewToggleRelease # [hidden] Toggle overview/launcher
bindid = Super, Super_L, Toggle search, global, quickshell:searchToggleRelease # Toggle search
bindid = Super, Super_R, Toggle search, global, quickshell:searchToggleRelease # [hidden] Toggle search
bind = Super, Super_L, exec, qs -c $qsConfig ipc call TEST_ALIVE || pkill fuzzel || fuzzel # [hidden] Launcher (fallback)
bind = Super, Super_R, exec, qs -c $qsConfig ipc call TEST_ALIVE || pkill fuzzel || fuzzel # [hidden] Launcher (fallback)
binditn = Super, catchall, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
bind = Ctrl, Super_L, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
bind = Ctrl, Super_R, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
bind = Super, mouse:272, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
bind = Super, mouse:273, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
bind = Super, mouse:274, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
bind = Super, mouse:275, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
bind = Super, mouse:276, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
bind = Super, mouse:277, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
bind = Super, mouse_up, global, quickshell:overviewToggleReleaseInterrupt # [hidden]
bind = Super, mouse_down,global, quickshell:overviewToggleReleaseInterrupt # [hidden]
binditn = Super, catchall, global, quickshell:searchToggleReleaseInterrupt # [hidden]
bind = Ctrl, Super_L, global, quickshell:searchToggleReleaseInterrupt # [hidden]
bind = Ctrl, Super_R, global, quickshell:searchToggleReleaseInterrupt # [hidden]
bind = Super, mouse:272, global, quickshell:searchToggleReleaseInterrupt # [hidden]
bind = Super, mouse:273, global, quickshell:searchToggleReleaseInterrupt # [hidden]
bind = Super, mouse:274, global, quickshell:searchToggleReleaseInterrupt # [hidden]
bind = Super, mouse:275, global, quickshell:searchToggleReleaseInterrupt # [hidden]
bind = Super, mouse:276, global, quickshell:searchToggleReleaseInterrupt # [hidden]
bind = Super, mouse:277, global, quickshell:searchToggleReleaseInterrupt # [hidden]
bind = Super, mouse_up, global, quickshell:searchToggleReleaseInterrupt # [hidden]
bind = Super, mouse_down,global, quickshell:searchToggleReleaseInterrupt # [hidden]
bindit = ,Super_L, global, quickshell:workspaceNumber # [hidden]
bindit = ,Super_R, global, quickshell:workspaceNumber # [hidden]
bind = Super, Tab, global, quickshell:overviewWorkspacesToggle # Toggle overview
bindd = Super, V, Clipboard history >> clipboard, global, quickshell:overviewClipboardToggle # Clipboard history >> clipboard
bindd = Super, Period, Emoji >> clipboard, global, quickshell:overviewEmojiToggle # Emoji >> clipboard
bind = Super, Tab, global, quickshell:overviewWorkspacesToggle # [hidden] Toggle overview/launcher (alt)
bind = Super, A, global, quickshell:sidebarLeftToggle # Toggle left sidebar
bind = Super+Alt, A, global, quickshell:sidebarLeftToggleDetach # [hidden]
bind = Super, B, global, quickshell:sidebarLeftToggle # [hidden]
@@ -218,8 +218,8 @@ submap = global
#!
# Testing
bind = Super+Alt, f11, exec, bash -c 'RANDOM_IMAGE=$(find ~/Pictures -type f | grep -v -i "nipple" | grep -v -i "pussy" | shuf -n 1); ACTION=$(notify-send "Test notification with body image" "This notification should contain your user account <b>image</b> and <a href=\"https://discord.com/app\">Discord</a> <b>icon</b>. Oh and here is a random image in your Pictures folder: <img src=\"$RANDOM_IMAGE\" alt=\"Testing image\"/>" -a "Hyprland keybind" -p -h "string:image-path:/var/lib/AccountsService/icons/$USER" -t 6000 -i "discord" -A "openImage=Open profile image" -A "action2=Open the random image" -A "action3=Useless button"); [[ $ACTION == *openImage ]] && xdg-open "/var/lib/AccountsService/icons/$USER"; [[ $ACTION == *action2 ]] && xdg-open \"$RANDOM_IMAGE\"' # [hidden]
bind = Super+Alt, f12, exec, bash -c 'RANDOM_IMAGE=$(find ~/Pictures -type f | grep -v -i "nipple" | grep -v -i "pussy" | shuf -n 1); ACTION=$(notify-send "Test notification" "This notification should contain a random image in your <b>Pictures</b> folder and <a href=\"https://discord.com/app\">Discord</a> <b>icon</b>.\n<i>Flick right to dismiss!</i>" -a "Discord (fake)" -p -h "string:image-path:$RANDOM_IMAGE" -t 6000 -i "discord" -A "openImage=Open profile image" -A "action2=Useless button" -A "action3=Cry more"); [[ $ACTION == *openImage ]] && xdg-open "/var/lib/AccountsService/icons/$USER"' # [hidden]
bind = Super+Alt, f11, exec, bash -c 'RANDOM_IMAGE=$(find ~/Pictures -type f | grep -v -i "nipple" | grep -v -i "pussy" | shuf -n 1); ACTION=$(notify-send "Test notification with body image" "This notification should contain your user account <b>image</b> and <a href=\"https://discord.com/app\">Discord</a> <b>icon</b>. Oh and here is a random image in your Pictures folder: <img src=\"$RANDOM_IMAGE\" alt=\"Testing image\"/>" -a "Hyprland keybind" -p -h "string:image-path:/var/lib/AccountsService/icons/$USER" -t 6000 -i "discord" -A "openImage=Profile image" -A "action2=Open the random image" -A "action3=Useless button"); [[ $ACTION == *openImage ]] && xdg-open "/var/lib/AccountsService/icons/$USER"; [[ $ACTION == *action2 ]] && xdg-open \"$RANDOM_IMAGE\"' # [hidden]
bind = Super+Alt, f12, exec, bash -c 'RANDOM_IMAGE=$(find ~/Pictures -type f | grep -v -i "nipple" | grep -v -i "pussy" | shuf -n 1); ACTION=$(notify-send "Test notification" "This notification should contain a random image in your <b>Pictures</b> folder and <a href=\"https://discord.com/app\">Discord</a> <b>icon</b>.\n<i>Flick right to dismiss!</i>" -a "Discord (fake)" -p -h "string:image-path:$RANDOM_IMAGE" -t 6000 -i "discord" -A "openImage=Profile image" -A "action2=Useless button"); [[ $ACTION == *openImage ]] && xdg-open "/var/lib/AccountsService/icons/$USER"' # [hidden]
bind = Super+Alt, Equal, exec, notify-send "Urgent notification" "Ah hell no" -u critical -a 'Hyprland keybind' # [hidden]
##! Session
+2
View File
@@ -159,7 +159,9 @@ 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
# Launchers need to be FAST
layerrule = noanim, gtk4-layer-shell
@@ -20,6 +20,7 @@ Singleton {
property bool overlayOpen: false
property bool overviewOpen: false
property bool regionSelectorOpen: false
property bool searchOpen: false
property bool screenLocked: false
property bool screenLockContainsCharacters: false
property bool screenUnlockFailed: false
@@ -0,0 +1,17 @@
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<mask id="mask0_525_6" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="4" y="4" width="56" height="56">
<rect x="4.5" y="4.5" width="26.5" height="26.5" fill="#D9D9D9"/>
<rect x="4.5" y="33" width="26.5" height="26.5" fill="#D9D9D9"/>
<rect x="33" y="4.5" width="26.5" height="26.5" fill="#D9D9D9"/>
<rect x="33" y="33" width="26.5" height="26.5" fill="#D9D9D9"/>
</mask>
<g mask="url(#mask0_525_6)">
<rect width="64" height="64" fill="url(#paint0_linear_525_6)"/>
</g>
<defs>
<linearGradient id="paint0_linear_525_6" x1="0" y1="0" x2="63.6279" y2="64" gradientUnits="userSpaceOnUse">
<stop stop-color="#5AE8C0"/>
<stop offset="0.99563" stop-color="#119979"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 795 B

@@ -0,0 +1,4 @@
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#000000">
<path d="M8.20514 4.84312C8.70586 4.61008 9.30068 4.82707 9.53372 5.32778C9.76676 5.82849 9.54977 6.42331 9.04906 6.65635C6.59946 7.79644 5 10.2547 5 13.003C5 16.8671 8.13391 19.9997 12 19.9997C15.8661 19.9997 19 16.8671 19 13.003C19 10.2604 17.4072 7.80631 14.9653 6.66304C14.4651 6.42887 14.2495 5.83355 14.4836 5.33337C14.7178 4.83319 15.3131 4.61756 15.8133 4.85173C18.9517 6.32109 21 9.47689 21 13.003C21 17.9719 16.9705 21.9997 12 21.9997C7.02953 21.9997 3 17.9719 3 13.003C3 9.46957 5.05682 6.30841 8.20514 4.84312ZM12 1.99902C12.5128 1.99902 12.9355 2.38506 12.9933 2.8824L13 2.99902V10.0004C13 10.5527 12.5523 11.0004 12 11.0004C11.4872 11.0004 11.0645 10.6144 11.0067 10.1171L11 10.0004V2.99902C11 2.44674 11.4477 1.99902 12 1.99902Z" fill="#000000"/>
</svg>

After

Width:  |  Height:  |  Size: 906 B

@@ -0,0 +1,4 @@
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="#000000">
<path d="M8.2042 4.82046C8.57962 4.64545 9.02584 4.80792 9.20085 5.18334C9.37586 5.55876 9.2134 6.00498 8.83797 6.18C6.21382 7.4033 4.5 10.0416 4.5 12.9914C4.5 17.1386 7.85759 20.5002 11.9989 20.5002C16.1403 20.5002 19.4979 17.1386 19.4979 12.9914C19.4979 10.0477 17.7912 7.41389 15.1753 6.18718C14.8002 6.01131 14.6388 5.56472 14.8147 5.1897C14.9905 4.81467 15.4371 4.65322 15.8121 4.82909C18.9502 6.30065 20.9979 9.46066 20.9979 12.9914C20.9979 17.9666 16.9691 22.0002 11.9989 22.0002C7.02876 22.0002 3 17.9666 3 12.9914C3 9.45334 5.05623 6.28796 8.2042 4.82046ZM11.9989 2.49609C12.3786 2.49609 12.6924 2.77825 12.7421 3.14432L12.7489 3.24609V10.746C12.7489 11.1602 12.4132 11.496 11.9989 11.496C11.6192 11.496 11.3055 11.2139 11.2558 10.8478L11.2489 10.746V3.24609C11.2489 2.83188 11.5847 2.49609 11.9989 2.49609Z" fill="#000000"/>
</svg>

After

Width:  |  Height:  |  Size: 979 B

@@ -1,24 +1,120 @@
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 6.2C4 4.98497 4.98497 4 6.2 4H28.2C29.415 4 30.4 4.98497 30.4 6.2V28.2C30.4 29.415 29.415 30.4 28.2 30.4H6.2C4.98497 30.4 4 29.415 4 28.2V6.2Z" fill="url(#paint0_radial_520_19)"/>
<path d="M32.6 6.2C32.6 4.98497 33.585 4 34.8 4H56.8C58.015 4 59 4.98497 59 6.2V28.2C59 29.415 58.015 30.4 56.8 30.4H34.8C33.585 30.4 32.6 29.415 32.6 28.2V6.2Z" fill="url(#paint1_radial_520_19)"/>
<path d="M32.6 34.8C32.6 33.585 33.585 32.6 34.8 32.6H56.8C58.015 32.6 59 33.585 59 34.8V56.8C59 58.015 58.015 59 56.8 59H34.8C33.585 59 32.6 58.015 32.6 56.8V34.8Z" fill="url(#paint2_radial_520_19)"/>
<path d="M4 34.8C4 33.585 4.98497 32.6 6.2 32.6H28.2C29.415 32.6 30.4 33.585 30.4 34.8V56.8C30.4 58.015 29.415 59 28.2 59H6.2C4.98497 59 4 58.015 4 56.8V34.8Z" fill="url(#paint3_radial_520_19)"/>
<defs>
<radialGradient id="paint0_radial_520_19" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(4 4) rotate(45) scale(77.7817)">
<stop stop-color="#0B9BFE"/>
<stop offset="1" stop-color="#0B9BFE"/>
</radialGradient>
<radialGradient id="paint1_radial_520_19" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(4 4) rotate(45) scale(77.7817)">
<stop stop-color="#0B9BFE"/>
<stop offset="1" stop-color="#0B9BFE"/>
</radialGradient>
<radialGradient id="paint2_radial_520_19" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(4 4) rotate(45) scale(77.7817)">
<stop stop-color="#0B9BFE"/>
<stop offset="1" stop-color="#0B9BFE"/>
</radialGradient>
<radialGradient id="paint3_radial_520_19" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(4 4) rotate(45) scale(77.7817)">
<stop stop-color="#0B9BFE"/>
<stop offset="1" stop-color="#0B9BFE"/>
</radialGradient>
</defs>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="64"
height="64"
viewBox="0 0 64 64"
fill="none"
version="1.1"
id="svg11"
sodipodi:docname="start-here-pressed.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview11"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="6.375"
inkscape:cx="32.156863"
inkscape:cy="28.862745"
inkscape:window-width="1164"
inkscape:window-height="1020"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg11" />
<g
id="g11"
transform="translate(0.5,0.5)">
<path
d="M 4,6.2 C 4,4.98497 4.98497,4 6.2,4 h 22 c 1.215,0 2.2,0.98497 2.2,2.2 v 22 c 0,1.215 -0.985,2.2 -2.2,2.2 H 6.2 C 4.98497,30.4 4,29.415 4,28.2 Z"
fill="url(#paint0_radial_520_19)"
id="path1"
style="fill:url(#paint0_radial_520_19)" />
<path
d="M 32.6,6.2 C 32.6,4.98497 33.585,4 34.8,4 h 22 C 58.015,4 59,4.98497 59,6.2 v 22 c 0,1.215 -0.985,2.2 -2.2,2.2 h -22 c -1.215,0 -2.2,-0.985 -2.2,-2.2 z"
fill="url(#paint1_radial_520_19)"
id="path2"
style="fill:url(#paint1_radial_520_19)" />
<path
d="m 32.6,34.8 c 0,-1.215 0.985,-2.2 2.2,-2.2 h 22 c 1.215,0 2.2,0.985 2.2,2.2 v 22 c 0,1.215 -0.985,2.2 -2.2,2.2 h -22 c -1.215,0 -2.2,-0.985 -2.2,-2.2 z"
fill="url(#paint2_radial_520_19)"
id="path3"
style="fill:url(#paint2_radial_520_19)" />
<path
d="m 4,34.8 c 0,-1.215 0.98497,-2.2 2.2,-2.2 h 22 c 1.215,0 2.2,0.985 2.2,2.2 v 22 c 0,1.215 -0.985,2.2 -2.2,2.2 H 6.2 C 4.98497,59 4,58.015 4,56.8 Z"
fill="url(#paint3_radial_520_19)"
id="path4"
style="fill:url(#paint3_radial_520_19)" />
</g>
<defs
id="defs11">
<radialGradient
id="paint0_radial_520_19"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(54.999968,54.999968,-54.999968,54.999968,4,4)">
<stop
stop-color="#0B9BFE"
id="stop4" />
<stop
offset="1"
stop-color="#0B9BFE"
id="stop5" />
</radialGradient>
<radialGradient
id="paint1_radial_520_19"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(54.999968,54.999968,-54.999968,54.999968,4,4)">
<stop
stop-color="#0B9BFE"
id="stop6" />
<stop
offset="1"
stop-color="#0B9BFE"
id="stop7" />
</radialGradient>
<radialGradient
id="paint2_radial_520_19"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(54.999968,54.999968,-54.999968,54.999968,4,4)">
<stop
stop-color="#0B9BFE"
id="stop8" />
<stop
offset="1"
stop-color="#0B9BFE"
id="stop9" />
</radialGradient>
<radialGradient
id="paint3_radial_520_19"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(54.999968,54.999968,-54.999968,54.999968,4,4)">
<stop
stop-color="#0B9BFE"
id="stop10" />
<stop
offset="1"
stop-color="#0B9BFE"
id="stop11" />
</radialGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

@@ -1,24 +1,120 @@
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 6.2C4 4.98497 4.98497 4 6.2 4H28.2C29.415 4 30.4 4.98497 30.4 6.2V28.2C30.4 29.415 29.415 30.4 28.2 30.4H6.2C4.98497 30.4 4 29.415 4 28.2V6.2Z" fill="url(#paint0_radial_519_6)"/>
<path d="M32.6 6.2C32.6 4.98497 33.585 4 34.8 4H56.8C58.015 4 59 4.98497 59 6.2V28.2C59 29.415 58.015 30.4 56.8 30.4H34.8C33.585 30.4 32.6 29.415 32.6 28.2V6.2Z" fill="url(#paint1_radial_519_6)"/>
<path d="M32.6 34.8C32.6 33.585 33.585 32.6 34.8 32.6H56.8C58.015 32.6 59 33.585 59 34.8V56.8C59 58.015 58.015 59 56.8 59H34.8C33.585 59 32.6 58.015 32.6 56.8V34.8Z" fill="url(#paint2_radial_519_6)"/>
<path d="M4 34.8C4 33.585 4.98497 32.6 6.2 32.6H28.2C29.415 32.6 30.4 33.585 30.4 34.8V56.8C30.4 58.015 29.415 59 28.2 59H6.2C4.98497 59 4 58.015 4 56.8V34.8Z" fill="url(#paint3_radial_519_6)"/>
<defs>
<radialGradient id="paint0_radial_519_6" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(4 4) rotate(45) scale(77.7817)">
<stop stop-color="#81DFFF"/>
<stop offset="1" stop-color="#0A99F9"/>
</radialGradient>
<radialGradient id="paint1_radial_519_6" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(4 4) rotate(45) scale(77.7817)">
<stop stop-color="#81DFFF"/>
<stop offset="1" stop-color="#0A99F9"/>
</radialGradient>
<radialGradient id="paint2_radial_519_6" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(4 4) rotate(45) scale(77.7817)">
<stop stop-color="#81DFFF"/>
<stop offset="1" stop-color="#0A99F9"/>
</radialGradient>
<radialGradient id="paint3_radial_519_6" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(4 4) rotate(45) scale(77.7817)">
<stop stop-color="#81DFFF"/>
<stop offset="1" stop-color="#0A99F9"/>
</radialGradient>
</defs>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="64"
height="64"
viewBox="0 0 64 64"
fill="none"
version="1.1"
id="svg11"
sodipodi:docname="start-here.svg"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview11"
pagecolor="#ffffff"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="4.5078057"
inkscape:cx="53.3519"
inkscape:cy="28.838865"
inkscape:window-width="1113"
inkscape:window-height="1020"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg11" />
<g
id="g11"
transform="translate(0.5,0.5)">
<path
d="M 4,6.2 C 4,4.98497 4.98497,4 6.2,4 h 22 c 1.215,0 2.2,0.98497 2.2,2.2 v 22 c 0,1.215 -0.985,2.2 -2.2,2.2 H 6.2 C 4.98497,30.4 4,29.415 4,28.2 Z"
fill="url(#paint0_radial_519_6)"
id="path1"
style="fill:url(#paint0_radial_519_6)" />
<path
d="M 32.6,6.2 C 32.6,4.98497 33.585,4 34.8,4 h 22 C 58.015,4 59,4.98497 59,6.2 v 22 c 0,1.215 -0.985,2.2 -2.2,2.2 h -22 c -1.215,0 -2.2,-0.985 -2.2,-2.2 z"
fill="url(#paint1_radial_519_6)"
id="path2"
style="fill:url(#paint1_radial_519_6)" />
<path
d="m 32.6,34.8 c 0,-1.215 0.985,-2.2 2.2,-2.2 h 22 c 1.215,0 2.2,0.985 2.2,2.2 v 22 c 0,1.215 -0.985,2.2 -2.2,2.2 h -22 c -1.215,0 -2.2,-0.985 -2.2,-2.2 z"
fill="url(#paint2_radial_519_6)"
id="path3"
style="fill:url(#paint2_radial_519_6)" />
<path
d="m 4,34.8 c 0,-1.215 0.98497,-2.2 2.2,-2.2 h 22 c 1.215,0 2.2,0.985 2.2,2.2 v 22 c 0,1.215 -0.985,2.2 -2.2,2.2 H 6.2 C 4.98497,59 4,58.015 4,56.8 Z"
fill="url(#paint3_radial_519_6)"
id="path4"
style="fill:url(#paint3_radial_519_6)" />
</g>
<defs
id="defs11">
<radialGradient
id="paint0_radial_519_6"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(54.999968,54.999968,-54.999968,54.999968,4,4)">
<stop
stop-color="#81DFFF"
id="stop4" />
<stop
offset="1"
stop-color="#0A99F9"
id="stop5" />
</radialGradient>
<radialGradient
id="paint1_radial_519_6"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(54.999968,54.999968,-54.999968,54.999968,4,4)">
<stop
stop-color="#81DFFF"
id="stop6" />
<stop
offset="1"
stop-color="#0A99F9"
id="stop7" />
</radialGradient>
<radialGradient
id="paint2_radial_519_6"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(54.999968,54.999968,-54.999968,54.999968,4,4)">
<stop
stop-color="#81DFFF"
id="stop8" />
<stop
offset="1"
stop-color="#0A99F9"
id="stop9" />
</radialGradient>
<radialGradient
id="paint3_radial_519_6"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(54.999968,54.999968,-54.999968,54.999968,4,4)">
<stop
stop-color="#81DFFF"
id="stop10" />
<stop
offset="1"
stop-color="#0A99F9"
id="stop11" />
</radialGradient>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

@@ -26,8 +26,8 @@
inkscape:zoom="4.65625"
inkscape:cx="32"
inkscape:cy="32"
inkscape:window-width="1197"
inkscape:window-height="1020"
inkscape:window-width="1595"
inkscape:window-height="664"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
@@ -48,10 +48,10 @@
</linearGradient>
<linearGradient
id="linearGradient919"
x1="4.3106"
x2="14.36"
y1="8.4665"
y2="8.4665"
x1="4.4818125"
x2="14.188787"
y1="7.1660123"
y2="9.7669888"
gradientTransform="matrix(1.226575,0,0,1.226575,-0.82407803,-6.4497629)"
gradientUnits="userSpaceOnUse">
<stop
@@ -61,7 +61,7 @@
style="stop-color:#5fe277;stop-opacity:1;" />
<stop
stop-color="#55b4ff"
offset="1"
offset="0.40375727"
id="stop2"
style="stop-color:#0078d3;stop-opacity:1;" />
</linearGradient>

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

@@ -23,10 +23,10 @@
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:zoom="6.5849319"
inkscape:cx="26.95548"
inkscape:cx="27.031411"
inkscape:cy="26.423963"
inkscape:window-width="1257"
inkscape:window-height="1020"
inkscape:window-width="1621"
inkscape:window-height="820"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
@@ -35,10 +35,10 @@
id="defs2">
<linearGradient
id="linearGradient919"
x1="4.3106"
x2="14.36"
y1="8.4665"
y2="8.4665"
x1="4.4818139"
x2="14.188786"
y1="7.1660118"
y2="9.7669868"
gradientTransform="matrix(1.226575,0,0,1.226575,-0.82407803,-6.4497629)"
gradientUnits="userSpaceOnUse">
<stop
@@ -48,7 +48,7 @@
style="stop-color:#5fe277;stop-opacity:1;" />
<stop
stop-color="#55b4ff"
offset="1"
offset="0.40380999"
id="stop2"
style="stop-color:#0078d3;stop-opacity:1;" />
</linearGradient>

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

@@ -154,6 +154,7 @@ Singleton {
property JsonObject apps: JsonObject {
property string bluetooth: "kcmshell6 kcm_bluetooth"
property string network: "kcmshell6 kcm_networkmanagement"
property string manageUser: "kcmshell6 kcm_users"
property string networkEthernet: "kcmshell6 kcm_networkmanagement"
property string taskManager: "plasma-systemmonitor --page-name Processes"
property string terminal: "kitty -1" // This is only for shell actions
@@ -1,6 +1,7 @@
pragma Singleton
pragma ComponentBehavior: Bound
import qs.services
import qs.modules.common.functions
import QtCore
import QtQuick
@@ -46,6 +47,9 @@ Singleton {
property string aiChats: FileUtils.trimFileProtocol(`${Directories.state}/user/ai/chats`)
property string aiTranslationScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/ai/gemini-translate.sh`)
property string recordScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/videos/record.sh`)
property string userAvatarPathAccountsService: FileUtils.trimFileProtocol(`/var/lib/AccountsService/icons/${SystemInfo.username}`)
property string userAvatarPathRicersAndWeirdSystems: FileUtils.trimFileProtocol(`${Directories.home}.face`)
property string userAvatarPathRicersAndWeirdSystems2: FileUtils.trimFileProtocol(`${Directories.home}.face.icon`)
// Cleanup on init
Component.onCompleted: {
Quickshell.execDetached(["mkdir", "-p", `${shellConfig}`])
@@ -84,4 +84,28 @@ Singleton {
// Older dates
return Qt.formatDateTime(messageTime, "MMMM dd");
}
function processNotificationBody(body, appName) {
let processedBody = body
// Clean Chromium-based browsers notifications - remove first line
if (appName) {
const lowerApp = appName.toLowerCase()
const chromiumBrowsers = [
"brave", "chrome", "chromium", "vivaldi", "opera", "microsoft edge"
]
if (chromiumBrowsers.some(name => lowerApp.includes(name))) {
const lines = body.split('\n\n')
if (lines.length > 1 && lines[0].startsWith('<a')) {
processedBody = lines.slice(1).join('\n\n')
}
}
}
processedBody = processedBody.replace(/<img/gi, '\n\n<img');
return processedBody
}
}
@@ -18,14 +18,19 @@ Process {
running: true
command: ["bash", "-c",
`mkdir -p $(dirname '${processFilePath(filePath)}'); [ -f '${processFilePath(filePath)}' ] || curl -sSL '${sourceUrl}' -o '${processFilePath(filePath)}' && magick identify -format '%w %h' '${processFilePath(filePath)}'[0]`
`mkdir -p $(dirname '${processFilePath()}'); [ -f '${processFilePath()}' ] || curl -sSL '${sourceUrl}' -o '${processFilePath()}' && file '${processFilePath()}'`
]
stdout: StdioCollector {
id: imageSizeOutputCollector
onStreamFinished: {
const output = imageSizeOutputCollector.text.trim();
const [width, height] = output.split(" ").map(Number);
root.done(root.filePath, width, height);
const match = output.match(/(\d+)\s*x\s*(\d+)/);
if (match) {
const width = Number(match[1]);
const height = Number(match[2]);
root.done(root.filePath, width, height);
}
}
}
}
@@ -31,28 +31,6 @@ Item { // Notification item area
implicitHeight: background.implicitHeight
function processNotificationBody(body, appName) {
let processedBody = body
// Clean Chromium-based browsers notifications - remove first line
if (appName) {
const lowerApp = appName.toLowerCase()
const chromiumBrowsers = [
"brave", "chrome", "chromium", "vivaldi", "opera", "microsoft edge"
]
if (chromiumBrowsers.some(name => lowerApp.includes(name))) {
const lines = body.split('\n\n')
if (lines.length > 1 && lines[0].startsWith('<a')) {
processedBody = lines.slice(1).join('\n\n')
}
}
}
return processedBody
}
function destroyWithAnimation(left = false) {
root.qmlParent.resetDrag()
background.anchors.leftMargin = background.anchors.leftMargin; // Break binding
@@ -196,12 +174,13 @@ Item { // Notification item area
maximumLineCount: 1
textFormat: Text.StyledText
text: {
return processNotificationBody(notificationObject.body, notificationObject.appName || notificationObject.summary).replace(/\n/g, "<br/>")
return NotificationUtils.processNotificationBody(notificationObject.body, notificationObject.appName || notificationObject.summary).replace(/\n/g, "<br/>")
}
}
}
ColumnLayout { // Expanded content
id: expandedContentColumn
Layout.fillWidth: true
opacity: root.expanded ? 1 : 0
visible: opacity > 0
@@ -218,8 +197,8 @@ Item { // Notification item area
elide: Text.ElideRight
textFormat: Text.RichText
text: {
return `<style>img{max-width:${300 /* binding to notificationBodyText.width would cause a binding loop */}px;}</style>` +
`${processNotificationBody(notificationObject.body, notificationObject.appName || notificationObject.summary).replace(/\n/g, "<br/>")}`
return `<style>img{max-width:${expandedContentColumn.width}px;}</style>` +
`${NotificationUtils.processNotificationBody(notificationObject.body, notificationObject.appName || notificationObject.summary).replace(/\n/g, "<br/>")}`
}
onLinkActivated: (link) => {
@@ -293,6 +272,8 @@ Item { // Notification item area
id: actionRepeater
model: notificationObject.actions
NotificationActionButton {
id: notifAction
required property var modelData
Layout.fillWidth: true
buttonText: modelData.text
urgency: notificationObject.urgency
@@ -12,4 +12,14 @@ Image {
Behavior on opacity {
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this)
}
property list<string> fallbacks: []
property int currentFallbackIndex: 0
onStatusChanged: {
if (status === Image.Error && currentFallbackIndex < fallbacks.length) {
source = fallbacks[currentFallbackIndex];
currentFallbackIndex += 1;
}
}
}
@@ -20,6 +20,7 @@ ToolTip {
hintingPreference: Font.PreferNoHinting // Prevent shaky text
}
delay: 0
visible: internalVisibleCondition
contentItem: StyledToolTipContent {
@@ -162,7 +162,7 @@ Scope {
}
IpcHandler {
target: "overview"
target: "search"
function toggle() {
GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
@@ -185,8 +185,8 @@ Scope {
}
GlobalShortcut {
name: "overviewToggle"
description: "Toggles overview on press"
name: "searchToggle"
description: "Toggles search on press"
onPressed: {
GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
@@ -201,16 +201,8 @@ Scope {
}
}
GlobalShortcut {
name: "overviewClose"
description: "Closes overview"
onPressed: {
GlobalStates.overviewOpen = false;
}
}
GlobalShortcut {
name: "overviewToggleRelease"
description: "Toggles overview on release"
name: "searchToggleRelease"
description: "Toggles search on release"
onPressed: {
GlobalStates.superReleaseMightTrigger = true;
@@ -225,8 +217,8 @@ Scope {
}
}
GlobalShortcut {
name: "overviewToggleReleaseInterrupt"
description: "Interrupts possibility of overview being toggled on release. " + "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. " + "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything."
name: "searchToggleReleaseInterrupt"
description: "Interrupts possibility of search being toggled on release. " + "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. " + "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything."
onPressed: {
GlobalStates.superReleaseMightTrigger = false;
@@ -81,7 +81,7 @@ RowLayout {
}
}
onTextChanged: root.searchingText = text
onTextChanged: LauncherSearch.query = text
onAccepted: {
if (appResults.count > 0) {
@@ -14,81 +14,11 @@ import Quickshell.Io
Item { // Wrapper
id: root
readonly property string xdgConfigHome: Directories.config
property string searchingText: ""
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
property string mathResult: ""
property bool clipboardWorkSafetyActive: {
const enabled = Config.options.workSafety.enable.clipboard;
const sensitiveNetwork = (StringUtils.stringListContainsSubstring(Network.networkName.toLowerCase(), Config.options.workSafety.triggerCondition.networkNameKeywords))
return enabled && sensitiveNetwork;
}
property var searchActions: [
{
action: "accentcolor",
execute: args => {
Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--noswitch", "--color", ...(args != '' ? [`${args}`] : [])]);
}
},
{
action: "dark",
execute: () => {
Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "dark", "--noswitch"]);
}
},
{
action: "konachanwallpaper",
execute: () => {
Quickshell.execDetached([Quickshell.shellPath("scripts/colors/random/random_konachan_wall.sh")]);
}
},
{
action: "light",
execute: () => {
Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "light", "--noswitch"]);
}
},
{
action: "superpaste",
execute: args => {
if (!/^(\d+)/.test(args.trim())) { // Invalid if doesn't start with numbers
Quickshell.execDetached([
"notify-send",
Translation.tr("Superpaste"),
Translation.tr("Usage: <tt>%1superpaste NUM_OF_ENTRIES[i]</tt>\nSupply <tt>i</tt> when you want images\nExamples:\n<tt>%1superpaste 4i</tt> for the last 4 images\n<tt>%1superpaste 7</tt> for the last 7 entries").arg(Config.options.search.prefix.action),
"-a", "Shell"
]);
return;
}
const syntaxMatch = /^(?:(\d+)(i)?)/.exec(args.trim());
const count = syntaxMatch[1] ? parseInt(syntaxMatch[1]) : 1;
const isImage = !!syntaxMatch[2];
Cliphist.superpaste(count, isImage);
}
},
{
action: "todo",
execute: args => {
Todo.addTask(args);
}
},
{
action: "wallpaper",
execute: () => {
GlobalStates.wallpaperSelectorOpen = true;
}
},
{
action: "wipeclipboard",
execute: () => {
Cliphist.wipe();
}
},
]
function focusFirstItem() {
appResults.currentIndex = 0;
}
@@ -103,13 +33,13 @@ Item { // Wrapper
function cancelSearch() {
searchBar.searchInput.selectAll();
root.searchingText = "";
LauncherSearch.query = "";
searchBar.animateWidth = true;
}
function setSearchingText(text) {
searchBar.searchInput.text = text;
root.searchingText = text;
LauncherSearch.query = text;
}
function containsUnsafeLink(entry) {
@@ -118,34 +48,6 @@ Item { // Wrapper
return StringUtils.stringListContainsSubstring(entry.toLowerCase(), unsafeKeywords);
}
Timer {
id: nonAppResultsTimer
interval: Config.options.search.nonAppResultDelay
onTriggered: {
let expr = root.searchingText;
if (expr.startsWith(Config.options.search.prefix.math)) {
expr = expr.slice(Config.options.search.prefix.math.length);
}
mathProcess.calculateExpression(expr);
}
}
Process {
id: mathProcess
property list<string> baseCommand: ["qalc", "-t"]
function calculateExpression(expression) {
mathProcess.running = false;
mathProcess.command = baseCommand.concat(expression);
mathProcess.running = true;
}
stdout: SplitParser {
onRead: data => {
root.mathResult = data;
root.focusFirstItem();
}
}
}
Keys.onPressed: event => {
// Prevent Esc and Backspace from registering
if (event.key === Qt.Key_Escape)
@@ -285,167 +187,9 @@ Item { // Wrapper
model: ScriptModel {
id: model
objectProp: "key"
values: {
// Search results are handled here
////////////////// Skip? //////////////////
if (root.searchingText == "")
return [];
///////////// Special cases ///////////////
if (root.searchingText.startsWith(Config.options.search.prefix.clipboard)) {
// Clipboard
const searchString = StringUtils.cleanPrefix(root.searchingText, Config.options.search.prefix.clipboard);
return Cliphist.fuzzyQuery(searchString).map((entry, index, array) => {
const mightBlurImage = Cliphist.entryIsImage(entry) && root.clipboardWorkSafetyActive;
let shouldBlurImage = mightBlurImage;
if (mightBlurImage) {
shouldBlurImage = shouldBlurImage && (containsUnsafeLink(array[index - 1]) || containsUnsafeLink(array[index + 1]));
}
const type = `#${entry.match(/^\s*(\S+)/)?.[1] || ""}`
return {
key: type,
cliphistRawString: entry,
name: StringUtils.cleanCliphistEntry(entry),
clickActionName: "",
type: type,
execute: () => {
Cliphist.copy(entry)
},
actions: [
{
name: "Copy",
materialIcon: "content_copy",
execute: () => {
Cliphist.copy(entry);
}
},
{
name: "Delete",
materialIcon: "delete",
execute: () => {
Cliphist.deleteEntry(entry);
}
}
],
blurImage: shouldBlurImage,
blurImageText: Translation.tr("Work safety")
};
}).filter(Boolean);
}
else if (root.searchingText.startsWith(Config.options.search.prefix.emojis)) {
// Clipboard
const searchString = StringUtils.cleanPrefix(root.searchingText, Config.options.search.prefix.emojis);
return Emojis.fuzzyQuery(searchString).map(entry => {
const emoji = entry.match(/^\s*(\S+)/)?.[1] || ""
return {
key: emoji,
cliphistRawString: entry,
bigText: emoji,
name: entry.replace(/^\s*\S+\s+/, ""),
clickActionName: "",
type: "Emoji",
execute: () => {
Quickshell.clipboardText = entry.match(/^\s*(\S+)/)?.[1];
}
};
}).filter(Boolean);
}
////////////////// Init ///////////////////
nonAppResultsTimer.restart();
const mathResultObject = {
key: `Math result: ${root.mathResult}`,
name: root.mathResult,
clickActionName: Translation.tr("Copy"),
type: Translation.tr("Math result"),
fontType: "monospace",
materialSymbol: 'calculate',
execute: () => {
Quickshell.clipboardText = root.mathResult;
}
};
const appResultObjects = AppSearch.fuzzyQuery(StringUtils.cleanPrefix(root.searchingText, Config.options.search.prefix.app)).map(entry => {
entry.clickActionName = Translation.tr("Launch");
entry.type = Translation.tr("App");
entry.key = entry.execute
return entry;
})
const commandResultObject = {
key: `cmd ${root.searchingText}`,
name: StringUtils.cleanPrefix(root.searchingText, Config.options.search.prefix.shellCommand).replace("file://", ""),
clickActionName: Translation.tr("Run"),
type: Translation.tr("Run command"),
fontType: "monospace",
materialSymbol: 'terminal',
execute: () => {
let cleanedCommand = root.searchingText.replace("file://", "");
cleanedCommand = StringUtils.cleanPrefix(cleanedCommand, Config.options.search.prefix.shellCommand);
if (cleanedCommand.startsWith(Config.options.search.prefix.shellCommand)) {
cleanedCommand = cleanedCommand.slice(Config.options.search.prefix.shellCommand.length);
}
Quickshell.execDetached(["bash", "-c", searchingText.startsWith('sudo') ? `${Config.options.apps.terminal} fish -C '${cleanedCommand}'` : cleanedCommand]);
}
};
const webSearchResultObject = {
key: `website ${root.searchingText}`,
name: StringUtils.cleanPrefix(root.searchingText, Config.options.search.prefix.webSearch),
clickActionName: Translation.tr("Search"),
type: Translation.tr("Search the web"),
materialSymbol: 'travel_explore',
execute: () => {
let query = StringUtils.cleanPrefix(root.searchingText, Config.options.search.prefix.webSearch);
let url = Config.options.search.engineBaseUrl + query;
for (let site of Config.options.search.excludedSites) {
url += ` -site:${site}`;
}
Qt.openUrlExternally(url);
}
}
const launcherActionObjects = root.searchActions.map(action => {
const actionString = `${Config.options.search.prefix.action}${action.action}`;
if (actionString.startsWith(root.searchingText) || root.searchingText.startsWith(actionString)) {
return {
key: `Action ${actionString}`,
name: root.searchingText.startsWith(actionString) ? root.searchingText : actionString,
clickActionName: Translation.tr("Run"),
type: Translation.tr("Action"),
materialSymbol: 'settings_suggest',
execute: () => {
action.execute(root.searchingText.split(" ").slice(1).join(" "));
}
};
}
return null;
}).filter(Boolean);
//////// Prioritized by prefix /////////
let result = [];
const startsWithNumber = /^\d/.test(root.searchingText);
const startsWithMathPrefix = root.searchingText.startsWith(Config.options.search.prefix.math);
const startsWithShellCommandPrefix = root.searchingText.startsWith(Config.options.search.prefix.shellCommand);
const startsWithWebSearchPrefix = root.searchingText.startsWith(Config.options.search.prefix.webSearch);
if (startsWithNumber || startsWithMathPrefix) {
result.push(mathResultObject);
} else if (startsWithShellCommandPrefix) {
result.push(commandResultObject);
} else if (startsWithWebSearchPrefix) {
result.push(webSearchResultObject);
}
//////////////// Apps //////////////////
result = result.concat(appResultObjects);
////////// Launcher actions ////////////
result = result.concat(launcherActionObjects);
/// Math result, command, web search ///
if (Config.options.search.prefix.showDefaultActionsWithoutPrefix) {
if (!startsWithShellCommandPrefix) result.push(commandResultObject);
if (!startsWithNumber && !startsWithMathPrefix) result.push(mathResultObject);
if (!startsWithWebSearchPrefix) result.push(webSearchResultObject);
}
return result;
values: LauncherSearch.results
onValuesChanged: {
root.focusFirstItem();
}
}
@@ -66,7 +66,7 @@ BarButton {
}
}
AppIcon {
WAppIcon {
id: iconWidget
anchors.centerIn: parent
iconName: root.iconName
@@ -5,17 +5,12 @@ import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
WButton {
AcrylicButton {
id: root
property var altAction: () => {}
property var middleClickAction: () => {}
colBackground: ColorUtils.transparentize(Looks.colors.bg1)
colBackgroundHover: Looks.colors.bg1Hover
colBackgroundActive: Looks.colors.bg1Active
property color colBackgroundBorder
property color color
Layout.fillHeight: true
topInset: 4
bottomInset: 4
@@ -23,16 +18,7 @@ WButton {
rightInset: 0
horizontalPadding: 8
colBackgroundBorder: ColorUtils.transparentize(Looks.colors.bg1Border, (root.checked || root.hovered) ? Looks.backgroundTransparency : 1)
color: {
if (root.down) {
return root.colBackgroundActive
} else if ((root.hovered && !root.down) || root.checked) {
return root.colBackgroundHover
} else {
return root.colBackground
}
}
colBackground: ColorUtils.transparentize(Looks.colors.bg1)
MouseArea {
anchors.fill: parent
@@ -50,15 +36,4 @@ WButton {
}
}
background: AcrylicRectangle {
shiny: ((root.hovered && !root.down) || root.checked)
color: root.color
radius: Looks.radius.medium
border.width: 1
border.color: root.colBackgroundBorder
Behavior on border.color {
animation: Looks.transition.color.createObject(this)
}
}
}
@@ -7,14 +7,16 @@ import qs.services
import qs.modules.common
import qs.modules.waffle.looks
// TODO: Replace the icon with QMLized svg (with /usr/lib/qt6/bin/svgtoqml) for proper micro-animation
AppButton {
id: root
leftInset: Config.options.waffles.bar.leftAlignApps ? 12 : 0
iconName: down ? "start-here-pressed" : "start-here"
checked: GlobalStates.searchOpen
onClicked: {
GlobalStates.overviewOpen = !GlobalStates.overviewOpen; // For now...
GlobalStates.searchOpen = !GlobalStates.searchOpen;
}
BarToolTip {
@@ -42,7 +42,7 @@ AppButton {
}
spacing: 6
AppIcon {
WAppIcon {
id: iconWidget
anchors.verticalCenter: parent.verticalCenter
iconName: root.iconName
@@ -43,7 +43,7 @@ Button {
Layout.fillHeight: false
spacing: 8
AppIcon {
WAppIcon {
id: appIcon
Layout.leftMargin: Looks.radius.large - root.padding + 2
Layout.alignment: Qt.AlignVCenter
@@ -0,0 +1,42 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
WButton {
id: root
colBackground: Looks.colors.bg1
colBackgroundHover: Looks.colors.bg1Hover
colBackgroundActive: Looks.colors.bg1Active
property color colBackgroundBorder
property color color
property alias border: background.border
property alias shinyColor: background.borderColor
colBackgroundBorder: ColorUtils.transparentize(color, (root.checked || root.hovered) ? Looks.backgroundTransparency : 0)
color: {
if (root.down) {
return root.colBackgroundActive
} else if ((root.hovered && !root.down) || root.checked) {
return root.colBackgroundHover
} else {
return root.colBackground
}
}
background: AcrylicRectangle {
id: background
shiny: ((root.hovered && !root.down) || root.checked)
color: root.color
radius: Looks.radius.medium
border.width: 1
border.color: root.colBackgroundBorder
Behavior on border.color {
animation: Looks.transition.color.createObject(this)
}
}
}
@@ -9,16 +9,17 @@ Rectangle {
id: root
property bool shiny: true // Top border
property color borderColor: ColorUtils.transparentize(Looks.colors.bg2Border, shiny ? 0.5 : 1)
property color borderColor: ColorUtils.transparentize(Looks.colors.bg1Hover, 0.7)
property color internalBorderColor: ColorUtils.transparentize(borderColor, shiny ? 0.0 : 1)
color: Looks.colors.bg1Hover
radius: Looks.radius.medium
Behavior on color {
animation: Looks.transition.color.createObject(this)
}
Behavior on borderColor {
Behavior on internalBorderColor {
animation: Looks.transition.color.createObject(this)
}
onBorderColorChanged: {
onInternalBorderColorChanged: {
borderCanvas.requestPaint();
}
@@ -32,7 +33,7 @@ Rectangle {
var ctx = getContext("2d");
ctx.clearRect(0, 0, width, height);
var borderColor = root.borderColor;
var borderColor = root.internalBorderColor;
var r = root.radius;
var fadeLength = Math.max(1, r);
@@ -53,6 +53,8 @@ Singleton {
property color controlBgHover: '#57575B'
property color controlFg: "#FFFFFF"
property color accentUnfocused: "#848484"
property color link: "#235CCF"
property color inputBg: ColorUtils.transparentize(bg0, 0.4)
}
darkColors: QtObject {
id: darkColors
@@ -70,7 +72,7 @@ Singleton {
property color bg2: '#8a8a8a'
property color bg2Hover: '#b1b1b1'
property color bg2Active: '#919191'
property color bg2Border: '#c4c4c4'
property color bg2Border: '#bdbdbd'
property color subfg: "#CED1D7"
property color fg: "#FFFFFF"
property color fg1: "#D1D1D1"
@@ -80,6 +82,8 @@ Singleton {
property color controlBgHover: "#CFCED1"
property color controlFg: "#454545"
property color accentUnfocused: "#989898"
property color link: "#A7C9FC"
property color inputBg: ColorUtils.transparentize(darkColors.bg0, 0.5)
}
colors: QtObject {
id: colors
@@ -110,6 +114,8 @@ Singleton {
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"
@@ -118,6 +124,7 @@ Singleton {
property color accentActive: Appearance.colors.colPrimaryActive
property color accentUnfocused: root.dark ? root.darkColors.accentUnfocused : root.lightColors.accentUnfocused
property color accentFg: ColorUtils.isDark(accent) ? "#FFFFFF" : "#000000"
property color selection: Appearance.colors.colPrimaryContainer
}
radius: QtObject {
@@ -2,7 +2,6 @@ import QtQuick
import org.kde.kirigami as Kirigami
import qs.services
import qs.modules.common
import qs.modules.waffle.looks
Kirigami.Icon {
id: root
@@ -53,10 +53,11 @@ Button {
// Hover stuff
signal hoverTimedOut
property bool shouldShowTooltip: false
ToolTip.delay: 400
property Timer hoverTimer: Timer {
id: hoverTimer
running: root.hovered
interval: 400
interval: root.ToolTip.delay
onTriggered: {
root.hoverTimedOut();
}
@@ -7,6 +7,10 @@ import qs.services
Singleton {
id: root
function pathForName(iconName) {
return Quickshell.shellPath(`assets/icons/fluent/${iconName}.svg`);
}
function wifiIconForStrength(strength) {
if (strength > 75)
return "wifi-1";
@@ -0,0 +1,83 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Hyprland
import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
Menu {
id: root
property bool downDirection: false
property bool hasIcons: false // TODO: implement
implicitWidth: background.implicitWidth + root.padding * 2
implicitHeight: background.implicitHeight + root.padding * 2
padding: 3
property real sourceEdgeMargin: -implicitHeight
clip: true
enter: Transition {
NumberAnimation {
property: "sourceEdgeMargin"
from: -root.implicitHeight
to: root.padding
duration: 200
easing.type: Easing.BezierSpline
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn
}
}
exit: Transition {
NumberAnimation {
property: "sourceEdgeMargin"
from: root.padding
to: -root.implicitHeight
duration: 150
easing.type: Easing.BezierSpline
easing.bezierCurve: Looks.transition.easing.bezierCurve.easeOut
}
}
background: WPane {
anchors {
left: parent.left
right: parent.right
top: root.downDirection ? parent.top : undefined
bottom: root.downDirection ? undefined : parent.bottom
margins: root.padding
topMargin: root.downDirection ? root.sourceEdgeMargin : root.padding
bottomMargin: root.downDirection ? root.padding : root.sourceEdgeMargin
}
contentItem: Rectangle {
color: Looks.colors.bg1Base
implicitWidth: menuListView.implicitWidth + root.padding * 2
implicitHeight: root.contentItem.implicitHeight + root.padding * 2
}
}
contentItem: ListView {
id: menuListView
anchors {
left: parent.left
right: parent.right
top: root.downDirection ? parent.top : undefined
bottom: root.downDirection ? undefined : parent.bottom
margins: root.padding * 2
topMargin: root.downDirection ? root.sourceEdgeMargin : root.padding
bottomMargin: root.downDirection ? root.padding : root.sourceEdgeMargin
}
implicitHeight: contentHeight
implicitWidth: Array.from({
length: count
}, (_, i) => itemAtIndex(i)?.implicitWidth ?? 0).reduce((a, b) => a > b ? a : b)
model: root.contentModel
}
delegate: WMenuItem {
id: menuItemDelegate
}
}
@@ -0,0 +1,93 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Hyprland
import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
MenuItem {
id: root
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 colForeground: Looks.colors.fg
property color colForegroundToggled: Looks.colors.accentFg
property color colForegroundDisabled: ColorUtils.transparentize(Looks.colors.subfg, 0.4)
property color color: {
if (!root.enabled)
return colBackground;
if (root.checked) {
if (root.down) {
return root.colBackgroundToggledActive;
} else if (root.hovered) {
return root.colBackgroundToggledHover;
} else {
return root.colBackgroundToggled;
}
}
if (root.down) {
return root.colBackgroundActive;
} else if (root.hovered) {
return root.colBackgroundHover;
} else {
return root.colBackground;
}
}
property color fgColor: {
if (root.checked)
return root.colForegroundToggled;
if (root.enabled)
return root.colForeground;
return root.colForegroundDisabled;
}
property real inset: 2
topInset: inset
bottomInset: inset
leftInset: inset
rightInset: inset
horizontalPadding: 11
background: Rectangle {
id: backgroundRect
radius: Looks.radius.medium
color: root.color
Behavior on color {
animation: Looks.transition.color.createObject(this)
}
}
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
}
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
}
}
}
@@ -10,7 +10,7 @@ import qs.modules.waffle.looks
Item {
id: root
required property Item contentItem
property Item contentItem
property real radius: Looks.radius.large
property alias border: borderRect
property alias borderColor: borderRect.border.color
@@ -8,8 +8,11 @@ Text {
color: Looks.colors.fg
font {
hintingPreference: Font.PreferFullHinting
family: Looks.font.family.ui
pixelSize: Looks.font.pixelSize.normal
weight: Looks.font.weight.regular
}
linkColor: Looks.colors.link
}
@@ -0,0 +1,18 @@
import QtQuick
import QtQuick.Controls
TextInput {
id: root
renderType: Text.NativeRendering
verticalAlignment: Text.AlignVCenter
color: Looks.colors.fg
font {
hintingPreference: Font.PreferFullHinting
family: Looks.font.family.ui
pixelSize: Looks.font.pixelSize.large
weight: Looks.font.weight.regular
}
selectionColor: Looks.colors.selection
}
@@ -25,6 +25,8 @@ StyledToolTip {
verticalPadding: 8
horizontalPadding: 10
delay: 400
contentItem: WToolTipContent {
id: tooltipContent
realContentItem: root.realContentItem
@@ -6,6 +6,7 @@ Item {
id: root
anchors.centerIn: parent
required property Item realContentItem
property alias radius: realContent.radius
property real verticalPadding: 8
property real horizontalPadding: 10
implicitWidth: realContent.implicitWidth + 2 * 2
@@ -0,0 +1,27 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets
import qs.modules.waffle.looks
StyledImage {
id: avatar
Layout.alignment: Qt.AlignTop
sourceSize: Qt.size(32, 32)
source: Directories.userAvatarPathAccountsService
fallbacks: [Directories.userAvatarPathRicersAndWeirdSystems, Directories.userAvatarPathRicersAndWeirdSystems2]
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Circle {
diameter: avatar.height
}
}
}
@@ -20,6 +20,7 @@ WBarAttachedPanelContent {
property bool collapsed: false
contentItem: ColumnLayout {
id: contentLayout
anchors {
horizontalCenter: parent.horizontalCenter
top: parent.top
@@ -41,9 +42,24 @@ WBarAttachedPanelContent {
}
contentItem: NotificationPaneContent {
implicitWidth: calendarColumnLayout.implicitWidth
implicitHeight: Notifications.list.length > 0 ? (notificationArea.height - notificationPane.borderWidth * 2) : 230
implicitHeight: {
if (Notifications.list.length > 0) {
return ((contentLayout.height - calendarPane.height - contentLayout.spacing) - notificationPane.borderWidth * 2)
}
return 230;
}
Timer {
id: enableTimer
interval: Config.options.hacks.arbitraryRaceConditionDelay
onTriggered: heightBehavior.enabled = true;
}
Behavior on implicitHeight {
id: heightBehavior
enabled: false
Component.onCompleted: {
enableTimer.restart();
}
animation: Looks.transition.enter.createObject(this)
}
}
@@ -51,9 +67,9 @@ WBarAttachedPanelContent {
}
WPane {
contentItem: ColumnLayout {
id: calendarPane
contentItem: WPanelPageColumn {
id: calendarColumnLayout
spacing: 0
DateHeader {
Layout.fillWidth: true
Synchronizer on collapsed {
@@ -8,18 +8,24 @@ import qs.modules.common.functions
import qs.modules.waffle.looks
WBorderlessButton {
id: headerButton
id: root
Layout.fillWidth: false
implicitWidth: 16
implicitHeight: 16
property real implicitSize: 16
implicitWidth: implicitSize
implicitHeight: implicitSize
color: "transparent"
colForeground: root.hovered && !root.pressed ? Looks.colors.fg : Looks.colors.fg1
Behavior on colForeground {
animation: Looks.transition.color.createObject(this)
}
contentItem: Item {
FluentIcon {
anchors.centerIn: parent
implicitSize: 16
icon: headerButton.icon.name
color: headerButton.hovered && !headerButton.pressed ? Looks.colors.fg : Looks.colors.fg1
implicitSize: root.implicitSize
icon: root.icon.name
color: root.colForeground
}
}
}
@@ -2,18 +2,27 @@ import QtQuick
import qs
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
SmallBorderedIconButton {
AcrylicButton {
id: root
property bool iconVisible: true
property string iconName: ""
property bool iconFilled: true
colBackground: Looks.colors.bg2
colBackgroundHover: Looks.colors.bg2Hover
colBackgroundActive: Looks.colors.bg2Active
property color colBorder: Looks.colors.bg2Border
property color colBorderToggled: Looks.colors.accent
border.color: checked ? colBorderToggled : colBorder
leftPadding: 12
rightPadding: 12
implicitWidth: focusButtonContent.implicitWidth + leftPadding + rightPadding
implicitHeight: 24
contentItem: Row {
id: focusButtonContent
@@ -7,11 +7,13 @@ import qs.modules.common.widgets
import qs.modules.common.functions
import qs.modules.waffle.looks
// TODO: Swipe to dismiss
MouseArea {
id: root
required property var notificationGroup
readonly property var notifications: notificationGroup?.notifications ?? []
property bool expanded: false
implicitWidth: contentLayout.implicitWidth
implicitHeight: contentLayout.implicitHeight
@@ -34,12 +36,23 @@ MouseArea {
interactive: false
spacing: 4
model: ScriptModel {
values: root.notifications.slice().reverse()
values: root.expanded ? root.notifications.slice().reverse() : root.notifications.slice(-1)
objectProp: "notificationId"
}
delegate: WSingleNotification {
required property int index
required property var modelData
width: ListView.view.width
notification: modelData
groupExpandControlMessage: {
if (root.notifications.length <= 1) return "";
if (!root.expanded) return Translation.tr("+%1 notifications").arg(root.notifications.length - 1);
if (index === root.notifications.length - 1) return Translation.tr("See fewer");
return "";
}
onGroupExpandToggle: {
root.expanded = !root.expanded;
}
}
}
}
@@ -1,3 +1,4 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import Quickshell
@@ -12,11 +13,18 @@ MouseArea {
id: root
required property var notification
property bool expanded: false
property bool expanded: notification.actions.length > 0
property string groupExpandControlMessage: ""
signal groupExpandToggle
hoverEnabled: true
implicitHeight: contentItem.implicitHeight
implicitWidth: contentItem.implicitWidth
Behavior on implicitHeight {
animation: Looks.transition.enter.createObject(this)
}
Rectangle {
id: contentItem
anchors.fill: parent
@@ -26,34 +34,205 @@ MouseArea {
implicitHeight: notificationContent.implicitHeight + padding * 2
implicitWidth: notificationContent.implicitWidth + padding * 2
border.width: 1
border.color: Looks.applyContentTransparency(Looks.colors.ambientShadow)
border.color: ColorUtils.applyAlpha(Looks.colors.ambientShadow, 0.1)
ColumnLayout {
id: notificationContent
anchors.fill: parent
anchors.margins: contentItem.padding
spacing: 19
RowLayout {
// Header
SingleNotificationHeader {
Layout.fillWidth: true
WText {
text: NotificationUtils.getFriendlyNotifTimeString(root.notification?.time)
}
// Content
Item {
id: actualContent
Layout.fillWidth: true
Layout.fillHeight: true
property real spacing: 16
implicitHeight: Math.max(contentColumn.implicitHeight, imageLoader.height)
implicitWidth: contentColumn.implicitWidth
Loader {
id: imageLoader
anchors {
top: parent.top
left: parent.left
}
active: root.notification.image != ""
sourceComponent: StyledImage {
readonly property int size: 48
width: size
height: size
sourceSize.width: size
sourceSize.height: size
source: root.notification.image
fillMode: Image.PreserveAspectFit
}
}
ColumnLayout {
id: contentColumn
anchors {
top: parent.top
left: parent.left
right: parent.right
}
spacing: 3
SummaryText {
id: summaryText
Layout.leftMargin: imageLoader.active ? imageLoader.width + actualContent.spacing : 0
}
BodyText {
Layout.leftMargin: imageLoader.active ? imageLoader.width + actualContent.spacing : 0
// onLineLaidOut: (line) => {
// if (!imageLoader.active) return;
// const dodgeDistance = imageLoader.width + actualContent.spacing;
// // print(line.y, dodgeDistance)
// if (summaryText.height + line.y > dodgeDistance) {
// line.x -= dodgeDistance;
// line.width += dodgeDistance;
// }
// }
}
}
}
ColumnLayout {
// Actions
ActionsRow {
Layout.fillWidth: true
WText {
Layout.fillWidth: true
elide: Text.ElideRight
text: root.notification.summary
}
WText {
Layout.fillWidth: true
elide: Text.ElideRight
}
// "+1 notifications" button
GroupExpandButton {
Layout.bottomMargin: 2
}
}
}
component SingleNotificationHeader: RowLayout {
ExpandButton {
Layout.topMargin: -2
}
Item {
Layout.fillWidth: true
}
NotificationHeaderButton {
Layout.rightMargin: 4
opacity: root.containsMouse ? 1 : 0
icon.name: "dismiss"
implicitSize: 12
onClicked: {
Qt.callLater(() => {
Notifications.discardNotification(root.notification?.notificationId);
});
}
}
}
component ActionsRow: RowLayout {
visible: root.expanded && root.notification.actions.length > 0
uniformCellSizes: true
Repeater {
id: actionRepeater
model: root.notification.actions
delegate: WBorderedButton {
id: actionButton
Layout.fillHeight: true
required property var modelData
Layout.fillWidth: true
verticalPadding: 16
horizontalPadding: 12
text: modelData.text
implicitHeight: actionButtonText.implicitHeight + verticalPadding * 2
contentItem: WText {
id: actionButtonText
text: actionButton.text
font.pixelSize: Looks.font.pixelSize.large
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap
maximumLineCount: root.expanded ? 100 : 1
}
}
}
}
component SummaryText: WText {
Layout.fillWidth: true
elide: Text.ElideRight
text: root.notification?.summary
font.pixelSize: Looks.font.pixelSize.large
}
component BodyText: WText {
Layout.fillWidth: true
Layout.fillHeight: true
elide: Text.ElideRight
verticalAlignment: Text.AlignTop
wrapMode: Text.Wrap
maximumLineCount: root.expanded ? 100 : 1
text: {
if (root.expanded)
return `<style>img{max-width:${summaryText.width}px; align: right}</style>` + `${NotificationUtils.processNotificationBody(root.notification.body, root.notification.appName || root.notification.summary).replace(/\n/g, "<br/>")}`;
return NotificationUtils.processNotificationBody(root.notification.body, root.notification.appName || root.notification.summary).replace(/\n/g, "<br/>");
}
color: Looks.colors.subfg
textFormat: root.expanded ? Text.RichText : Text.StyledText
onLinkActivated: link => {
Qt.openUrlExternally(link);
GlobalStates.sidebarRightOpen = false;
}
}
component ExpandButton: NotificationHeaderButton {
id: expandButton
implicitWidth: expandButtonContent.implicitWidth
onClicked: root.expanded = !root.expanded
contentItem: Item {
id: expandButtonContent
implicitWidth: expandButtonRow.implicitWidth
implicitHeight: expandButtonRow.implicitHeight
RowLayout {
id: expandButtonRow
anchors.centerIn: parent
spacing: 8
WText {
color: expandButton.colForeground
text: NotificationUtils.getFriendlyNotifTimeString(root.notification?.time)
}
FluentIcon {
Layout.rightMargin: 12
icon: "chevron-down"
implicitSize: 18
rotation: root.expanded ? -180 : 0
color: expandButton.colForeground
Behavior on rotation {
animation: Looks.transition.rotate.createObject(this)
}
}
}
}
}
component GroupExpandButton: AcrylicButton {
id: groupExpandButton
visible: root.groupExpandControlMessage !== ""
horizontalPadding: 10
implicitHeight: 24
implicitWidth: expandButtonText.implicitWidth + horizontalPadding * 2
onClicked: root.groupExpandToggle()
contentItem: Item {
WText {
id: expandButtonText
anchors.centerIn: parent
text: root.groupExpandControlMessage
}
}
}
}
@@ -0,0 +1,84 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
FooterRectangle {
id: root
property bool searching: text.length > 0
property alias text: searchInput.text
Component.onCompleted: searchInput.forceActiveFocus()
focus: true
color: searching ? Looks.colors.bgPanelBody : Looks.colors.bgPanelFooter
implicitWidth: 832 // TODO: Make sizes naturally inferred
implicitHeight: 63
Rectangle {
id: outline
anchors {
fill: parent
leftMargin: 32
rightMargin: 32
topMargin: 16
bottomMargin: 15
}
color: "transparent"
radius: height / 2
border.width: 1
border.color: Looks.colors.bg2Border
}
Rectangle {
id: searchInputBg
anchors.fill: outline
anchors.margins: 1
radius: height / 2
color: Looks.colors.inputBg
RowLayout {
anchors.fill: parent
spacing: 11
WAppIcon {
Layout.leftMargin: 14
iconName: "system-search-checked"
separateLightDark: true
implicitSize: 18
}
WTextInput {
id: searchInput
focus: true
Layout.fillWidth: true
WText {
anchors {
left: parent.left
verticalCenter: parent.verticalCenter
}
color: Looks.colors.accentUnfocused
text: Translation.tr("Search for apps") // should also have "", settings, and documents" but we don't have those
visible: searchInput.text.length === 0
font.pixelSize: Looks.font.pixelSize.large
}
}
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.IBeamCursor
acceptedButtons: Qt.NoButton
}
}
@@ -0,0 +1,16 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
BodyRectangle {
id: root
}
@@ -0,0 +1,39 @@
pragma ComponentBehavior: Bound
import Qt.labs.synchronizer
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks
WBarAttachedPanelContent {
id: root
property bool searching: false
property string searchText: ""
contentItem: WPane {
contentItem: WPanelPageColumn {
SearchBar {
focus: true
Layout.fillWidth: true
Synchronizer on searching {
property alias target: root.searching
}
Synchronizer on text {
property alias source: root.searchText
}
}
Loader {
id: pageContentLoader
Layout.fillWidth: true
source: root.searching ? "SearchPageContent.qml" : "StartPageContent.qml"
}
}
}
}
@@ -0,0 +1,227 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Qt5Compat.GraphicalEffects
import Quickshell
import qs
import qs.services
import qs.modules.common
import qs.modules.common.functions
import qs.modules.common.widgets
import qs.modules.waffle.looks
WPanelPageColumn {
id: root
WPanelSeparator {}
BodyRectangle {
implicitHeight: 736 // TODO: Make sizes naturally inferred
}
WPanelSeparator {}
StartFooter {
Layout.fillWidth: true
}
component StartFooter: FooterRectangle {
implicitHeight: 63
UserButton {
anchors {
left: parent.left
leftMargin: 52
bottom: parent.bottom
bottomMargin: 12
}
}
PowerButton {
anchors {
right: parent.right
rightMargin: 52
bottom: parent.bottom
bottomMargin: 12
}
}
}
component UserButton: WBorderlessButton {
id: userButton
implicitWidth: userButtonRow.implicitWidth + 12 * 2
implicitHeight: 40
contentItem: Item {
RowLayout {
id: userButtonRow
anchors.centerIn: parent
spacing: 12
WUserAvatar {
sourceSize: Qt.size(32, 32)
}
WText {
Layout.alignment: Qt.AlignVCenter
text: SystemInfo.username
}
}
}
onClicked: {
userMenu.open();
}
WToolTip {
text: SystemInfo.username
}
Popup {
id: userMenu
x: -51
y: -userMenu.implicitHeight + userButton.implicitHeight / 2 - 10
background: null
WToolTipContent {
id: popupContent
horizontalPadding: 10
verticalPadding: 7
radius: Looks.radius.large
realContentItem: Item {
implicitWidth: userMenuContentLayout.implicitWidth
implicitHeight: userMenuContentLayout.implicitHeight
ColumnLayout {
id: userMenuContentLayout
anchors {
fill: parent
leftMargin: popupContent.horizontalPadding
rightMargin: popupContent.horizontalPadding
topMargin: popupContent.verticalPadding
bottomMargin: popupContent.verticalPadding
}
spacing: 5
RowLayout {
Layout.fillWidth: true
Layout.leftMargin: 6
FluentIcon {
Layout.alignment: Qt.AlignVCenter
implicitSize: 22
icon: "corporation"
monochrome: false
}
WText {
Layout.alignment: Qt.AlignVCenter
text: "Megahard"
font.pixelSize: Looks.font.pixelSize.large
font.weight: Looks.font.weight.strong
}
Item { Layout.fillWidth: true }
WBorderlessButton {
Layout.alignment: Qt.AlignVCenter
implicitHeight: 36
implicitWidth: textItem.implicitWidth + 10 * 2
contentItem: WText {
id: textItem
text: Translation.tr("Sign out")
font.pixelSize: Looks.font.pixelSize.large
}
onClicked: Session.logout()
}
}
Item { // Force min width 360 (using min on the item somehow doesn't work)
implicitWidth: 334
}
RowLayout {
Layout.fillWidth: true
Layout.bottomMargin: 7
Layout.leftMargin: 6
spacing: 12
WUserAvatar {
sourceSize: Qt.size(58, 58)
}
ColumnLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
spacing: 2
WText {
text: SystemInfo.username
font.pixelSize: Looks.font.pixelSize.larger
font.weight: Looks.font.weight.strong
}
WText {
color: Looks.colors.fg1
text: Translation.tr("Local account")
}
WText {
color: Looks.colors.accent
text: Translation.tr("Manage my account")
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
Quickshell.execDetached(["bash", "-c", Config.options.apps.manageUser])
GlobalStates.searchOpen = false;
}
}
}
}
}
}
}
}
}
}
component PowerButton: WBorderlessButton {
id: powerButton
implicitWidth: 40
implicitHeight: 40
contentItem: Item {
FluentIcon {
anchors.centerIn: parent
icon: "power"
implicitSize: 20
}
}
WToolTip {
extraVisibleCondition: !powerMenu.visible
text: qsTr("Power")
}
onClicked: {
powerMenu.open()
}
WMenu {
id: powerMenu
x: -powerMenu.implicitWidth / 2 + powerButton.implicitWidth / 2
y: -powerMenu.implicitHeight - 4
Action {
icon.name: "lock-closed"
text: Translation.tr("Lock")
onTriggered: Session.lock()
}
Action {
icon.name: "weather-moon"
text: Translation.tr("Sleep")
onTriggered: Session.suspend()
}
Action {
icon.name: "power"
text: Translation.tr("Shut down")
onTriggered: Session.poweroff()
}
Action {
icon.name: "arrow-counterclockwise"
text: Translation.tr("Restart")
onTriggered: Session.reboot()
}
}
}
}
@@ -0,0 +1,119 @@
import QtQuick
import Quickshell
import Quickshell.Io
import Quickshell.Wayland
import Quickshell.Hyprland
import qs
import qs.services
import qs.modules.common
import qs.modules.common.widgets
Scope {
id: root
Connections {
target: GlobalStates
function onSearchOpenChanged() {
if (GlobalStates.searchOpen)
panelLoader.active = true;
}
}
Loader {
id: panelLoader
active: GlobalStates.searchOpen
sourceComponent: PanelWindow {
id: panelWindow
exclusiveZone: 0
WlrLayershell.namespace: "quickshell:wStartMenu"
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
color: "transparent"
anchors {
bottom: Config.options.waffles.bar.bottom
top: !Config.options.waffles.bar.bottom
left: Config.options.waffles.bar.leftAlignApps
}
implicitWidth: content.implicitWidth
implicitHeight: content.implicitHeight
HyprlandFocusGrab {
id: focusGrab
active: true
windows: [panelWindow]
onCleared: content.close()
}
Connections {
target: GlobalStates
function onSearchOpenChanged() {
if (!GlobalStates.searchOpen)
content.close();
}
}
StartMenuContent {
id: content
anchors.fill: parent
focus: true
onClosed: {
GlobalStates.searchOpen = false;
panelLoader.active = false;
}
}
}
}
IpcHandler {
target: "search"
function toggle() {
GlobalStates.searchOpen = !GlobalStates.searchOpen;
}
function close() {
GlobalStates.searchOpen = false;
}
function open() {
GlobalStates.searchOpen = true;
}
function toggleReleaseInterrupt() {
GlobalStates.superReleaseMightTrigger = false;
}
}
GlobalShortcut {
name: "searchToggle"
description: "Toggles search on press"
onPressed: {
GlobalStates.searchOpen = !GlobalStates.searchOpen;
}
}
GlobalShortcut {
name: "searchToggleRelease"
description: "Toggles search on release"
onPressed: {
GlobalStates.superReleaseMightTrigger = true;
}
onReleased: {
if (!GlobalStates.superReleaseMightTrigger) {
GlobalStates.superReleaseMightTrigger = true;
return;
}
GlobalStates.searchOpen = !GlobalStates.searchOpen;
}
}
GlobalShortcut {
name: "searchToggleReleaseInterrupt"
description: "Interrupts possibility of search being toggled on release. " + "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. " + "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything."
onPressed: {
GlobalStates.superReleaseMightTrigger = false;
}
}
}
@@ -0,0 +1,278 @@
pragma Singleton
import qs.modules.common
import qs.modules.common.models
import qs.modules.common.functions
import QtQuick
import QtQuick.Controls
import Quickshell
import Quickshell.Io
Singleton {
id: root
property string query: ""
property var searchActions: [
{
action: "accentcolor",
execute: args => {
Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--noswitch", "--color", ...(args != '' ? [`${args}`] : [])]);
}
},
{
action: "dark",
execute: () => {
Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "dark", "--noswitch"]);
}
},
{
action: "konachanwallpaper",
execute: () => {
Quickshell.execDetached([Quickshell.shellPath("scripts/colors/random/random_konachan_wall.sh")]);
}
},
{
action: "light",
execute: () => {
Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "light", "--noswitch"]);
}
},
{
action: "superpaste",
execute: args => {
if (!/^(\d+)/.test(args.trim())) {
// Invalid if doesn't start with numbers
Quickshell.execDetached(["notify-send", Translation.tr("Superpaste"), Translation.tr("Usage: <tt>%1superpaste NUM_OF_ENTRIES[i]</tt>\nSupply <tt>i</tt> when you want images\nExamples:\n<tt>%1superpaste 4i</tt> for the last 4 images\n<tt>%1superpaste 7</tt> for the last 7 entries").arg(Config.options.search.prefix.action), "-a", "Shell"]);
return;
}
const syntaxMatch = /^(?:(\d+)(i)?)/.exec(args.trim());
const count = syntaxMatch[1] ? parseInt(syntaxMatch[1]) : 1;
const isImage = !!syntaxMatch[2];
Cliphist.superpaste(count, isImage);
}
},
{
action: "todo",
execute: args => {
Todo.addTask(args);
}
},
{
action: "wallpaper",
execute: () => {
GlobalStates.wallpaperSelectorOpen = true;
}
},
{
action: "wipeclipboard",
execute: () => {
Cliphist.wipe();
}
},
]
property string mathResult: ""
property bool clipboardWorkSafetyActive: {
const enabled = Config.options.workSafety.enable.clipboard;
const sensitiveNetwork = (StringUtils.stringListContainsSubstring(Network.networkName.toLowerCase(), Config.options.workSafety.triggerCondition.networkNameKeywords))
return enabled && sensitiveNetwork;
}
function containsUnsafeLink(entry) {
if (entry == undefined) return false;
const unsafeKeywords = Config.options.workSafety.triggerCondition.linkKeywords;
return StringUtils.stringListContainsSubstring(entry.toLowerCase(), unsafeKeywords);
}
Timer {
id: nonAppResultsTimer
interval: Config.options.search.nonAppResultDelay
onTriggered: {
let expr = root.query;
if (expr.startsWith(Config.options.search.prefix.math)) {
expr = expr.slice(Config.options.search.prefix.math.length);
}
mathProc.calculateExpression(expr);
}
}
Process {
id: mathProc
property list<string> baseCommand: ["qalc", "-t"]
function calculateExpression(expression) {
mathProc.running = false;
mathProc.command = baseCommand.concat(expression);
mathProc.running = true;
}
stdout: SplitParser {
onRead: data => {
root.mathResult = data;
}
}
}
property list<var> results: {
// Search results are handled here
////////////////// Skip? //////////////////
if (root.query == "")
return [];
///////////// Special cases ///////////////
if (root.query.startsWith(Config.options.search.prefix.clipboard)) {
// Clipboard
const searchString = StringUtils.cleanPrefix(root.query, Config.options.search.prefix.clipboard);
return Cliphist.fuzzyQuery(searchString).map((entry, index, array) => {
const mightBlurImage = Cliphist.entryIsImage(entry) && root.clipboardWorkSafetyActive;
let shouldBlurImage = mightBlurImage;
if (mightBlurImage) {
shouldBlurImage = shouldBlurImage && (root.containsUnsafeLink(array[index - 1]) || root.containsUnsafeLink(array[index + 1]));
}
const type = `#${entry.match(/^\s*(\S+)/)?.[1] || ""}`;
return {
key: type,
cliphistRawString: entry,
name: StringUtils.cleanCliphistEntry(entry),
clickActionName: "",
type: type,
execute: () => {
Cliphist.copy(entry);
},
actions: [
{
name: "Copy",
materialIcon: "content_copy",
execute: () => {
Cliphist.copy(entry);
}
},
{
name: "Delete",
materialIcon: "delete",
execute: () => {
Cliphist.deleteEntry(entry);
}
}
],
blurImage: shouldBlurImage,
blurImageText: Translation.tr("Work safety")
};
}).filter(Boolean);
} else if (root.query.startsWith(Config.options.search.prefix.emojis)) {
// Clipboard
const searchString = StringUtils.cleanPrefix(root.query, Config.options.search.prefix.emojis);
return Emojis.fuzzyQuery(searchString).map(entry => {
const emoji = entry.match(/^\s*(\S+)/)?.[1] || "";
return {
key: emoji,
cliphistRawString: entry,
bigText: emoji,
name: entry.replace(/^\s*\S+\s+/, ""),
clickActionName: "",
type: "Emoji",
execute: () => {
Quickshell.clipboardText = entry.match(/^\s*(\S+)/)?.[1];
}
};
}).filter(Boolean);
}
////////////////// Init ///////////////////
nonAppResultsTimer.restart();
const mathResultObject = {
key: `Math result: ${root.mathResult}`,
name: root.mathResult,
clickActionName: Translation.tr("Copy"),
type: Translation.tr("Math result"),
fontType: "monospace",
materialSymbol: 'calculate',
execute: () => {
Quickshell.clipboardText = root.mathResult;
}
};
const appResultObjects = AppSearch.fuzzyQuery(StringUtils.cleanPrefix(root.query, Config.options.search.prefix.app)).map(entry => {
entry.clickActionName = Translation.tr("Launch");
entry.type = Translation.tr("App");
entry.key = entry.execute;
return entry;
});
const commandResultObject = {
key: `cmd ${root.query}`,
name: StringUtils.cleanPrefix(root.query, Config.options.search.prefix.shellCommand).replace("file://", ""),
clickActionName: Translation.tr("Run"),
type: Translation.tr("Run command"),
fontType: "monospace",
materialSymbol: 'terminal',
execute: () => {
let cleanedCommand = root.query.replace("file://", "");
cleanedCommand = StringUtils.cleanPrefix(cleanedCommand, Config.options.search.prefix.shellCommand);
if (cleanedCommand.startsWith(Config.options.search.prefix.shellCommand)) {
cleanedCommand = cleanedCommand.slice(Config.options.search.prefix.shellCommand.length);
}
Quickshell.execDetached(["bash", "-c", searchingText.startsWith('sudo') ? `${Config.options.apps.terminal} fish -C '${cleanedCommand}'` : cleanedCommand]);
}
};
const webSearchResultObject = {
key: `website ${root.query}`,
name: StringUtils.cleanPrefix(root.query, Config.options.search.prefix.webSearch),
clickActionName: Translation.tr("Search"),
type: Translation.tr("Search the web"),
materialSymbol: 'travel_explore',
execute: () => {
let query = StringUtils.cleanPrefix(root.query, Config.options.search.prefix.webSearch);
let url = Config.options.search.engineBaseUrl + query;
for (let site of Config.options.search.excludedSites) {
url += ` -site:${site}`;
}
Qt.openUrlExternally(url);
}
};
const launcherActionObjects = root.searchActions.map(action => {
const actionString = `${Config.options.search.prefix.action}${action.action}`;
if (actionString.startsWith(root.query) || root.query.startsWith(actionString)) {
return {
key: `Action ${actionString}`,
name: root.query.startsWith(actionString) ? root.query : actionString,
clickActionName: Translation.tr("Run"),
type: Translation.tr("Action"),
materialSymbol: 'settings_suggest',
execute: () => {
action.execute(root.query.split(" ").slice(1).join(" "));
}
};
}
return null;
}).filter(Boolean);
//////// Prioritized by prefix /////////
let result = [];
const startsWithNumber = /^\d/.test(root.query);
const startsWithMathPrefix = root.query.startsWith(Config.options.search.prefix.math);
const startsWithShellCommandPrefix = root.query.startsWith(Config.options.search.prefix.shellCommand);
const startsWithWebSearchPrefix = root.query.startsWith(Config.options.search.prefix.webSearch);
if (startsWithNumber || startsWithMathPrefix) {
result.push(mathResultObject);
} else if (startsWithShellCommandPrefix) {
result.push(commandResultObject);
} else if (startsWithWebSearchPrefix) {
result.push(webSearchResultObject);
}
//////////////// Apps //////////////////
result = result.concat(appResultObjects);
////////// Launcher actions ////////////
result = result.concat(launcherActionObjects);
/// Math result, command, web search ///
if (Config.options.search.prefix.showDefaultActionsWithoutPrefix) {
if (!startsWithShellCommandPrefix)
result.push(commandResultObject);
if (!startsWithNumber && !startsWithMathPrefix)
result.push(mathResultObject);
if (!startsWithWebSearchPrefix)
result.push(webSearchResultObject);
}
return result;
}
}
+4 -1
View File
@@ -33,6 +33,7 @@ import qs.modules.waffle.background
import qs.modules.waffle.bar
import qs.modules.waffle.notificationCenter
import qs.modules.waffle.onScreenDisplay
import qs.modules.waffle.startMenu
import QtQuick
import QtQuick.Window
@@ -77,11 +78,13 @@ ShellRoot {
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: "wNotificationCenter"; component: WaffleNotificationCenter {} }
PanelLoader { identifier: "wOnScreenDisplay"; component: WaffleOSD {} }
PanelLoader { identifier: "wStartMenu"; component: WaffleStartMenu {} }
ReloadPopup {}
component PanelLoader: LazyLoader {
@@ -94,7 +97,7 @@ ShellRoot {
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", "wNotificationCenter", "wOnScreenDisplay", "iiCheatsheet", "iiLock", "iiNotificationPopup", "iiOnScreenKeyboard", "iiOverlay", "iiOverview", "iiPolkit", "iiRegionSelector", "iiSessionScreen", "iiWallpaperSelector"],
"waffle": ["wActionCenter", "wBar", "wBackground", "wNotificationCenter", "wOnScreenDisplay", "wStartMenu", "iiCheatsheet", "iiLock", "iiNotificationPopup", "iiOnScreenKeyboard", "iiOverlay", "iiPolkit", "iiRegionSelector", "iiSessionScreen", "iiWallpaperSelector"],
})
function cyclePanelFamily() {
const currentIndex = families.indexOf(Config.options.panelFamily)
+3
View File
@@ -191,6 +191,9 @@ Tips:
- Used in Quickshell config.
- `wlogout`
- Used in Hyprland config.
- `libqalculate`
- Used in Quickshell config, providing math ability in searchbar.
- Note that `qalc` is the needed executable. In Arch Linux [libqalculate](https://archlinux.org/packages/extra/x86_64/libqalculate) provides it, but in Fedora [qalculate](https://packages.fedoraproject.org/pkgs/libqalculate/qalculate/fedora-43.html#files) does and [libqalculate](https://packages.fedoraproject.org/pkgs/libqalculate/libqalculate/fedora-43.html#files) does not.
# Actual packages
@@ -1,6 +1,6 @@
pkgname=illogical-impulse-widgets
pkgver=1.0
pkgrel=5
pkgrel=6
pkgdesc='Illogical Impulse Widget Dependencies'
arch=(any)
license=(None)
@@ -14,4 +14,5 @@ depends=(
songrec
translate-shell
wlogout
libqalculate
)
+3 -1
View File
@@ -181,6 +181,7 @@ packages = [
"hyprpicker",
"songrec",
"translate-shell",
"qalculate",
"wlogout"
]
@@ -191,4 +192,5 @@ packages = [
"plasma-systemmonitor",
"unzip"
]
install_opts = ["--setopt=install_weak_deps=False"]
install_opts = ["--setopt=install_weak_deps=False"]
@@ -21,6 +21,7 @@ RDEPEND="
app-misc/songrec
app-i18n/translate-shell
gui-apps/wlogout
sci-libs/libqalculate
"
##### CUSTOM EBUILDS
# app-misc/songrec
+32 -2
View File
@@ -37,8 +37,38 @@ As [commented](https://github.com/end-4/dots-hyprland/issues/1061#issuecomment-3
See also [caelestia-dots/shell#668](https://github.com/caelestia-dots/shell/issues/668).
### NixGL
On non-NixOS distros, packages installed via home-manager have problem accessing GPU, especially Hyprland because it requires GPU acceleration to launch. `nixGL` should be used to address the problem.
### GPU
On non-NixOS distros, packages installed via home-manager have problem accessing GPU, especially Hyprland because it requires GPU acceleration to launch.
~~`nixGL` should be used to address the problem.~~
Since home-manager 25.11, for non-NixOS just set the following:
```nix
targets.genericLinux.enable = true;
```
Then during building, home-manager will show a message to tell you running a command manually to configure GPU, like:
```bash
sudo /nix/store/<HASH>-non-nixos-gpu/bin/non-nixos-gpu-setup
```
It runs a bash script with following content:
```
#!/nix/store/<HASH>-bash-<VERSION>/bin/bash
set -e
# Install the systemd service file and ensure that the store path won't be
# garbage-collected as long as it's installed.
unit_path=/etc/systemd/system/non-nixos-gpu.service
ln -sf /nix/store/<HASH>-non-nixos-gpu/resources/non-nixos-gpu.service "$unit_path"
ln -sf "$unit_path" "/nix/var/nix"/gcroots/non-nixos-gpu.service
systemctl daemon-reload
systemctl enable non-nixos-gpu.service
systemctl restart non-nixos-gpu.service
```
_Note: it uses `systemctl`, maybe won't work for OpenRC..._
See [gpu-non-nixos](https://nix-community.github.io/home-manager/index.xhtml#sec-usage-gpu-non-nixos).
# Handling dot files
## Status
+11 -8
View File
@@ -3,23 +3,24 @@
description = "illogical-impulse";
inputs = {
# Qt 6.10 is not yet available from released version of nixpkgs.
#nixpkgs.url = "nixpkgs/nixos-25.05";
nixpkgs.url = "nixpkgs/nixos-unstable";
nixpkgs.url = "nixpkgs/nixos-25.11";
#nixpkgs.url = "nixpkgs/nixos-unstable";
home-manager = {
#url = "github:nix-community/home-manager/release-25.05";
url = "github:nix-community/home-manager/master";
url = "github:nix-community/home-manager/release-25.11";
#url = "github:nix-community/home-manager/master";
inputs.nixpkgs.follows = "nixpkgs";
};
nixgl.url = "github:nix-community/nixGL";
#nixgl.url = "github:nix-community/nixGL";
quickshell = {
url = "github:quickshell-mirror/quickshell/db1777c20b936a86528c1095cbcb1ebd92801402";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = { nixpkgs, home-manager, nixgl, quickshell, ... }:
outputs = { nixpkgs, home-manager,
#nixgl,
quickshell, ... }:
let
home_attrs = rec {
username = import ./username.nix;
@@ -36,7 +37,9 @@
homeConfigurations = {
illogical_impulse = home-manager.lib.homeManagerConfiguration {
inherit pkgs;
extraSpecialArgs = { inherit home_attrs nixgl quickshell; };
extraSpecialArgs = { inherit home_attrs
#nixgl
quickshell; };
modules = [
./home.nix
];
+14 -5
View File
@@ -1,8 +1,13 @@
{ config, lib, pkgs, nixgl, quickshell, home_attrs, ... }:
{ config, lib, pkgs,
#nixgl,
quickshell, home_attrs, ... }:
{
programs.home-manager.enable = true;
nixGL.packages = nixgl.packages;
nixGL.defaultWrapper = "mesa";
# Necessary for non-NixOS to handle GPU (since home-manager version 25.11)
targets.genericLinux.enable = true;
#nixGL.packages = nixgl.packages;
#nixGL.defaultWrapper = "mesa";
xdg.portal = {
enable = true;
@@ -27,7 +32,8 @@
systemd.enable = false; plugins = []; settings = {}; extraConfig = "";
enable = true;
## Use NixGL
package = config.lib.nixGL.wrap pkgs.hyprland;
#package = config.lib.nixGL.wrap pkgs.hyprland;
package = pkgs.hyprland;
};
home = {
@@ -167,6 +173,7 @@
songrec #songrec
translate-shell #translate-shell
wlogout #wlogout
libqalculate #libqalculate
]
++ [
@@ -174,7 +181,9 @@
### illogical-impulse-quickshell-git
#(config.lib.nixGL.wrap quickshell.packages.x86_64-linux.default)
(import ./quickshell.nix { inherit pkgs quickshell; nixGLWrap = config.lib.nixGL.wrap; })
(import ./quickshell.nix { inherit pkgs quickshell;
#nixGLWrap = config.lib.nixGL.wrap;
})
];
}//home_attrs;
}
+7 -3
View File
@@ -1,10 +1,14 @@
{ pkgs, quickshell, nixGLWrap, ... }:
{ pkgs, quickshell,
#nixGLWrap,
... }:
let
qs = nixGLWrap quickshell.packages.x86_64-linux.default;
#qs = nixGLWrap quickshell.packages.x86_64-linux.default;
qs = quickshell.packages.x86_64-linux.default;
in pkgs.stdenv.mkDerivation {
name = "illogical-impulse-quickshell-wrapper";
meta = with pkgs.lib; {
description = "Quickshell wrapped with NixGL + bundled Qt deps for home-manager usage";
#description = "Quickshell wrapped with NixGL + bundled Qt deps for home-manager usage";
description = "Quickshell bundled Qt deps for home-manager usage";
license = licenses.gpl3Only;
};
+3 -2
View File
@@ -32,8 +32,8 @@ function install_home-manager(){
try source $HOME/.nix-profile/etc/profile.d/hm-session-vars.sh
command -v $cmd && return
x nix-channel --add https://nixos.org/channels/nixos-25.05 nixpkgs-home
x nix-channel --add https://github.com/nix-community/home-manager/archive/release-25.05.tar.gz home-manager
x nix-channel --add https://nixos.org/channels/nixos-25.11 nixpkgs-home
x nix-channel --add https://github.com/nix-community/home-manager/archive/release-25.11.tar.gz home-manager
x nix-channel --update
x env NIX_PATH="nixpkgs=$HOME/.nix-defexpr/channels/nixpkgs-home" nix-shell '<home-manager>' -A install
@@ -56,6 +56,7 @@ function hm_deps(){
x home-manager switch --flake .#illogical_impulse \
--extra-experimental-features nix-command \
--extra-experimental-features flakes
x sudo /nix/store/*-non-nixos-gpu/bin/non-nixos-gpu-setup
cd $REPO_ROOT
x git rm -f "${SETUP_USERNAME_NIXFILE}"
}