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 # You can put extra environment variables here
# https://wiki.hyprland.org/Configuring/Environment-variables/ # 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 # You can make apps auto-start here
# Relevant Hyprland wiki section: https://wiki.hyprland.org/Configuring/Keywords/#executing # 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 ############# # ############ Wayland #############
env = ELECTRON_OZONE_PLATFORM_HINT,auto 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 = qs -c $qsConfig &
exec-once = ~/.config/hypr/custom/scripts/__restore_video_wallpaper.sh exec-once = ~/.config/hypr/custom/scripts/__restore_video_wallpaper.sh
# Input method
# exec-once = fcitx5
# Core components (authentication, lock screen, notification daemon) # Core components (authentication, lock screen, notification daemon)
exec-once = gnome-keyring-daemon --start --components=secrets exec-once = gnome-keyring-daemon --start --components=secrets
exec-once = hypridle exec-once = hypridle
+16 -16
View File
@@ -4,27 +4,27 @@
#! #!
##! Shell ##! Shell
# These absolutely need to be on top, or they won't work consistently # 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_L, Toggle search, global, quickshell:searchToggleRelease # Toggle search
bindid = Super, Super_R, Toggle overview, global, quickshell:overviewToggleRelease # [hidden] Toggle overview/launcher 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_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) 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] binditn = Super, catchall, global, quickshell:searchToggleReleaseInterrupt # [hidden]
bind = Ctrl, Super_L, global, quickshell:overviewToggleReleaseInterrupt # [hidden] bind = Ctrl, Super_L, global, quickshell:searchToggleReleaseInterrupt # [hidden]
bind = Ctrl, Super_R, global, quickshell:overviewToggleReleaseInterrupt # [hidden] bind = Ctrl, Super_R, global, quickshell:searchToggleReleaseInterrupt # [hidden]
bind = Super, mouse:272, global, quickshell:overviewToggleReleaseInterrupt # [hidden] bind = Super, mouse:272, global, quickshell:searchToggleReleaseInterrupt # [hidden]
bind = Super, mouse:273, global, quickshell:overviewToggleReleaseInterrupt # [hidden] bind = Super, mouse:273, global, quickshell:searchToggleReleaseInterrupt # [hidden]
bind = Super, mouse:274, global, quickshell:overviewToggleReleaseInterrupt # [hidden] bind = Super, mouse:274, global, quickshell:searchToggleReleaseInterrupt # [hidden]
bind = Super, mouse:275, global, quickshell:overviewToggleReleaseInterrupt # [hidden] bind = Super, mouse:275, global, quickshell:searchToggleReleaseInterrupt # [hidden]
bind = Super, mouse:276, global, quickshell:overviewToggleReleaseInterrupt # [hidden] bind = Super, mouse:276, global, quickshell:searchToggleReleaseInterrupt # [hidden]
bind = Super, mouse:277, global, quickshell:overviewToggleReleaseInterrupt # [hidden] bind = Super, mouse:277, global, quickshell:searchToggleReleaseInterrupt # [hidden]
bind = Super, mouse_up, global, quickshell:overviewToggleReleaseInterrupt # [hidden] bind = Super, mouse_up, global, quickshell:searchToggleReleaseInterrupt # [hidden]
bind = Super, mouse_down,global, quickshell:overviewToggleReleaseInterrupt # [hidden] bind = Super, mouse_down,global, quickshell:searchToggleReleaseInterrupt # [hidden]
bindit = ,Super_L, global, quickshell:workspaceNumber # [hidden] bindit = ,Super_L, global, quickshell:workspaceNumber # [hidden]
bindit = ,Super_R, 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, V, Clipboard history >> clipboard, global, quickshell:overviewClipboardToggle # Clipboard history >> clipboard
bindd = Super, Period, Emoji >> clipboard, global, quickshell:overviewEmojiToggle # Emoji >> 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, A, global, quickshell:sidebarLeftToggle # Toggle left sidebar
bind = Super+Alt, A, global, quickshell:sidebarLeftToggleDetach # [hidden] bind = Super+Alt, A, global, quickshell:sidebarLeftToggleDetach # [hidden]
bind = Super, B, global, quickshell:sidebarLeftToggle # [hidden] bind = Super, B, global, quickshell:sidebarLeftToggle # [hidden]
@@ -218,8 +218,8 @@ submap = global
#! #!
# Testing # 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, 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=Open profile image" -A "action2=Useless button" -A "action3=Cry more"); [[ $ACTION == *openImage ]] && xdg-open "/var/lib/AccountsService/icons/$USER"' # [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] bind = Super+Alt, Equal, exec, notify-send "Urgent notification" "Ah hell no" -u critical -a 'Hyprland keybind' # [hidden]
##! Session ##! Session
+2
View File
@@ -159,7 +159,9 @@ layerrule = animation slide right, quickshell:sidebarRight
layerrule = animation slide left, quickshell:sidebarLeft layerrule = animation slide left, quickshell:sidebarLeft
layerrule = animation slide, quickshell:verticalBar layerrule = animation slide, quickshell:verticalBar
layerrule = animation slide top, quickshell:wallpaperSelector layerrule = animation slide top, quickshell:wallpaperSelector
layerrule = noanim, quickshell:wNotificationCenter
layerrule = noanim, quickshell:wOnScreenDisplay layerrule = noanim, quickshell:wOnScreenDisplay
layerrule = noanim, quickshell:wStartMenu
# Launchers need to be FAST # Launchers need to be FAST
layerrule = noanim, gtk4-layer-shell layerrule = noanim, gtk4-layer-shell
@@ -20,6 +20,7 @@ Singleton {
property bool overlayOpen: false property bool overlayOpen: false
property bool overviewOpen: false property bool overviewOpen: false
property bool regionSelectorOpen: false property bool regionSelectorOpen: false
property bool searchOpen: false
property bool screenLocked: false property bool screenLocked: false
property bool screenLockContainsCharacters: false property bool screenLockContainsCharacters: false
property bool screenUnlockFailed: 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"> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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)"/> <svg
<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)"/> width="64"
<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)"/> height="64"
<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)"/> viewBox="0 0 64 64"
<defs> fill="none"
<radialGradient id="paint0_radial_520_19" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(4 4) rotate(45) scale(77.7817)"> version="1.1"
<stop stop-color="#0B9BFE"/> id="svg11"
<stop offset="1" stop-color="#0B9BFE"/> sodipodi:docname="start-here-pressed.svg"
</radialGradient> inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
<radialGradient id="paint1_radial_520_19" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(4 4) rotate(45) scale(77.7817)"> xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
<stop stop-color="#0B9BFE"/> xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
<stop offset="1" stop-color="#0B9BFE"/> xmlns="http://www.w3.org/2000/svg"
</radialGradient> xmlns:svg="http://www.w3.org/2000/svg">
<radialGradient id="paint2_radial_520_19" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(4 4) rotate(45) scale(77.7817)"> <sodipodi:namedview
<stop stop-color="#0B9BFE"/> id="namedview11"
<stop offset="1" stop-color="#0B9BFE"/> pagecolor="#ffffff"
</radialGradient> bordercolor="#000000"
<radialGradient id="paint3_radial_520_19" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(4 4) rotate(45) scale(77.7817)"> borderopacity="0.25"
<stop stop-color="#0B9BFE"/> inkscape:showpageshadow="2"
<stop offset="1" stop-color="#0B9BFE"/> inkscape:pageopacity="0.0"
</radialGradient> inkscape:pagecheckerboard="0"
</defs> 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> </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"> <?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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)"/> <svg
<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)"/> width="64"
<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)"/> height="64"
<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)"/> viewBox="0 0 64 64"
<defs> fill="none"
<radialGradient id="paint0_radial_519_6" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(4 4) rotate(45) scale(77.7817)"> version="1.1"
<stop stop-color="#81DFFF"/> id="svg11"
<stop offset="1" stop-color="#0A99F9"/> sodipodi:docname="start-here.svg"
</radialGradient> inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
<radialGradient id="paint1_radial_519_6" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(4 4) rotate(45) scale(77.7817)"> xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
<stop stop-color="#81DFFF"/> xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
<stop offset="1" stop-color="#0A99F9"/> xmlns="http://www.w3.org/2000/svg"
</radialGradient> xmlns:svg="http://www.w3.org/2000/svg">
<radialGradient id="paint2_radial_519_6" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(4 4) rotate(45) scale(77.7817)"> <sodipodi:namedview
<stop stop-color="#81DFFF"/> id="namedview11"
<stop offset="1" stop-color="#0A99F9"/> pagecolor="#ffffff"
</radialGradient> bordercolor="#000000"
<radialGradient id="paint3_radial_519_6" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(4 4) rotate(45) scale(77.7817)"> borderopacity="0.25"
<stop stop-color="#81DFFF"/> inkscape:showpageshadow="2"
<stop offset="1" stop-color="#0A99F9"/> inkscape:pageopacity="0.0"
</radialGradient> inkscape:pagecheckerboard="0"
</defs> 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> </svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

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

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

@@ -23,10 +23,10 @@
inkscape:pagecheckerboard="0" inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1" inkscape:deskcolor="#d1d1d1"
inkscape:zoom="6.5849319" inkscape:zoom="6.5849319"
inkscape:cx="26.95548" inkscape:cx="27.031411"
inkscape:cy="26.423963" inkscape:cy="26.423963"
inkscape:window-width="1257" inkscape:window-width="1621"
inkscape:window-height="1020" inkscape:window-height="820"
inkscape:window-x="0" inkscape:window-x="0"
inkscape:window-y="0" inkscape:window-y="0"
inkscape:window-maximized="1" inkscape:window-maximized="1"
@@ -35,10 +35,10 @@
id="defs2"> id="defs2">
<linearGradient <linearGradient
id="linearGradient919" id="linearGradient919"
x1="4.3106" x1="4.4818139"
x2="14.36" x2="14.188786"
y1="8.4665" y1="7.1660118"
y2="8.4665" y2="9.7669868"
gradientTransform="matrix(1.226575,0,0,1.226575,-0.82407803,-6.4497629)" gradientTransform="matrix(1.226575,0,0,1.226575,-0.82407803,-6.4497629)"
gradientUnits="userSpaceOnUse"> gradientUnits="userSpaceOnUse">
<stop <stop
@@ -48,7 +48,7 @@
style="stop-color:#5fe277;stop-opacity:1;" /> style="stop-color:#5fe277;stop-opacity:1;" />
<stop <stop
stop-color="#55b4ff" stop-color="#55b4ff"
offset="1" offset="0.40380999"
id="stop2" id="stop2"
style="stop-color:#0078d3;stop-opacity:1;" /> style="stop-color:#0078d3;stop-opacity:1;" />
</linearGradient> </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 JsonObject apps: JsonObject {
property string bluetooth: "kcmshell6 kcm_bluetooth" property string bluetooth: "kcmshell6 kcm_bluetooth"
property string network: "kcmshell6 kcm_networkmanagement" property string network: "kcmshell6 kcm_networkmanagement"
property string manageUser: "kcmshell6 kcm_users"
property string networkEthernet: "kcmshell6 kcm_networkmanagement" property string networkEthernet: "kcmshell6 kcm_networkmanagement"
property string taskManager: "plasma-systemmonitor --page-name Processes" property string taskManager: "plasma-systemmonitor --page-name Processes"
property string terminal: "kitty -1" // This is only for shell actions property string terminal: "kitty -1" // This is only for shell actions
@@ -1,6 +1,7 @@
pragma Singleton pragma Singleton
pragma ComponentBehavior: Bound pragma ComponentBehavior: Bound
import qs.services
import qs.modules.common.functions import qs.modules.common.functions
import QtCore import QtCore
import QtQuick import QtQuick
@@ -46,6 +47,9 @@ Singleton {
property string aiChats: FileUtils.trimFileProtocol(`${Directories.state}/user/ai/chats`) property string aiChats: FileUtils.trimFileProtocol(`${Directories.state}/user/ai/chats`)
property string aiTranslationScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/ai/gemini-translate.sh`) property string aiTranslationScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/ai/gemini-translate.sh`)
property string recordScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/videos/record.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 // Cleanup on init
Component.onCompleted: { Component.onCompleted: {
Quickshell.execDetached(["mkdir", "-p", `${shellConfig}`]) Quickshell.execDetached(["mkdir", "-p", `${shellConfig}`])
@@ -84,4 +84,28 @@ Singleton {
// Older dates // Older dates
return Qt.formatDateTime(messageTime, "MMMM dd"); 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 running: true
command: ["bash", "-c", 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 { stdout: StdioCollector {
id: imageSizeOutputCollector id: imageSizeOutputCollector
onStreamFinished: { onStreamFinished: {
const output = imageSizeOutputCollector.text.trim(); const output = imageSizeOutputCollector.text.trim();
const [width, height] = output.split(" ").map(Number); const match = output.match(/(\d+)\s*x\s*(\d+)/);
root.done(root.filePath, width, height);
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 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) { function destroyWithAnimation(left = false) {
root.qmlParent.resetDrag() root.qmlParent.resetDrag()
background.anchors.leftMargin = background.anchors.leftMargin; // Break binding background.anchors.leftMargin = background.anchors.leftMargin; // Break binding
@@ -196,12 +174,13 @@ Item { // Notification item area
maximumLineCount: 1 maximumLineCount: 1
textFormat: Text.StyledText textFormat: Text.StyledText
text: { 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 ColumnLayout { // Expanded content
id: expandedContentColumn
Layout.fillWidth: true Layout.fillWidth: true
opacity: root.expanded ? 1 : 0 opacity: root.expanded ? 1 : 0
visible: opacity > 0 visible: opacity > 0
@@ -218,8 +197,8 @@ Item { // Notification item area
elide: Text.ElideRight elide: Text.ElideRight
textFormat: Text.RichText textFormat: Text.RichText
text: { text: {
return `<style>img{max-width:${300 /* binding to notificationBodyText.width would cause a binding loop */}px;}</style>` + return `<style>img{max-width:${expandedContentColumn.width}px;}</style>` +
`${processNotificationBody(notificationObject.body, notificationObject.appName || notificationObject.summary).replace(/\n/g, "<br/>")}` `${NotificationUtils.processNotificationBody(notificationObject.body, notificationObject.appName || notificationObject.summary).replace(/\n/g, "<br/>")}`
} }
onLinkActivated: (link) => { onLinkActivated: (link) => {
@@ -293,6 +272,8 @@ Item { // Notification item area
id: actionRepeater id: actionRepeater
model: notificationObject.actions model: notificationObject.actions
NotificationActionButton { NotificationActionButton {
id: notifAction
required property var modelData
Layout.fillWidth: true Layout.fillWidth: true
buttonText: modelData.text buttonText: modelData.text
urgency: notificationObject.urgency urgency: notificationObject.urgency
@@ -12,4 +12,14 @@ Image {
Behavior on opacity { Behavior on opacity {
animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) 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 hintingPreference: Font.PreferNoHinting // Prevent shaky text
} }
delay: 0
visible: internalVisibleCondition visible: internalVisibleCondition
contentItem: StyledToolTipContent { contentItem: StyledToolTipContent {
@@ -162,7 +162,7 @@ Scope {
} }
IpcHandler { IpcHandler {
target: "overview" target: "search"
function toggle() { function toggle() {
GlobalStates.overviewOpen = !GlobalStates.overviewOpen; GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
@@ -185,8 +185,8 @@ Scope {
} }
GlobalShortcut { GlobalShortcut {
name: "overviewToggle" name: "searchToggle"
description: "Toggles overview on press" description: "Toggles search on press"
onPressed: { onPressed: {
GlobalStates.overviewOpen = !GlobalStates.overviewOpen; GlobalStates.overviewOpen = !GlobalStates.overviewOpen;
@@ -201,16 +201,8 @@ Scope {
} }
} }
GlobalShortcut { GlobalShortcut {
name: "overviewClose" name: "searchToggleRelease"
description: "Closes overview" description: "Toggles search on release"
onPressed: {
GlobalStates.overviewOpen = false;
}
}
GlobalShortcut {
name: "overviewToggleRelease"
description: "Toggles overview on release"
onPressed: { onPressed: {
GlobalStates.superReleaseMightTrigger = true; GlobalStates.superReleaseMightTrigger = true;
@@ -225,8 +217,8 @@ Scope {
} }
} }
GlobalShortcut { GlobalShortcut {
name: "overviewToggleReleaseInterrupt" name: "searchToggleReleaseInterrupt"
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." 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: { onPressed: {
GlobalStates.superReleaseMightTrigger = false; GlobalStates.superReleaseMightTrigger = false;
@@ -81,7 +81,7 @@ RowLayout {
} }
} }
onTextChanged: root.searchingText = text onTextChanged: LauncherSearch.query = text
onAccepted: { onAccepted: {
if (appResults.count > 0) { if (appResults.count > 0) {
@@ -14,81 +14,11 @@ import Quickshell.Io
Item { // Wrapper Item { // Wrapper
id: root id: root
readonly property string xdgConfigHome: Directories.config readonly property string xdgConfigHome: Directories.config
property string searchingText: "" 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: 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() { function focusFirstItem() {
appResults.currentIndex = 0; appResults.currentIndex = 0;
} }
@@ -103,13 +33,13 @@ Item { // Wrapper
function cancelSearch() { function cancelSearch() {
searchBar.searchInput.selectAll(); searchBar.searchInput.selectAll();
root.searchingText = ""; LauncherSearch.query = "";
searchBar.animateWidth = true; searchBar.animateWidth = true;
} }
function setSearchingText(text) { function setSearchingText(text) {
searchBar.searchInput.text = text; searchBar.searchInput.text = text;
root.searchingText = text; LauncherSearch.query = text;
} }
function containsUnsafeLink(entry) { function containsUnsafeLink(entry) {
@@ -118,34 +48,6 @@ Item { // Wrapper
return StringUtils.stringListContainsSubstring(entry.toLowerCase(), unsafeKeywords); 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 => { Keys.onPressed: event => {
// Prevent Esc and Backspace from registering // Prevent Esc and Backspace from registering
if (event.key === Qt.Key_Escape) if (event.key === Qt.Key_Escape)
@@ -285,167 +187,9 @@ Item { // Wrapper
model: ScriptModel { model: ScriptModel {
id: model id: model
objectProp: "key" objectProp: "key"
values: { values: LauncherSearch.results
// Search results are handled here onValuesChanged: {
////////////////// Skip? ////////////////// root.focusFirstItem();
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;
} }
} }
@@ -66,7 +66,7 @@ BarButton {
} }
} }
AppIcon { WAppIcon {
id: iconWidget id: iconWidget
anchors.centerIn: parent anchors.centerIn: parent
iconName: root.iconName iconName: root.iconName
@@ -5,17 +5,12 @@ import qs.modules.common
import qs.modules.common.functions import qs.modules.common.functions
import qs.modules.waffle.looks import qs.modules.waffle.looks
WButton { AcrylicButton {
id: root id: root
property var altAction: () => {} property var altAction: () => {}
property var middleClickAction: () => {} 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 Layout.fillHeight: true
topInset: 4 topInset: 4
bottomInset: 4 bottomInset: 4
@@ -23,16 +18,7 @@ WButton {
rightInset: 0 rightInset: 0
horizontalPadding: 8 horizontalPadding: 8
colBackgroundBorder: ColorUtils.transparentize(Looks.colors.bg1Border, (root.checked || root.hovered) ? Looks.backgroundTransparency : 1) colBackground: ColorUtils.transparentize(Looks.colors.bg1)
color: {
if (root.down) {
return root.colBackgroundActive
} else if ((root.hovered && !root.down) || root.checked) {
return root.colBackgroundHover
} else {
return root.colBackground
}
}
MouseArea { MouseArea {
anchors.fill: parent 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.common
import qs.modules.waffle.looks import qs.modules.waffle.looks
// TODO: Replace the icon with QMLized svg (with /usr/lib/qt6/bin/svgtoqml) for proper micro-animation
AppButton { AppButton {
id: root id: root
leftInset: Config.options.waffles.bar.leftAlignApps ? 12 : 0 leftInset: Config.options.waffles.bar.leftAlignApps ? 12 : 0
iconName: down ? "start-here-pressed" : "start-here" iconName: down ? "start-here-pressed" : "start-here"
checked: GlobalStates.searchOpen
onClicked: { onClicked: {
GlobalStates.overviewOpen = !GlobalStates.overviewOpen; // For now... GlobalStates.searchOpen = !GlobalStates.searchOpen;
} }
BarToolTip { BarToolTip {
@@ -42,7 +42,7 @@ AppButton {
} }
spacing: 6 spacing: 6
AppIcon { WAppIcon {
id: iconWidget id: iconWidget
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
iconName: root.iconName iconName: root.iconName
@@ -43,7 +43,7 @@ Button {
Layout.fillHeight: false Layout.fillHeight: false
spacing: 8 spacing: 8
AppIcon { WAppIcon {
id: appIcon id: appIcon
Layout.leftMargin: Looks.radius.large - root.padding + 2 Layout.leftMargin: Looks.radius.large - root.padding + 2
Layout.alignment: Qt.AlignVCenter 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 id: root
property bool shiny: true // Top border 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 color: Looks.colors.bg1Hover
radius: Looks.radius.medium radius: Looks.radius.medium
Behavior on color { Behavior on color {
animation: Looks.transition.color.createObject(this) animation: Looks.transition.color.createObject(this)
} }
Behavior on borderColor { Behavior on internalBorderColor {
animation: Looks.transition.color.createObject(this) animation: Looks.transition.color.createObject(this)
} }
onBorderColorChanged: { onInternalBorderColorChanged: {
borderCanvas.requestPaint(); borderCanvas.requestPaint();
} }
@@ -32,7 +33,7 @@ Rectangle {
var ctx = getContext("2d"); var ctx = getContext("2d");
ctx.clearRect(0, 0, width, height); ctx.clearRect(0, 0, width, height);
var borderColor = root.borderColor; var borderColor = root.internalBorderColor;
var r = root.radius; var r = root.radius;
var fadeLength = Math.max(1, r); var fadeLength = Math.max(1, r);
@@ -53,6 +53,8 @@ Singleton {
property color controlBgHover: '#57575B' property color controlBgHover: '#57575B'
property color controlFg: "#FFFFFF" property color controlFg: "#FFFFFF"
property color accentUnfocused: "#848484" property color accentUnfocused: "#848484"
property color link: "#235CCF"
property color inputBg: ColorUtils.transparentize(bg0, 0.4)
} }
darkColors: QtObject { darkColors: QtObject {
id: darkColors id: darkColors
@@ -70,7 +72,7 @@ Singleton {
property color bg2: '#8a8a8a' property color bg2: '#8a8a8a'
property color bg2Hover: '#b1b1b1' property color bg2Hover: '#b1b1b1'
property color bg2Active: '#919191' property color bg2Active: '#919191'
property color bg2Border: '#c4c4c4' property color bg2Border: '#bdbdbd'
property color subfg: "#CED1D7" property color subfg: "#CED1D7"
property color fg: "#FFFFFF" property color fg: "#FFFFFF"
property color fg1: "#D1D1D1" property color fg1: "#D1D1D1"
@@ -80,6 +82,8 @@ Singleton {
property color controlBgHover: "#CFCED1" property color controlBgHover: "#CFCED1"
property color controlFg: "#454545" property color controlFg: "#454545"
property color accentUnfocused: "#989898" property color accentUnfocused: "#989898"
property color link: "#A7C9FC"
property color inputBg: ColorUtils.transparentize(darkColors.bg0, 0.5)
} }
colors: QtObject { colors: QtObject {
id: colors id: colors
@@ -110,6 +114,8 @@ Singleton {
property color controlBg: root.dark ? root.darkColors.controlBg : root.lightColors.controlBg property color controlBg: root.dark ? root.darkColors.controlBg : root.lightColors.controlBg
property color controlBgHover: root.dark ? root.darkColors.controlBgHover : root.lightColors.controlBgHover property color controlBgHover: root.dark ? root.darkColors.controlBgHover : root.lightColors.controlBgHover
property color controlFg: root.dark ? root.darkColors.controlFg : root.lightColors.controlFg 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 danger: "#C42B1C"
property color dangerActive: "#B62D1F" property color dangerActive: "#B62D1F"
property color warning: "#FF9900" property color warning: "#FF9900"
@@ -118,6 +124,7 @@ Singleton {
property color accentActive: Appearance.colors.colPrimaryActive property color accentActive: Appearance.colors.colPrimaryActive
property color accentUnfocused: root.dark ? root.darkColors.accentUnfocused : root.lightColors.accentUnfocused property color accentUnfocused: root.dark ? root.darkColors.accentUnfocused : root.lightColors.accentUnfocused
property color accentFg: ColorUtils.isDark(accent) ? "#FFFFFF" : "#000000" property color accentFg: ColorUtils.isDark(accent) ? "#FFFFFF" : "#000000"
property color selection: Appearance.colors.colPrimaryContainer
} }
radius: QtObject { radius: QtObject {
@@ -2,7 +2,6 @@ import QtQuick
import org.kde.kirigami as Kirigami import org.kde.kirigami as Kirigami
import qs.services import qs.services
import qs.modules.common import qs.modules.common
import qs.modules.waffle.looks
Kirigami.Icon { Kirigami.Icon {
id: root id: root
@@ -53,10 +53,11 @@ Button {
// Hover stuff // Hover stuff
signal hoverTimedOut signal hoverTimedOut
property bool shouldShowTooltip: false property bool shouldShowTooltip: false
ToolTip.delay: 400
property Timer hoverTimer: Timer { property Timer hoverTimer: Timer {
id: hoverTimer id: hoverTimer
running: root.hovered running: root.hovered
interval: 400 interval: root.ToolTip.delay
onTriggered: { onTriggered: {
root.hoverTimedOut(); root.hoverTimedOut();
} }
@@ -7,6 +7,10 @@ import qs.services
Singleton { Singleton {
id: root id: root
function pathForName(iconName) {
return Quickshell.shellPath(`assets/icons/fluent/${iconName}.svg`);
}
function wifiIconForStrength(strength) { function wifiIconForStrength(strength) {
if (strength > 75) if (strength > 75)
return "wifi-1"; 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 { Item {
id: root id: root
required property Item contentItem property Item contentItem
property real radius: Looks.radius.large property real radius: Looks.radius.large
property alias border: borderRect property alias border: borderRect
property alias borderColor: borderRect.border.color property alias borderColor: borderRect.border.color
@@ -8,8 +8,11 @@ Text {
color: Looks.colors.fg color: Looks.colors.fg
font { font {
hintingPreference: Font.PreferFullHinting
family: Looks.font.family.ui family: Looks.font.family.ui
pixelSize: Looks.font.pixelSize.normal pixelSize: Looks.font.pixelSize.normal
weight: Looks.font.weight.regular 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 verticalPadding: 8
horizontalPadding: 10 horizontalPadding: 10
delay: 400
contentItem: WToolTipContent { contentItem: WToolTipContent {
id: tooltipContent id: tooltipContent
realContentItem: root.realContentItem realContentItem: root.realContentItem
@@ -6,6 +6,7 @@ Item {
id: root id: root
anchors.centerIn: parent anchors.centerIn: parent
required property Item realContentItem required property Item realContentItem
property alias radius: realContent.radius
property real verticalPadding: 8 property real verticalPadding: 8
property real horizontalPadding: 10 property real horizontalPadding: 10
implicitWidth: realContent.implicitWidth + 2 * 2 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 property bool collapsed: false
contentItem: ColumnLayout { contentItem: ColumnLayout {
id: contentLayout
anchors { anchors {
horizontalCenter: parent.horizontalCenter horizontalCenter: parent.horizontalCenter
top: parent.top top: parent.top
@@ -41,9 +42,24 @@ WBarAttachedPanelContent {
} }
contentItem: NotificationPaneContent { contentItem: NotificationPaneContent {
implicitWidth: calendarColumnLayout.implicitWidth 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 { Behavior on implicitHeight {
id: heightBehavior
enabled: false
Component.onCompleted: {
enableTimer.restart();
}
animation: Looks.transition.enter.createObject(this) animation: Looks.transition.enter.createObject(this)
} }
} }
@@ -51,9 +67,9 @@ WBarAttachedPanelContent {
} }
WPane { WPane {
contentItem: ColumnLayout { id: calendarPane
contentItem: WPanelPageColumn {
id: calendarColumnLayout id: calendarColumnLayout
spacing: 0
DateHeader { DateHeader {
Layout.fillWidth: true Layout.fillWidth: true
Synchronizer on collapsed { Synchronizer on collapsed {
@@ -8,18 +8,24 @@ import qs.modules.common.functions
import qs.modules.waffle.looks import qs.modules.waffle.looks
WBorderlessButton { WBorderlessButton {
id: headerButton id: root
Layout.fillWidth: false Layout.fillWidth: false
implicitWidth: 16 property real implicitSize: 16
implicitHeight: 16 implicitWidth: implicitSize
implicitHeight: implicitSize
color: "transparent" color: "transparent"
colForeground: root.hovered && !root.pressed ? Looks.colors.fg : Looks.colors.fg1
Behavior on colForeground {
animation: Looks.transition.color.createObject(this)
}
contentItem: Item { contentItem: Item {
FluentIcon { FluentIcon {
anchors.centerIn: parent anchors.centerIn: parent
implicitSize: 16 implicitSize: root.implicitSize
icon: headerButton.icon.name icon: root.icon.name
color: headerButton.hovered && !headerButton.pressed ? Looks.colors.fg : Looks.colors.fg1 color: root.colForeground
} }
} }
} }
@@ -2,18 +2,27 @@ import QtQuick
import qs import qs
import qs.services import qs.services
import qs.modules.common import qs.modules.common
import qs.modules.common.functions
import qs.modules.waffle.looks import qs.modules.waffle.looks
SmallBorderedIconButton { AcrylicButton {
id: root id: root
property bool iconVisible: true property bool iconVisible: true
property string iconName: "" property string iconName: ""
property bool iconFilled: true 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 leftPadding: 12
rightPadding: 12 rightPadding: 12
implicitWidth: focusButtonContent.implicitWidth + leftPadding + rightPadding implicitWidth: focusButtonContent.implicitWidth + leftPadding + rightPadding
implicitHeight: 24
contentItem: Row { contentItem: Row {
id: focusButtonContent id: focusButtonContent
@@ -7,11 +7,13 @@ import qs.modules.common.widgets
import qs.modules.common.functions import qs.modules.common.functions
import qs.modules.waffle.looks import qs.modules.waffle.looks
// TODO: Swipe to dismiss
MouseArea { MouseArea {
id: root id: root
required property var notificationGroup required property var notificationGroup
readonly property var notifications: notificationGroup?.notifications ?? [] readonly property var notifications: notificationGroup?.notifications ?? []
property bool expanded: false
implicitWidth: contentLayout.implicitWidth implicitWidth: contentLayout.implicitWidth
implicitHeight: contentLayout.implicitHeight implicitHeight: contentLayout.implicitHeight
@@ -34,12 +36,23 @@ MouseArea {
interactive: false interactive: false
spacing: 4 spacing: 4
model: ScriptModel { model: ScriptModel {
values: root.notifications.slice().reverse() values: root.expanded ? root.notifications.slice().reverse() : root.notifications.slice(-1)
objectProp: "notificationId"
} }
delegate: WSingleNotification { delegate: WSingleNotification {
required property int index
required property var modelData required property var modelData
width: ListView.view.width width: ListView.view.width
notification: modelData 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
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell import Quickshell
@@ -12,11 +13,18 @@ MouseArea {
id: root id: root
required property var notification 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 implicitHeight: contentItem.implicitHeight
implicitWidth: contentItem.implicitWidth implicitWidth: contentItem.implicitWidth
Behavior on implicitHeight {
animation: Looks.transition.enter.createObject(this)
}
Rectangle { Rectangle {
id: contentItem id: contentItem
anchors.fill: parent anchors.fill: parent
@@ -26,34 +34,205 @@ MouseArea {
implicitHeight: notificationContent.implicitHeight + padding * 2 implicitHeight: notificationContent.implicitHeight + padding * 2
implicitWidth: notificationContent.implicitWidth + padding * 2 implicitWidth: notificationContent.implicitWidth + padding * 2
border.width: 1 border.width: 1
border.color: Looks.applyContentTransparency(Looks.colors.ambientShadow) border.color: ColorUtils.applyAlpha(Looks.colors.ambientShadow, 0.1)
ColumnLayout { ColumnLayout {
id: notificationContent id: notificationContent
anchors.fill: parent anchors.fill: parent
anchors.margins: contentItem.padding anchors.margins: contentItem.padding
spacing: 19
RowLayout { // Header
SingleNotificationHeader {
Layout.fillWidth: true 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 Layout.fillWidth: true
WText { }
Layout.fillWidth: true
elide: Text.ElideRight // "+1 notifications" button
text: root.notification.summary GroupExpandButton {
} Layout.bottomMargin: 2
WText { }
Layout.fillWidth: true }
elide: Text.ElideRight }
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 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.bar
import qs.modules.waffle.notificationCenter import qs.modules.waffle.notificationCenter
import qs.modules.waffle.onScreenDisplay import qs.modules.waffle.onScreenDisplay
import qs.modules.waffle.startMenu
import QtQuick import QtQuick
import QtQuick.Window import QtQuick.Window
@@ -77,11 +78,13 @@ ShellRoot {
PanelLoader { identifier: "iiSidebarRight"; component: SidebarRight {} } PanelLoader { identifier: "iiSidebarRight"; component: SidebarRight {} }
PanelLoader { identifier: "iiVerticalBar"; extraCondition: Config.options.bar.vertical; component: VerticalBar {} } PanelLoader { identifier: "iiVerticalBar"; extraCondition: Config.options.bar.vertical; component: VerticalBar {} }
PanelLoader { identifier: "iiWallpaperSelector"; component: WallpaperSelector {} } PanelLoader { identifier: "iiWallpaperSelector"; component: WallpaperSelector {} }
PanelLoader { identifier: "wActionCenter"; component: WaffleActionCenter {} } PanelLoader { identifier: "wActionCenter"; component: WaffleActionCenter {} }
PanelLoader { identifier: "wBar"; component: WaffleBar {} } PanelLoader { identifier: "wBar"; component: WaffleBar {} }
PanelLoader { identifier: "wBackground"; component: WaffleBackground {} } PanelLoader { identifier: "wBackground"; component: WaffleBackground {} }
PanelLoader { identifier: "wNotificationCenter"; component: WaffleNotificationCenter {} } PanelLoader { identifier: "wNotificationCenter"; component: WaffleNotificationCenter {} }
PanelLoader { identifier: "wOnScreenDisplay"; component: WaffleOSD {} } PanelLoader { identifier: "wOnScreenDisplay"; component: WaffleOSD {} }
PanelLoader { identifier: "wStartMenu"; component: WaffleStartMenu {} }
ReloadPopup {} ReloadPopup {}
component PanelLoader: LazyLoader { component PanelLoader: LazyLoader {
@@ -94,7 +97,7 @@ ShellRoot {
property list<string> families: ["ii", "waffle"] property list<string> families: ["ii", "waffle"]
property var panelFamilies: ({ property var panelFamilies: ({
"ii": ["iiBar", "iiBackground", "iiCheatsheet", "iiDock", "iiLock", "iiMediaControls", "iiNotificationPopup", "iiOnScreenDisplay", "iiOnScreenKeyboard", "iiOverlay", "iiOverview", "iiPolkit", "iiRegionSelector", "iiScreenCorners", "iiSessionScreen", "iiSidebarLeft", "iiSidebarRight", "iiVerticalBar", "iiWallpaperSelector"], "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() { function cyclePanelFamily() {
const currentIndex = families.indexOf(Config.options.panelFamily) const currentIndex = families.indexOf(Config.options.panelFamily)
+3
View File
@@ -191,6 +191,9 @@ Tips:
- Used in Quickshell config. - Used in Quickshell config.
- `wlogout` - `wlogout`
- Used in Hyprland config. - 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 # Actual packages
@@ -1,6 +1,6 @@
pkgname=illogical-impulse-widgets pkgname=illogical-impulse-widgets
pkgver=1.0 pkgver=1.0
pkgrel=5 pkgrel=6
pkgdesc='Illogical Impulse Widget Dependencies' pkgdesc='Illogical Impulse Widget Dependencies'
arch=(any) arch=(any)
license=(None) license=(None)
@@ -14,4 +14,5 @@ depends=(
songrec songrec
translate-shell translate-shell
wlogout wlogout
libqalculate
) )
+3 -1
View File
@@ -181,6 +181,7 @@ packages = [
"hyprpicker", "hyprpicker",
"songrec", "songrec",
"translate-shell", "translate-shell",
"qalculate",
"wlogout" "wlogout"
] ]
@@ -191,4 +192,5 @@ packages = [
"plasma-systemmonitor", "plasma-systemmonitor",
"unzip" "unzip"
] ]
install_opts = ["--setopt=install_weak_deps=False"] install_opts = ["--setopt=install_weak_deps=False"]
@@ -21,6 +21,7 @@ RDEPEND="
app-misc/songrec app-misc/songrec
app-i18n/translate-shell app-i18n/translate-shell
gui-apps/wlogout gui-apps/wlogout
sci-libs/libqalculate
" "
##### CUSTOM EBUILDS ##### CUSTOM EBUILDS
# app-misc/songrec # 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). See also [caelestia-dots/shell#668](https://github.com/caelestia-dots/shell/issues/668).
### NixGL ### 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. 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 # Handling dot files
## Status ## Status
+11 -8
View File
@@ -3,23 +3,24 @@
description = "illogical-impulse"; description = "illogical-impulse";
inputs = { inputs = {
# Qt 6.10 is not yet available from released version of nixpkgs. nixpkgs.url = "nixpkgs/nixos-25.11";
#nixpkgs.url = "nixpkgs/nixos-25.05"; #nixpkgs.url = "nixpkgs/nixos-unstable";
nixpkgs.url = "nixpkgs/nixos-unstable";
home-manager = { home-manager = {
#url = "github:nix-community/home-manager/release-25.05"; url = "github:nix-community/home-manager/release-25.11";
url = "github:nix-community/home-manager/master"; #url = "github:nix-community/home-manager/master";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
nixgl.url = "github:nix-community/nixGL"; #nixgl.url = "github:nix-community/nixGL";
quickshell = { quickshell = {
url = "github:quickshell-mirror/quickshell/db1777c20b936a86528c1095cbcb1ebd92801402"; url = "github:quickshell-mirror/quickshell/db1777c20b936a86528c1095cbcb1ebd92801402";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
}; };
outputs = { nixpkgs, home-manager, nixgl, quickshell, ... }: outputs = { nixpkgs, home-manager,
#nixgl,
quickshell, ... }:
let let
home_attrs = rec { home_attrs = rec {
username = import ./username.nix; username = import ./username.nix;
@@ -36,7 +37,9 @@
homeConfigurations = { homeConfigurations = {
illogical_impulse = home-manager.lib.homeManagerConfiguration { illogical_impulse = home-manager.lib.homeManagerConfiguration {
inherit pkgs; inherit pkgs;
extraSpecialArgs = { inherit home_attrs nixgl quickshell; }; extraSpecialArgs = { inherit home_attrs
#nixgl
quickshell; };
modules = [ modules = [
./home.nix ./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; 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 = { xdg.portal = {
enable = true; enable = true;
@@ -27,7 +32,8 @@
systemd.enable = false; plugins = []; settings = {}; extraConfig = ""; systemd.enable = false; plugins = []; settings = {}; extraConfig = "";
enable = true; enable = true;
## Use NixGL ## Use NixGL
package = config.lib.nixGL.wrap pkgs.hyprland; #package = config.lib.nixGL.wrap pkgs.hyprland;
package = pkgs.hyprland;
}; };
home = { home = {
@@ -167,6 +173,7 @@
songrec #songrec songrec #songrec
translate-shell #translate-shell translate-shell #translate-shell
wlogout #wlogout wlogout #wlogout
libqalculate #libqalculate
] ]
++ [ ++ [
@@ -174,7 +181,9 @@
### illogical-impulse-quickshell-git ### illogical-impulse-quickshell-git
#(config.lib.nixGL.wrap quickshell.packages.x86_64-linux.default) #(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; }//home_attrs;
} }
+7 -3
View File
@@ -1,10 +1,14 @@
{ pkgs, quickshell, nixGLWrap, ... }: { pkgs, quickshell,
#nixGLWrap,
... }:
let 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 { in pkgs.stdenv.mkDerivation {
name = "illogical-impulse-quickshell-wrapper"; name = "illogical-impulse-quickshell-wrapper";
meta = with pkgs.lib; { 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; 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 try source $HOME/.nix-profile/etc/profile.d/hm-session-vars.sh
command -v $cmd && return command -v $cmd && return
x nix-channel --add https://nixos.org/channels/nixos-25.05 nixpkgs-home 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.05.tar.gz home-manager x nix-channel --add https://github.com/nix-community/home-manager/archive/release-25.11.tar.gz home-manager
x nix-channel --update x nix-channel --update
x env NIX_PATH="nixpkgs=$HOME/.nix-defexpr/channels/nixpkgs-home" nix-shell '<home-manager>' -A install 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 \ x home-manager switch --flake .#illogical_impulse \
--extra-experimental-features nix-command \ --extra-experimental-features nix-command \
--extra-experimental-features flakes --extra-experimental-features flakes
x sudo /nix/store/*-non-nixos-gpu/bin/non-nixos-gpu-setup
cd $REPO_ROOT cd $REPO_ROOT
x git rm -f "${SETUP_USERNAME_NIXFILE}" x git rm -f "${SETUP_USERNAME_NIXFILE}"
} }