Merge remote-tracking branch 'origin/main' into addon-i18n

This commit is contained in:
月月
2025-06-21 09:10:40 +08:00
71 changed files with 2040 additions and 671 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
--password-store=gnome-libsecret
# --ozone-platform-hint=wayland
--ozone-platform-hint=wayland
--gtk-version=4
--ignore-gpu-blocklist
--enable-features=TouchpadOverscrollHistoryNavigation
+1 -1
View File
@@ -1,4 +1,4 @@
# --ozone-platform-hint=wayland
--ozone-platform-hint=wayland
--gtk-version=4
--ignore-gpu-blocklist
--enable-features=TouchpadOverscrollHistoryNavigation
+3 -3
View File
@@ -97,10 +97,10 @@ animations {
animation = border, 1, 10, emphasizedDecel
# layers
animation = layersIn, 1, 2.7, emphasizedDecel, popin 93%
animation = layersOut, 1, 2, menu_accel, popin 94%
animation = layersOut, 1, 2.4, menu_accel, popin 94%
# fade
animation = fadeLayersIn, 1, 0.5, menu_decel
animation = fadeLayersOut, 1, 2.2, menu_accel
animation = fadeLayersOut, 1, 2.7, menu_accel
# workspaces
animation = workspaces, 1, 7, menu_decel, slide
## specialWorkspace
@@ -133,7 +133,7 @@ misc {
key_press_enables_dpms = true
animate_manual_resizes = false
animate_mouse_windowdragging = false
enable_swallow = true
enable_swallow = false
swallow_regex = (foot|kitty|allacritty|Alacritty)
new_window_takes_over_fullscreen = 2
allow_session_lock_restore = true
+4 -4
View File
@@ -51,7 +51,7 @@ bind = Ctrl+Super, R, exec, killall ags agsv1 gjs ydotool qs quickshell; qs & #
bindd = Super, V, Copy clipboard history entry, exec, qs ipc call TEST_ALIVE || pkill fuzzel || cliphist list | fuzzel --match-mode fzf --dmenu | cliphist decode | wl-copy # [hidden] Clipboard history >> clipboard (fallback)
bindd = Super, Period, Copy an emoji, exec, qs ipc call TEST_ALIVE || pkill fuzzel || ~/.config/hypr/hyprland/scripts/fuzzel-emoji.sh copy # [hidden] Emoji >> clipboard (fallback)
bindd = Super+Shift, S, Screen snip, exec, pidof slurp || hyprshot --freeze --clipboard-only --mode region --silent # Screen snip >> clipboard
bindd = Super+Shift+Alt, S, Screen snip and annotate, exec, pidof slurp || grim -g "$(slurp)" - | swappy -f - # Screen snip and annotate
bindd = Super+Shift+Alt, S, Screen snip and annotate, exec, pidof slurp || grim -g "$(slurp)" - | ksnip -e - # Screen snip and annotate
# OCR
bindd = Super+Shift, T, Character recognition,exec,grim -g "$(slurp $SLURP_ARGS)" "tmp.png" && tesseract "tmp.png" - | wl-copy && rm "tmp.png" # [hidden]
# Color picker
@@ -64,7 +64,7 @@ bindd = Super+Alt, R, Record region (no sound), exec, ~/.config/hypr/hyprland/sc
bindd = Ctrl+Alt, R, Record screen (no sound), exec, ~/.config/hypr/hyprland/scripts/record.sh --fullscreen # [hidden] Record screen (no sound)
bindd = Super+Shift+Alt, R, Record screen (with sound), exec, ~/.config/hypr/hyprland/scripts/record.sh --fullscreen-sound # Record screen (with sound)
# AI
bindd = Super+Shift+Alt, mouse:273, Generate AI summary for selected text, exec, ~/.config/ags/scripts/ai/primary-buffer-query.sh # AI summary for selected text
bindd = Super+Shift+Alt, mouse:273, Generate AI summary for selected text, exec, ~/.config/hypr/hyprland/scripts/ai/primary-buffer-query.sh # AI summary for selected text
#!
##! Window
@@ -202,13 +202,13 @@ bindl= ,XF86AudioPause, exec, playerctl play-pause # [hidden]
bind = Super, Return, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "kitty -1" "foot" "alacritty" "wezterm" "konsole" "kgx" "uxterm" "xterm" # Terminal
bind = Super, T, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "kitty -1" "foot" "alacritty" "wezterm" "konsole" "kgx" "uxterm" "xterm" # [hidden] Kitty (terminal) (alt)
bind = Ctrl+Alt, T, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "kitty -1" "foot" "alacritty" "wezterm" "konsole" "kgx" "uxterm" "xterm" # [hidden] Kitty (for Ubuntu people)
bind = Super, E, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "dolphin" "nautilus" "nemo" "thunar" # File manager
bind = Super, E, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "dolphin" "nautilus" "nemo" "thunar" "kitty -1 fish -c yazi" # File manager
bind = Super, W, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "zen-browser" "firefox" "brave" "chromium" "google-chrome-stable" "microsoft-edge-stable" "opera" # Browser
bind = Super, C, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "code" "codium" "zed" "kate" "gnome-text-editor" "emacs" "command -v nvim && kitty -1 nvim" # Code editor
bind = Super+Shift, W, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "wps" "onlyoffice-desktopeditors" # Office software
bind = Super, X, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "kate" "gnome-text-editor" "emacs" # Text editor
bind = Ctrl+Super, V, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "pavucontrol-qt" "pavucontrol" # Volume mixer
bind = Super, I, exec, XDG_CURRENT_DESKTOP=gnome ~/.config/hypr/hyprland/scripts/launch_first_available.sh "systemsettings" "gnome-control-center" "better-control" # Settings app
bind = Super, I, exec, XDG_CURRENT_DESKTOP=gnome ~/.config/hypr/hyprland/scripts/launch_first_available.sh "qs -p ~/.config/quickshell/settings.qml" "systemsettings" "gnome-control-center" "better-control" # Settings app
bind = Ctrl+Shift, Escape, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "gnome-system-monitor" "plasma-systemmonitzor --page-name Processes" "command -v btop && kitty -1 fish -c btop" # System monitor
# Cursed stuff
+2 -1
View File
@@ -22,7 +22,8 @@ windowrulev2 = center, class:^(nm-connection-editor)$
windowrulev2 = float, class:.*plasmawindowed.*
windowrulev2 = float, class:kcm_.*
windowrulev2 = float, class:.*bluedevilwizard
windowrulev2 = float, title:.*Welcome.*
windowrulev2 = float, title:.*Welcome
windowrulev2 = float, title:^(illogical-impulse Settings)$
# No appearance
# kde-material-you-colors spawns a window when changing dark/light theme. This is to make sure it doesn't interfere at all.
@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+41
View File
@@ -0,0 +1,41 @@
#!/usr/bin/env bash
# Default system prompt
SYSTEM_PROMPT="You are a helpful, quick assistant that provides brief and concise explanation \
to given content in at most 100 characters. If the given content is not in English, translate \
it to English. If the content is an English word, provide its meaning. If the content is a name, \
provide some info about it. For a math expression, provide a simplification, \
each step on a line following this style: \`2x=11 (subtract 7 from both sides)\`. \
If you do not know the answer, simply say 'No info available'. \
Only respond for the appropriate case and use as little text as possible.\
The content:"
first_loaded_model=$("$(dirname "$0")/show-loaded-ollama-models.sh" -j | jq -r '.[0].model' 2>/dev/null) || first_loaded_model=""
model=${first_loaded_model:-"llama3.2"}
# Parse command-line arguments
while [[ "$#" -gt 0 ]]; do
case $1 in
--model) model="$2"; shift ;; # Set the model from the flag
*) echo "Unknown parameter: $1"; exit 1 ;;
esac
shift
done
# Combine the system prompt with the clipboard content
content=$(wl-paste -p | tr '\n' ' ')
prompt="$SYSTEM_PROMPT $content"
# Make the API call with the specified or default model
response=$(curl http://localhost:11434/api/generate -d \
"{\"model\": \"$model\",\"prompt\": \"$prompt\",\"stream\": false}" \
| jq -r '.response')
# Check if content is a single line and no longer than 30 characters
if [[ ${#content} -le 30 && "$content" != *$'\n'* ]]; then
notify-send --app-name="Text selection query" --expire-time=10000 \
"$content" "$response"
else
notify-send --app-name="Text selection query" --expire-time=10000 \
"AI Response" "$response"
fi
@@ -0,0 +1,99 @@
#!/bin/bash
# From strikeoncmputrz/LLM_Scripts
# License: Apache-2.0, can be found in the same folder as this script
# Global Vars
ollama_url=http://localhost
port="11434"
blobs=()
model_name_paths=()
#Parse arguments
while [ "$#" -gt 0 ]; do
case $1 in
-h|--help)
echo
echo " Identifies Ollama models running on this operating system by parsing running processes."
echo
echo " Usage: $0 [options]"
echo
echo " Options:"
echo " -j, --json_output Prints result as a json object. Other output disabled. (Default: false)"
echo " -p, --port [port number] Specify Ollama Server port (Default: 11434)"
echo " -u, --ollama_url [url] Specify Ollama Server URL (Default: http://localhost)"
echo
echo " Dependencies: jq"
exit 0
;;
-j|--json_output)
json_out=1
shift 1
;;
-u|--ollama_url)
ollama_url=$2
shift 2
;;
-p|--port)
port=$2
shift 2
;;
*)
echo "Unknown option: $1"
exit 1
;;
esac
done
compare_running_models_and_modelfiles() {
json_match=()
json_output=()
local matching_models=()
OLDIFS=$IFS
for ((i=0; i<${#model_name_paths[@]}; i++)); do # Iterate over the array of modelname,blob-path
for blob in "${blobs[@]}"; do
IFS=',', read -ra fields <<< "${model_name_paths[i]}" # Split the string into parts
if [ "${fields[1]}" == "$blob" ]; then # Check if current 'field' matches a blob
matching_models+=( '{ "model": "'"${fields[0]}"'", "path": "'"${fields[1]}"'"}') # Add to list of matching models
fi
done
done
if [ -z "$json_out" ]; then
echo -e "\nModel Found: \n $(echo ${matching_models[*]} | jq '.' | sed s/[{}]//g) \n"
else
local json_match="${matching_models[*]}"
json_output=$(echo $json_match | jq -c -s .)
echo "$json_output"
fi
IFS=$OLDIFS
}
get_running_model_paths() {
blobs=$(ps aux | grep -- '--model' | grep -v grep | grep -Po '(?<=--model\s).*' | cut -d ' ' -f1)
if [ -z "$blobs" ]; then
echo -e "\n\n Warning: No running Ollama models detected!\n"
exit 0
fi
}
parse_modelfiles() {
if [ -z "$json_out" ]; then
echo -e "\nConnecting to $ollama_url:$port\n"
if [ -z "$(curl -s $ollama_url:$port)" ]; then
echo -e "Could not connect to Ollama. Check the ollama_url parameter and that the server is running\n"
exit 1
fi
curl -s "$ollama_url:$port"
fi
local models=( $(curl -s "$ollama_url:$port/api/tags" | jq -r '.models[].name') )
for model in "${models[@]}"; do
local modelfile=$(curl -s "$ollama_url:$port/api/show" -d '{ "name": "'"$model"'", "modelfile": true }' | jq -r '.modelfile')
model_name_paths+=($model,$(echo "$modelfile" | awk '/^FROM/{print $2}'))
done
}
parse_modelfiles
get_running_model_paths
compare_running_models_and_modelfiles
+29 -15
View File
@@ -1,7 +1,7 @@
import "root:/"
import "root:/modules/common"
import "root:/modules/common/widgets"
import "root:/services"
import "root:/modules/common/"
import "root:/modules/common/widgets"
import "root:/modules/common/functions/color_utils.js" as ColorUtils
import QtQuick
import QtQuick.Controls
@@ -240,25 +240,36 @@ Scope {
VerticalBarSeparator {visible: ConfigOptions?.bar.borderless}
BarGroup {
MouseArea {
id: rightCenterGroup
implicitWidth: rightCenterGroupContent.implicitWidth
implicitHeight: rightCenterGroupContent.implicitHeight
Layout.preferredWidth: barRoot.centerSideModuleWidth
Layout.fillHeight: true
ClockWidget {
showDate: (ConfigOptions.bar.verbose && barRoot.useShortenedForm < 2)
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true
onPressed: {
Hyprland.dispatch('global quickshell:sidebarRightToggle')
}
UtilButtons {
visible: (ConfigOptions.bar.verbose && barRoot.useShortenedForm === 0)
Layout.alignment: Qt.AlignVCenter
}
BarGroup {
id: rightCenterGroupContent
anchors.fill: parent
ClockWidget {
showDate: (ConfigOptions.bar.verbose && barRoot.useShortenedForm < 2)
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true
}
BatteryIndicator {
visible: (barRoot.useShortenedForm < 2 && UPower.displayDevice.isLaptopBattery)
Layout.alignment: Qt.AlignVCenter
UtilButtons {
visible: (ConfigOptions.bar.verbose && barRoot.useShortenedForm === 0)
Layout.alignment: Qt.AlignVCenter
}
BatteryIndicator {
visible: (barRoot.useShortenedForm < 2 && UPower.displayDevice.isLaptopBattery)
Layout.alignment: Qt.AlignVCenter
}
}
}
@@ -445,6 +456,7 @@ Scope {
bottom: ConfigOptions.bar.bottom ? barContent.top : undefined
}
height: Appearance.rounding.screenRounding
visible: showBarBackground
RoundCorner {
anchors.top: parent.top
@@ -452,6 +464,7 @@ Scope {
size: Appearance.rounding.screenRounding
corner: ConfigOptions.bar.bottom ? cornerEnum.bottomLeft : cornerEnum.topLeft
color: showBarBackground ? Appearance.colors.colLayer0 : "transparent"
opacity: 1.0 - Appearance.transparency
}
RoundCorner {
anchors.top: parent.top
@@ -459,6 +472,7 @@ Scope {
size: Appearance.rounding.screenRounding
corner: ConfigOptions.bar.bottom ? cornerEnum.bottomRight : cornerEnum.topRight
color: showBarBackground ? Appearance.colors.colLayer0 : "transparent"
opacity: 1.0 - Appearance.transparency
}
}
@@ -16,8 +16,8 @@ Singleton {
property string syntaxHighlightingTheme
// Extremely conservative transparency values for consistency and readability
property real transparency: ConfigOptions?.appearance.transparency ? (m3colors.darkmode ? 0.1 : 0) : 0
property real contentTransparency: ConfigOptions?.appearance.transparency ? (m3colors.darkmode ? 0.55 : 0) : 0
property real transparency: ConfigOptions?.appearance.transparency ? (m3colors.darkmode ? 0.1 : 0.07) : 0
property real contentTransparency: ConfigOptions?.appearance.transparency ? (m3colors.darkmode ? 0.55 : 0.55) : 0
m3colors: QtObject {
property bool darkmode: false
@@ -126,10 +126,11 @@ Singleton {
property color colPrimaryContainer: m3colors.m3primaryContainer
property color colPrimaryContainerHover: ColorUtils.mix(colors.colPrimaryContainer, colLayer1Hover, 0.7)
property color colPrimaryContainerActive: ColorUtils.mix(colors.colPrimaryContainer, colLayer1Active, 0.6)
property color colOnPrimaryContainer: m3colors.m3onPrimaryContainer
property color colSecondary: m3colors.m3secondary
property color colSecondaryHover: ColorUtils.mix(m3colors.m3secondary, colLayer1Hover, 0.85)
property color colSecondaryActive: ColorUtils.mix(m3colors.m3secondary, colLayer1Active, 0.4)
property color colSecondaryContainer: ColorUtils.transparentize(m3colors.m3secondaryContainer, root.contentTransparency)
property color colSecondaryContainer: m3colors.m3secondaryContainer
property color colSecondaryContainerHover: ColorUtils.mix(m3colors.m3secondaryContainer, colLayer1Hover, 0.6)
property color colSecondaryContainerActive: ColorUtils.mix(m3colors.m3secondaryContainer, colLayer1Active, 0.54)
property color colOnSecondaryContainer: m3colors.m3onSecondaryContainer
@@ -177,7 +178,7 @@ Singleton {
property int larger: 19
property int huge: 22
property int hugeass: 23
property int title: 28
property int title: huge
}
}
@@ -187,11 +188,17 @@ Singleton {
readonly property list<real> expressiveSlowSpatial: [0.39, 1.29, 0.35, 0.98, 1, 1] // Default, 650ms
readonly property list<real> expressiveEffects: [0.34, 0.80, 0.34, 1.00, 1, 1] // Default, 200ms
readonly property list<real> emphasized: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82, 0.25, 1, 1, 1]
readonly property list<real> emphasizedFirstHalf: [0.05, 0, 2 / 15, 0.06, 1 / 6, 0.4, 5 / 24, 0.82]
readonly property list<real> emphasizedLastHalf: [5 / 24, 0.82, 0.25, 1, 1, 1]
readonly property list<real> emphasizedAccel: [0.3, 0, 0.8, 0.15, 1, 1]
readonly property list<real> emphasizedDecel: [0.05, 0.7, 0.1, 1, 1, 1]
readonly property list<real> standard: [0.2, 0, 0, 1, 1, 1]
readonly property list<real> standardAccel: [0.3, 0, 1, 1, 1, 1]
readonly property list<real> standardDecel: [0, 0, 0, 1, 1, 1]
readonly property real expressiveFastSpatialDuration: 350
readonly property real expressiveDefaultSpatialDuration: 500
readonly property real expressiveSlowSpatialDuration: 650
readonly property real expressiveEffectsDuration: 200
}
animation: QtObject {
@@ -31,10 +31,8 @@ Singleton {
property QtObject apps: QtObject {
property string bluetooth: "kcmshell6 kcm_bluetooth"
property string imageViewer: "loupe"
property string network: "plasmawindowed org.kde.plasma.networkmanagement"
property string networkEthernet: "kcmshell6 kcm_networkmanagement"
property string settings: "systemsettings"
property string taskManager: "plasma-systemmonitor --page-name Processes"
property string terminal: "kitty -1" // This is only for shell actions
}
@@ -157,6 +155,7 @@ Singleton {
property QtObject windows: QtObject {
property bool showTitlebar: true // Client-side decoration for shell apps
property bool centerTitle: true
}
property QtObject hacks: QtObject {
@@ -32,12 +32,12 @@ Singleton {
property string wallpaperSwitchScriptPath: FileUtils.trimFileProtocol(`${Directories.config}/quickshell/scripts/colors/switchwall.sh`)
// Cleanup on init
Component.onCompleted: {
Hyprland.dispatch(`exec mkdir -p '${shellConfig}'`)
Hyprland.dispatch(`exec mkdir -p '${favicons}'`)
Hyprland.dispatch(`exec rm -rf '${coverArt}'; mkdir -p '${coverArt}'`)
Hyprland.dispatch(`exec rm -rf '${booruPreviews}'; mkdir -p '${booruPreviews}'`)
Hyprland.dispatch(`exec mkdir -p '${booruDownloads}' && mkdir -p '${booruDownloadsNsfw}'`)
Hyprland.dispatch(`exec rm -rf '${latexOutput}'; mkdir -p '${latexOutput}'`)
Hyprland.dispatch(`exec rm -rf '${cliphistDecode}'; mkdir -p '${cliphistDecode}'`)
Quickshell.execDetached(["bash", "-c", `mkdir -p '${shellConfig}'`])
Quickshell.execDetached(["bash", "-c", `mkdir -p '${favicons}'`])
Quickshell.execDetached(["bash", "-c", `rm -rf '${coverArt}'; mkdir -p '${coverArt}'`])
Quickshell.execDetached(["bash", "-c", `rm -rf '${booruPreviews}'; mkdir -p '${booruPreviews}'`])
Quickshell.execDetached(["bash", "-c", `mkdir -p '${booruDownloads}' && mkdir -p '${booruDownloadsNsfw}'`])
Quickshell.execDetached(["bash", "-c", `rm -rf '${latexOutput}'; mkdir -p '${latexOutput}'`])
Quickshell.execDetached(["bash", "-c", `rm -rf '${cliphistDecode}'; mkdir -p '${cliphistDecode}'`])
}
}
@@ -8,6 +8,7 @@ import Qt.labs.platform
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Widgets
import Quickshell.Hyprland
@@ -71,7 +72,7 @@ Rectangle {
}
Component.onDestruction: {
Hyprland.dispatch(`exec bash -c "[ -f '${imageDecodeFilePath}' ] && rm -f '${imageDecodeFilePath}'"`)
Quickshell.execDetached(["bash", "-c", `[ -f '${imageDecodeFilePath}' ] && rm -f '${imageDecodeFilePath}'`])
}
Image {
@@ -0,0 +1,8 @@
import QtQuick
import QtQuick.Layouts
RowLayout {
property bool uniform: false
spacing: 10
uniformCellSizes: uniform
}
@@ -0,0 +1,48 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
import "root:/services/"
import "root:/modules/common/"
import "root:/modules/common/widgets/"
import "root:/modules/common/functions/color_utils.js" as ColorUtils
import "root:/modules/common/functions/file_utils.js" as FileUtils
Flow {
id: root
Layout.fillWidth: true
spacing: 2
property list<var> options: []
property string configOptionName: ""
property var currentValue: null
signal selected(var newValue)
Repeater {
model: root.options
delegate: SelectionGroupButton {
id: paletteButton
required property var modelData
required property int index
onYChanged: {
if (index === 0) {
paletteButton.leftmost = true
} else {
var prev = root.children[index - 1]
var thisIsOnNewLine = prev && prev.y !== paletteButton.y
paletteButton.leftmost = thisIsOnNewLine
prev.rightmost = thisIsOnNewLine
}
}
leftmost: index === 0
rightmost: index === root.options.length - 1
buttonText: modelData.displayName;
toggled: root.currentValue === modelData.value
onClicked: {
root.selected(modelData.value);
}
}
}
}
@@ -0,0 +1,31 @@
import "root:/modules/common/widgets/"
import "root:/modules/common/"
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
RippleButton {
id: root
Layout.fillWidth: true
implicitHeight: contentItem.implicitHeight + 8 * 2
contentItem: RowLayout {
spacing: 10
StyledText {
id: labelWidget
Layout.fillWidth: true
text: root.text
font.pixelSize: Appearance.font.pixelSize.small
color: Appearance.colors.colOnSecondaryContainer
}
StyledSwitch {
id: switchWidget
down: root.down
scale: 0.6
Layout.fillWidth: false
checked: root.checked
onClicked: root.clicked()
}
}
}
@@ -0,0 +1,29 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "root:/modules/common/"
import "root:/modules/common/widgets/"
Flickable {
id: root
property real baseWidth: 500
property bool forceWidth: false
property real bottomContentPadding: 100
default property alias data: contentColumn.data
clip: true
contentHeight: contentColumn.implicitHeight + root.bottomContentPadding // Add some padding at the bottom
implicitWidth: contentColumn.implicitWidth
ColumnLayout {
id: contentColumn
width: root.forceWidth ? root.baseWidth : Math.max(root.baseWidth, implicitWidth)
anchors {
top: parent.top
horizontalCenter: parent.horizontalCenter
margins: 10
}
spacing: 20
}
}
@@ -0,0 +1,22 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "root:/modules/common/"
import "root:/modules/common/widgets/"
ColumnLayout {
id: root
property string title
default property alias data: sectionContent.data
Layout.fillWidth: true
spacing: 8
StyledText {
text: root.title
font.pixelSize: Appearance.font.pixelSize.larger
}
ColumnLayout {
id: sectionContent
spacing: 4
}
}
@@ -0,0 +1,60 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "root:/modules/common/"
import "root:/modules/common/widgets/"
/**
* Material 3 FAB.
*/
RippleButton {
id: root
property string iconText: "add"
property bool expanded: false
property real baseSize: 56
property real elementSpacing: 5
implicitWidth: Math.max(contentRowLayout.implicitWidth + 10 * 2, baseSize)
implicitHeight: baseSize
buttonRadius: Appearance.rounding.small
colBackground: Appearance.colors.colPrimaryContainer
colBackgroundHover: Appearance.colors.colPrimaryContainerHover
colRipple: Appearance.colors.colPrimaryContainerActive
contentItem: RowLayout {
id: contentRowLayout
property real horizontalMargins: (root.baseSize - icon.width) / 2
anchors {
verticalCenter: parent?.verticalCenter
left: parent?.left
leftMargin: contentRowLayout.horizontalMargins
}
spacing: 0
MaterialSymbol {
id: icon
Layout.fillWidth: true
horizontalAlignment: Text.AlignHCenter
iconSize: 24
color: Appearance.colors.colOnPrimaryContainer
text: root.iconText
}
Loader {
active: true
sourceComponent: Revealer {
visible: root.expanded || implicitWidth > 0
reveal: root.expanded
implicitWidth: reveal ? (buttonText.implicitWidth + root.elementSpacing + contentRowLayout.horizontalMargins) : 0
StyledText {
id: buttonText
anchors {
left: parent.left
leftMargin: root.elementSpacing
}
text: root.buttonText
color: Appearance.colors.colOnPrimaryContainer
font.pixelSize: 14
font.weight: 450
}
}
}
}
}
@@ -15,7 +15,7 @@ Rectangle {
property real extraBottomBorderWidth: 2
property color borderColor: Appearance.colors.colOnLayer0
property real borderRadius: 5
property color keyColor: Appearance.colors.colSurfaceContainerLow
property color keyColor: Appearance.m3colors.m3surfaceContainerLow
implicitWidth: keyFace.implicitWidth + borderWidth * 2
implicitHeight: keyFace.implicitHeight + borderWidth * 2 + extraBottomBorderWidth
radius: borderRadius
@@ -0,0 +1,124 @@
import "root:/modules/common"
import "root:/modules/common/widgets"
import "root:/modules/common/functions/color_utils.js" as ColorUtils
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
GroupButton {
id: lightDarkButtonRoot
required property bool dark
property color previewBg: dark ? ColorUtils.colorWithHueOf("#3f3838", Appearance.m3colors.m3primary) :
ColorUtils.colorWithHueOf("#F7F9FF", Appearance.m3colors.m3primary)
property color previewFg: dark ? Qt.lighter(previewBg, 2.2) : ColorUtils.mix(previewBg, "#292929", 0.85)
padding: 5
Layout.fillWidth: true
colBackground: Appearance.colors.colLayer2
toggled: Appearance.m3colors.darkmode === dark
onClicked: {
Quickshell.execDetached(["bash", "-c", `${Directories.wallpaperSwitchScriptPath} --mode ${dark ? "dark" : "light"} --noswitch`])
}
contentItem: Item {
anchors.centerIn: parent
implicitWidth: buttonContentLayout.implicitWidth
implicitHeight: buttonContentLayout.implicitHeight
ColumnLayout {
id: buttonContentLayout
anchors.centerIn: parent
Rectangle {
Layout.alignment: Qt.AlignHCenter
implicitWidth: 250
implicitHeight: skeletonColumnLayout.implicitHeight + 10 * 2
radius: lightDarkButtonRoot.buttonRadius - lightDarkButtonRoot.padding
color: lightDarkButtonRoot.previewBg
border {
width: 1
color: Appearance.m3colors.m3outlineVariant
}
// Some skeleton items
ColumnLayout {
id: skeletonColumnLayout
anchors.fill: parent
anchors.margins: 10
spacing: 10
RowLayout {
Rectangle {
radius: Appearance.rounding.full
color: lightDarkButtonRoot.previewFg
implicitWidth: 50
implicitHeight: 50
}
ColumnLayout {
spacing: 4
Rectangle {
radius: Appearance.rounding.unsharpenmore
color: lightDarkButtonRoot.previewFg
Layout.fillWidth: true
implicitHeight: 22
}
Rectangle {
radius: Appearance.rounding.unsharpenmore
color: lightDarkButtonRoot.previewFg
Layout.fillWidth: true
Layout.rightMargin: 45
implicitHeight: 18
}
}
}
StyledProgressBar {
Layout.topMargin: 5
Layout.bottomMargin: 5
Layout.fillWidth: true
value: 0.7
sperm: true
animateSperm: lightDarkButtonRoot.toggled
highlightColor: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3primary : lightDarkButtonRoot.previewFg
trackColor: ColorUtils.mix(lightDarkButtonRoot.previewBg, lightDarkButtonRoot.previewFg, 0.5)
}
RowLayout {
spacing: 2
Rectangle {
radius: Appearance.rounding.full
color: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3primary : lightDarkButtonRoot.previewFg
Layout.fillWidth: true
implicitHeight: 30
MaterialSymbol {
visible: lightDarkButtonRoot.toggled
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
text: "check"
iconSize: 20
color: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3onPrimary : lightDarkButtonRoot.previewBg
}
}
Rectangle {
radius: Appearance.rounding.unsharpenmore
color: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3secondaryContainer : lightDarkButtonRoot.previewFg
Layout.fillWidth: true
implicitHeight: 30
}
Rectangle {
topLeftRadius: Appearance.rounding.unsharpenmore
bottomLeftRadius: Appearance.rounding.unsharpenmore
topRightRadius: Appearance.rounding.full
bottomRightRadius: Appearance.rounding.full
color: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3secondaryContainer : lightDarkButtonRoot.previewFg
Layout.fillWidth: true
implicitHeight: 30
}
}
}
}
StyledText {
Layout.fillWidth: true
text: dark ? "Dark" : "Light"
color: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3onPrimary : Appearance.colors.colOnLayer2
horizontalAlignment: Text.AlignHCenter
}
}
}
}
@@ -12,6 +12,7 @@ Text {
hintingPreference: Font.PreferFullHinting
family: Appearance?.font.family.iconMaterial ?? "Material Symbols Rounded"
pixelSize: iconSize
weight: Font.Normal + (Font.DemiBold - Font.Normal) * fill
}
verticalAlignment: Text.AlignVCenter
color: Appearance.m3colors.m3onBackground
@@ -1,60 +0,0 @@
import "root:/modules/common"
import "root:/modules/common/widgets"
import "root:/modules/common/functions/color_utils.js" as ColorUtils
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell.Io
Button {
id: button
property bool toggled
property string buttonIcon
property string buttonText
Layout.alignment: Qt.AlignHCenter
implicitHeight: columnLayout.implicitHeight
implicitWidth: columnLayout.implicitWidth
background: null
PointingHandInteraction {}
// Real stuff
ColumnLayout {
id: columnLayout
spacing: 5
Rectangle {
width: 62
implicitHeight: navRailButtonIcon.height + 2 * 2
Layout.alignment: Qt.AlignHCenter
radius: Appearance.rounding.full
color: toggled ?
(button.down ? Appearance.colors.colSecondaryContainerActive : button.hovered ? Appearance.colors.colSecondaryContainerHover : Appearance.colors.colSecondaryContainer) :
(button.down ? Appearance.colors.colLayer1Active : button.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1))
Behavior on color {
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
}
MaterialSymbol {
id: navRailButtonIcon
anchors.centerIn: parent
iconSize: Appearance.font.pixelSize.hugeass
fill: toggled ? 1 : 0
text: buttonIcon
color: toggled ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer1
Behavior on color {
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
}
}
}
StyledText {
Layout.alignment: Qt.AlignHCenter
text: buttonText
color: Appearance.colors.colOnLayer1
}
}
}
@@ -0,0 +1,12 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "root:/modules/common/"
import "root:/modules/common/widgets/"
ColumnLayout { // Window content with navigation rail and content pane
id: root
property bool expanded: true
property int currentIndex: 0
spacing: 5
}
@@ -0,0 +1,149 @@
import "root:/modules/common"
import "root:/modules/common/widgets"
import "root:/modules/common/functions/color_utils.js" as ColorUtils
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell.Io
TabButton {
id: root
property bool toggled: TabBar.tabBar.currentIndex === TabBar.index
property string buttonIcon
property string buttonText
property bool expanded: false
property bool showToggledHighlight: true
readonly property real visualWidth: root.expanded ? root.baseSize + 20 + itemText.implicitWidth : root.baseSize
property real baseSize: 56
property real baseHighlightHeight: 32
property real highlightCollapsedTopMargin: 8
padding: 0
// The navigation items target area always spans the full width of the
// nav rail, even if the item container hugs its contents.
Layout.fillWidth: true
// implicitWidth: contentItem.implicitWidth
implicitHeight: baseSize
background: null
PointingHandInteraction {}
// Real stuff
contentItem: Item {
id: buttonContent
anchors {
top: parent.top
bottom: parent.bottom
left: parent.left
right: undefined
}
implicitWidth: root.visualWidth
implicitHeight: root.expanded ? itemIconBackground.implicitHeight : itemIconBackground.implicitHeight + itemText.implicitHeight
Rectangle {
id: itemBackground
anchors.top: itemIconBackground.top
anchors.left: itemIconBackground.left
anchors.bottom: itemIconBackground.bottom
implicitWidth: root.visualWidth
radius: Appearance.rounding.full
color: toggled ?
root.showToggledHighlight ?
(root.down ? Appearance.colors.colSecondaryContainerActive : root.hovered ? Appearance.colors.colSecondaryContainerHover : Appearance.colors.colSecondaryContainer)
: ColorUtils.transparentize(Appearance.colors.colSecondaryContainer) :
(root.down ? Appearance.colors.colLayer1Active : root.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1))
states: State {
name: "expanded"
when: root.expanded
AnchorChanges {
target: itemBackground
anchors.top: buttonContent.top
anchors.left: buttonContent.left
anchors.bottom: buttonContent.bottom
}
PropertyChanges {
target: itemBackground
implicitWidth: root.visualWidth
}
}
transitions: Transition {
AnchorAnimation {
duration: Appearance.animation.elementMoveFast.duration
easing.type: Appearance.animation.elementMoveFast.type
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
}
PropertyAnimation {
target: itemBackground
property: "implicitWidth"
duration: Appearance.animation.elementMove.duration
easing.type: Appearance.animation.elementMove.type
easing.bezierCurve: Appearance.animation.elementMove.bezierCurve
}
}
Behavior on color {
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
}
}
Item {
id: itemIconBackground
implicitWidth: root.baseSize
implicitHeight: root.baseHighlightHeight
anchors {
left: parent.left
verticalCenter: parent.verticalCenter
}
MaterialSymbol {
id: navRailButtonIcon
anchors.centerIn: parent
iconSize: 24
fill: toggled ? 1 : 0
font.weight: (toggled || root.hovered) ? Font.DemiBold : Font.Normal
text: buttonIcon
color: toggled ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer1
Behavior on color {
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
}
}
}
StyledText {
id: itemText
anchors {
top: itemIconBackground.bottom
topMargin: 2
horizontalCenter: itemIconBackground.horizontalCenter
}
states: State {
name: "expanded"
when: root.expanded
AnchorChanges {
target: itemText
anchors {
top: undefined
horizontalCenter: undefined
left: itemIconBackground.right
verticalCenter: itemIconBackground.verticalCenter
}
}
}
transitions: Transition {
AnchorAnimation {
duration: Appearance.animation.elementMoveFast.duration
easing.type: Appearance.animation.elementMoveFast.type
easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve
}
}
text: buttonText
font.pixelSize: 14
color: Appearance.colors.colOnLayer1
}
}
}
@@ -0,0 +1,25 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "root:/modules/common/"
import "root:/modules/common/widgets/"
RippleButton {
id: root
Layout.alignment: Qt.AlignLeft
implicitWidth: 40
implicitHeight: 40
Layout.leftMargin: 8
onClicked: {
parent.expanded = !parent.expanded;
}
buttonRadius: Appearance.rounding.full
contentItem: MaterialSymbol {
id: icon
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
iconSize: 24
color: Appearance.colors.colOnLayer1
text: root.parent.expanded ? "menu_open" : "menu"
}
}
@@ -0,0 +1,44 @@
import "root:/modules/common"
import "root:/modules/common/widgets"
import "root:/modules/common/functions/color_utils.js" as ColorUtils
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell.Io
Item {
id: root
property int currentIndex: 0
property bool expanded: false
default property alias data: tabBarColumn.data
implicitHeight: tabBarColumn.implicitHeight
implicitWidth: tabBarColumn.implicitWidth
Layout.topMargin: 25
Rectangle {
property real itemHeight: tabBarColumn.children[0].baseSize
property real baseHighlightHeight: tabBarColumn.children[0].baseHighlightHeight
anchors {
top: tabBarColumn.top
left: tabBarColumn.left
topMargin: itemHeight * root.currentIndex + (root.expanded ? 0 : ((itemHeight - baseHighlightHeight) / 2))
}
radius: Appearance.rounding.full
color: Appearance.colors.colSecondaryContainer
implicitHeight: root.expanded ? itemHeight : baseHighlightHeight
implicitWidth: tabBarColumn.children[root.currentIndex].visualWidth
Behavior on anchors.topMargin {
NumberAnimation {
duration: Appearance.animationCurves.expressiveFastSpatialDuration
easing.type: Appearance.animation.elementMove.type
easing.bezierCurve: Appearance.animationCurves.expressiveFastSpatial
}
}
}
ColumnLayout {
id: tabBarColumn
anchors.fill: parent
spacing: 0
}
}
@@ -94,12 +94,6 @@ Item { // Notification item area
}
}
onPressAndHold: (mouse) => {
if (mouse.button === Qt.LeftButton) {
Hyprland.dispatch(`exec wl-copy '${StringUtils.shellSingleQuoteEscape(notificationObject.body)}'`)
notificationSummaryText.text = String.format(Translation.tr("{0} (copied)"), notificationObject.summary)
}
}
onDraggingChanged: () => {
if (dragging) {
root.qmlParent.dragIndex = root.index ?? root.parent.children.indexOf(root);
@@ -226,12 +220,8 @@ Item { // Notification item area
Qt.openUrlExternally(link)
Hyprland.dispatch("global quickshell:sidebarRightClose")
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton // Only for hover
hoverEnabled: true
cursorShape: parent.hoveredLink !== "" ? Qt.PointingHandCursor : Qt.ArrowCursor
}
PointingHandLinkHover {}
}
Flickable { // Notification actions
@@ -295,7 +285,7 @@ Item { // Notification item area
(contentItem.implicitWidth + leftPadding + rightPadding)
onClicked: {
Hyprland.dispatch(`exec wl-copy '${StringUtils.shellSingleQuoteEscape(notificationObject.body)}'`)
Quickshell.clipboardText = notificationObject.body
copyIcon.text = "inventory"
copyIconTimer.restart()
}
@@ -0,0 +1,8 @@
import QtQuick
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton // Only for hover
hoverEnabled: true
cursorShape: parent.hoveredLink !== "" ? Qt.PointingHandCursor : Qt.ArrowCursor
}
@@ -13,7 +13,7 @@ Item {
implicitWidth: (reveal || vertical) ? childrenRect.width : 0
implicitHeight: (reveal || !vertical) ? childrenRect.height : 0
visible: reveal && width > 0 && height > 0
visible: reveal || (width > 0 && height > 0)
Behavior on implicitWidth {
enabled: !vertical
@@ -0,0 +1,56 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "root:/modules/common/"
import "root:/modules/common/widgets/"
RippleButton {
id: buttonWithIconRoot
property string nerdIcon
property string materialIcon
property bool materialIconFill: true
property string mainText: "Button text"
property Component mainContentComponent: Component {
StyledText {
text: buttonWithIconRoot.mainText
font.pixelSize: Appearance.font.pixelSize.small
color: Appearance.colors.colOnSecondaryContainer
}
}
implicitHeight: 35
horizontalPadding: 15
buttonRadius: Appearance.rounding.small
colBackground: Appearance.colors.colLayer2
contentItem: RowLayout {
Item {
implicitWidth: Math.max(materialIconLoader.implicitWidth, nerdIconLoader.implicitWidth)
Loader {
id: materialIconLoader
anchors.centerIn: parent
active: !nerdIcon
sourceComponent: MaterialSymbol {
text: buttonWithIconRoot.materialIcon
iconSize: Appearance.font.pixelSize.larger
color: Appearance.colors.colOnSecondaryContainer
fill: buttonWithIconRoot.materialIconFill ? 1 : 0
}
}
Loader {
id: nerdIconLoader
anchors.centerIn: parent
active: nerdIcon
sourceComponent: StyledText {
text: buttonWithIconRoot.nerdIcon
font.pixelSize: Appearance.font.pixelSize.larger
font.family: Appearance.font.family.iconNerd
color: Appearance.colors.colOnSecondaryContainer
}
}
}
Loader {
sourceComponent: buttonWithIconRoot.mainContentComponent
Layout.alignment: Qt.AlignVCenter
}
}
}
@@ -0,0 +1,24 @@
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
import "root:/services/"
import "root:/modules/common/"
import "root:/modules/common/widgets/"
GroupButton {
id: root
horizontalPadding: 12
verticalPadding: 8
bounce: false
property bool leftmost: false
property bool rightmost: false
leftRadius: (toggled || leftmost) ? (height / 2) : Appearance.rounding.unsharpenmore
rightRadius: (toggled || rightmost) ? (height / 2) : Appearance.rounding.unsharpenmore
colBackground: Appearance.colors.colSecondaryContainer
contentItem: StyledText {
color: parent.toggled ? Appearance.colors.colOnPrimary : Appearance.colors.colOnSecondaryContainer
text: root.buttonText
}
}
@@ -41,8 +41,7 @@ ProgressBar {
}
contentItem: Item {
implicitWidth: parent.width
implicitHeight: parent.height
anchors.fill: parent
Canvas {
id: wavyFill
@@ -6,7 +6,8 @@ RectangularShadow {
required property var target
anchors.fill: target
radius: target.radius
blur: 1.2 * Appearance.sizes.elevationMargin
blur: 0.9 * Appearance.sizes.elevationMargin
offset: Qt.vector2d(0.0, 1.0)
spread: 1
color: Appearance.colors.colShadow
cached: true
@@ -36,13 +36,13 @@ Switch {
// Custom thumb styling
indicator: Rectangle {
width: root.pressed ? (28 * root.scale) : root.checked ? (24 * root.scale) : (16 * root.scale)
height: root.pressed ? (28 * root.scale) : root.checked ? (24 * root.scale) : (16 * root.scale)
width: (root.pressed || root.down) ? (28 * root.scale) : root.checked ? (24 * root.scale) : (16 * root.scale)
height: (root.pressed || root.down) ? (28 * root.scale) : root.checked ? (24 * root.scale) : (16 * root.scale)
radius: Appearance.rounding.full
color: root.checked ? Appearance.m3colors.m3onPrimary : Appearance.m3colors.m3outline
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: root.checked ? (root.pressed ? (22 * root.scale) : 24 * root.scale) : (root.pressed ? (2 * root.scale) : 8 * root.scale)
anchors.leftMargin: root.checked ? ((root.pressed || root.down) ? (22 * root.scale) : 24 * root.scale) : ((root.pressed || root.down) ? (2 * root.scale) : 8 * root.scale)
Behavior on anchors.leftMargin {
animation: Appearance.animation.elementMove.numberAnimation.createObject(this)
@@ -2,6 +2,9 @@ import "root:/modules/common"
import QtQuick
import QtQuick.Controls
/**
* Does not include visual layout, but includes the easily neglected colors.
*/
TextArea {
renderType: Text.NativeRendering
selectedTextColor: Appearance.m3colors.m3onSecondaryContainer
@@ -54,7 +54,9 @@ Scope {
for (let j = i + 1; j < players.length; ++j) {
let p2 = players[j];
if (p1.trackTitle && p2.trackTitle &&
(p1.trackTitle.includes(p2.trackTitle) || p2.trackTitle.includes(p1.trackTitle))) {
(p1.trackTitle.includes(p2.trackTitle)
|| p2.trackTitle.includes(p1.trackTitle))
|| (p1.position - p2.position <= 2 && p1.length - p2.length <= 2)) {
group.push(j);
}
}
@@ -111,7 +111,6 @@ Item {
acceptedButtons: Qt.LeftButton
onClicked: {
if (root.draggingTargetWorkspace === -1) {
// Hyprland.dispatch(`exec qs ipc call overview close`)
GlobalStates.overviewOpen = false
Hyprland.dispatch(`workspace ${workspaceValue}`)
}
@@ -303,7 +303,9 @@ Item { // Wrapper
clickActionName: "",
type: `#${entry.match(/^\s*(\S+)/)?.[1] || ""}`,
execute: () => {
Hyprland.dispatch(`exec echo '${StringUtils.shellSingleQuoteEscape(entry)}' | cliphist decode | wl-copy`);
Quickshell.execDetached(
["bash", "-c", `echo '${StringUtils.shellSingleQuoteEscape(entry)}' | cliphist decode | wl-copy`]
);
}
};
}).filter(Boolean);
@@ -318,7 +320,7 @@ Item { // Wrapper
clickActionName: "",
type: "Emoji",
execute: () => {
Hyprland.dispatch(`exec wl-copy '${StringUtils.shellSingleQuoteEscape(entry.match(/^\s*(\S+)/)?.[1])}'`);
Quickshell.clipboardText = entry.match(/^\s*(\S+)/)?.[1]
}
};
}).filter(Boolean);
@@ -334,7 +336,7 @@ Item { // Wrapper
fontType: "monospace",
materialSymbol: 'calculate',
execute: () => {
Hyprland.dispatch(`exec wl-copy '${StringUtils.shellSingleQuoteEscape(root.mathResult)}'`)
Quickshell.clipboardText = root.mathResult;
}
}
const commandResultObject = {
@@ -122,7 +122,7 @@ Scope {
id: sessionTaskManager
buttonIcon: "browse_activity"
buttonText: Translation.tr("Task Manager")
onClicked: { Hyprland.dispatch(`exec ${ConfigOptions.apps.taskManager}`); sessionRoot.hide() }
onClicked: { Quickshell.execDetached(["bash", "-c", `${ConfigOptions.apps.taskManager}`]); sessionRoot.hide() }
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
KeyNavigation.left: sessionLogout
KeyNavigation.down: sessionFirmwareReboot
@@ -132,7 +132,7 @@ Scope {
id: sessionHibernate
buttonIcon: "downloading"
buttonText: Translation.tr("Hibernate")
onClicked: { Hyprland.dispatch("exec systemctl hibernate || loginctl hibernate"); sessionRoot.hide() }
onClicked: { Quickshell.execDetached(["bash", "-c", `systemctl hibernate || loginctl hibernate`]); sessionRoot.hide() }
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
KeyNavigation.up: sessionLock
KeyNavigation.right: sessionShutdown
@@ -141,7 +141,7 @@ Scope {
id: sessionShutdown
buttonIcon: "power_settings_new"
buttonText: Translation.tr("Shutdown")
onClicked: { Hyprland.dispatch("exec systemctl poweroff || loginctl poweroff"); sessionRoot.hide() }
onClicked: { Quickshell.execDetached(["bash", "-c", `systemctl poweroff || loginctl poweroff`]); sessionRoot.hide() }
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
KeyNavigation.left: sessionHibernate
KeyNavigation.right: sessionReboot
@@ -151,7 +151,7 @@ Scope {
id: sessionReboot
buttonIcon: "restart_alt"
buttonText: Translation.tr("Reboot")
onClicked: { Hyprland.dispatch("exec reboot || loginctl reboot"); sessionRoot.hide() }
onClicked: { Quickshell.execDetached(["bash", "-c", `reboot || loginctl reboot`]); sessionRoot.hide() }
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
KeyNavigation.left: sessionShutdown
KeyNavigation.right: sessionFirmwareReboot
@@ -161,7 +161,7 @@ Scope {
id: sessionFirmwareReboot
buttonIcon: "settings_applications"
buttonText: Translation.tr("Reboot to firmware settings")
onClicked: { Hyprland.dispatch("exec systemctl reboot --firmware-setup || loginctl reboot --firmware-setup"); sessionRoot.hide() }
onClicked: { Quickshell.execDetached(["bash", "-c", `systemctl reboot --firmware-setup || loginctl reboot --firmware-setup`]); sessionRoot.hide() }
onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText }
KeyNavigation.up: sessionTaskManager
KeyNavigation.left: sessionReboot
@@ -0,0 +1,151 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Widgets
import "root:/services/"
import "root:/modules/common/"
import "root:/modules/common/widgets/"
ContentPage {
forceWidth: true
ContentSection {
title: "Distro"
RowLayout {
Layout.alignment: Qt.AlignHCenter
spacing: 20
Layout.topMargin: 10
Layout.bottomMargin: 10
IconImage {
implicitSize: 100
source: Quickshell.iconPath(SystemInfo.logo)
}
ColumnLayout {
Layout.alignment: Qt.AlignVCenter
// spacing: 10
StyledText {
text: SystemInfo.distroName
font.pixelSize: Appearance.font.pixelSize.title
}
StyledText {
font.pixelSize: Appearance.font.pixelSize.normal
text: SystemInfo.homeUrl
textFormat: Text.MarkdownText
onLinkActivated: (link) => {
Qt.openUrlExternally(link)
}
PointingHandLinkHover {}
}
}
}
Flow {
Layout.fillWidth: true
spacing: 5
RippleButtonWithIcon {
materialIcon: "auto_stories"
mainText: "Documentation"
onClicked: {
Qt.openUrlExternally(SystemInfo.documentationUrl)
}
}
RippleButtonWithIcon {
materialIcon: "support"
mainText: "Help & Support"
onClicked: {
Qt.openUrlExternally(SystemInfo.supportUrl)
}
}
RippleButtonWithIcon {
materialIcon: "bug_report"
mainText: "Report a Bug"
onClicked: {
Qt.openUrlExternally(SystemInfo.bugReportUrl)
}
}
RippleButtonWithIcon {
materialIcon: "policy"
materialIconFill: false
mainText: "Privacy Policy"
onClicked: {
Qt.openUrlExternally(SystemInfo.privacyPolicyUrl)
}
}
}
}
ContentSection {
title: "Dotfiles"
RowLayout {
Layout.alignment: Qt.AlignHCenter
spacing: 20
Layout.topMargin: 10
Layout.bottomMargin: 10
MaterialSymbol {
iconSize: 70
text: "files"
color: Appearance.colors.colOnSecondaryContainer
}
ColumnLayout {
Layout.alignment: Qt.AlignVCenter
// spacing: 10
StyledText {
text: "illogical-impulse"
font.pixelSize: Appearance.font.pixelSize.title
}
StyledText {
text: "https://github.com/end-4/dots-hyprland"
font.pixelSize: Appearance.font.pixelSize.normal
textFormat: Text.MarkdownText
onLinkActivated: (link) => {
Qt.openUrlExternally(link)
}
PointingHandLinkHover {}
}
}
}
Flow {
Layout.fillWidth: true
spacing: 5
RippleButtonWithIcon {
materialIcon: "auto_stories"
mainText: "Documentation"
onClicked: {
Qt.openUrlExternally("https://end-4.github.io/dots-hyprland-wiki/en/ii-qs/02usage/")
}
}
RippleButtonWithIcon {
materialIcon: "adjust"
materialIconFill: false
mainText: "Issues"
onClicked: {
Qt.openUrlExternally("https://github.com/end-4/dots-hyprland/issues")
}
}
RippleButtonWithIcon {
materialIcon: "forum"
mainText: "Discussions"
onClicked: {
Qt.openUrlExternally("https://github.com/end-4/dots-hyprland/discussions")
}
}
RippleButtonWithIcon {
materialIcon: "favorite"
mainText: "Donate"
onClicked: {
Qt.openUrlExternally("https://github.com/sponsors/end-4")
}
}
}
}
}
@@ -0,0 +1,54 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "root:/services/"
import "root:/modules/common/"
import "root:/modules/common/widgets/"
ContentPage {
ContentSection {
title: "Policies"
ConfigRow {
ColumnLayout { // Weeb policy
StyledText {
text: "Weeb"
color: Appearance.colors.colSubtext
}
ConfigSelectionArray {
currentValue: ConfigOptions.policies.weeb
configOptionName: "policies.weeb"
onSelected: (newValue) => {
ConfigLoader.setConfigValueAndSave("policies.weeb", newValue);
}
options: [
{ displayName: "No", value: 0 },
{ displayName: "Yes", value: 1 },
{ displayName: "Closet", value: 2 }
]
}
}
ColumnLayout { // AI policy
StyledText {
text: "AI"
color: Appearance.colors.colSubtext
}
ConfigSelectionArray {
currentValue: ConfigOptions.policies.ai
configOptionName: "policies.ai"
onSelected: (newValue) => {
ConfigLoader.setConfigValueAndSave("policies.ai", newValue);
}
options: [
{ displayName: "No", value: 0 },
{ displayName: "Yes", value: 1 },
{ displayName: "Local only", value: 2 }
]
}
}
}
}
}
@@ -0,0 +1,214 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
import "root:/services/"
import "root:/modules/common/"
import "root:/modules/common/widgets/"
import "root:/modules/common/functions/color_utils.js" as ColorUtils
import "root:/modules/common/functions/file_utils.js" as FileUtils
ContentPage {
baseWidth: lightDarkButtonGroup.implicitWidth
forceWidth: true
Process {
id: konachanWallProc
property string status: ""
command: ["bash", "-c", FileUtils.trimFileProtocol(`${Directories.config}/quickshell/scripts/colors/random_konachan_wall.sh`)]
stdout: SplitParser {
onRead: data => {
console.log(`Konachan wall proc output: ${data}`);
konachanWallProc.status = data.trim();
}
}
}
ContentSection {
title: "Colors & Wallpaper"
// Light/Dark mode preference
ButtonGroup {
id: lightDarkButtonGroup
Layout.fillWidth: true
LightDarkPreferenceButton {
dark: false
}
LightDarkPreferenceButton {
dark: true
}
}
// Material palette selection
StyledText {
text: "Material palette"
color: Appearance.colors.colSubtext
}
ConfigSelectionArray {
currentValue: ConfigOptions.appearance.palette.type
configOptionName: "appearance.palette.type"
onSelected: (newValue) => {
ConfigLoader.setConfigValueAndSave("appearance.palette.type", newValue);
}
options: [
{"value": "auto", "displayName": "Auto"},
{"value": "scheme-content", "displayName": "Content"},
{"value": "scheme-expressive", "displayName": "Expressive"},
{"value": "scheme-fidelity", "displayName": "Fidelity"},
{"value": "scheme-fruit-salad", "displayName": "Fruit Salad"},
{"value": "scheme-monochrome", "displayName": "Monochrome"},
{"value": "scheme-neutral", "displayName": "Neutral"},
{"value": "scheme-rainbow", "displayName": "Rainbow"},
{"value": "scheme-tonal-spot", "displayName": "Tonal Spot"}
]
}
// Wallpaper selection
StyledText {
text: "Wallpaper"
color: Appearance.colors.colSubtext
}
RowLayout {
Layout.alignment: Qt.AlignHCenter
RippleButtonWithIcon {
id: rndWallBtn
Layout.alignment: Qt.AlignHCenter
buttonRadius: Appearance.rounding.small
materialIcon: "wallpaper"
mainText: konachanWallProc.running ? "Be patient..." : "Random: Konachan"
onClicked: {
console.log(konachanWallProc.command.join(" "))
konachanWallProc.running = true;
}
StyledToolTip {
content: "Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers"
}
}
RippleButtonWithIcon {
materialIcon: "wallpaper"
StyledToolTip {
content: "Pick wallpaper image on your system"
}
onClicked: {
Quickshell.execDetached(`${Directories.wallpaperSwitchScriptPath}`)
}
mainContentComponent: Component {
RowLayout {
spacing: 10
StyledText {
font.pixelSize: Appearance.font.pixelSize.small
text: "Choose file"
color: Appearance.colors.colOnSecondaryContainer
}
RowLayout {
spacing: 3
KeyboardKey {
key: "Ctrl"
}
KeyboardKey {
key: "󰖳"
}
StyledText {
Layout.alignment: Qt.AlignVCenter
text: "+"
}
KeyboardKey {
key: "T"
}
}
}
}
}
}
StyledText {
Layout.alignment: Qt.AlignHCenter
text: "Change any time later with /dark, /light, /img in the launcher"
font.pixelSize: Appearance.font.pixelSize.smaller
color: Appearance.colors.colSubtext
}
}
ContentSection {
title: "Shell style"
ColumnLayout { // Fake screen rounding
StyledText {
text: "Fake screen rounding"
color: Appearance.colors.colSubtext
}
ButtonGroup {
id: fakeScreenRoundingButtonGroup
property int selectedPolicy: ConfigOptions.appearance.fakeScreenRounding
spacing: 2
SelectionGroupButton {
property int value: 0
leftmost: true
buttonText: "No"
toggled: (fakeScreenRoundingButtonGroup.selectedPolicy === value)
onClicked: {
ConfigLoader.setConfigValueAndSave("appearance.fakeScreenRounding", value);
}
}
SelectionGroupButton {
property int value: 1
buttonText: "Yes"
toggled: (fakeScreenRoundingButtonGroup.selectedPolicy === value)
onClicked: {
ConfigLoader.setConfigValueAndSave("appearance.fakeScreenRounding", value);
}
}
SelectionGroupButton {
property int value: 2
rightmost: true
buttonText: "When not fullscreen"
toggled: (fakeScreenRoundingButtonGroup.selectedPolicy === value)
onClicked: {
ConfigLoader.setConfigValueAndSave("appearance.fakeScreenRounding", value);
}
}
}
}
ConfigSwitch {
text: "Transparency"
checked: ConfigOptions.appearance.transparency
onClicked: checked = !checked;
onCheckedChanged: {
ConfigLoader.setConfigValueAndSave("appearance.transparency", checked);
}
StyledToolTip {
content: "Might look ass. Unsupported."
}
}
}
ContentSection {
title: "Shell windows"
spacing: 4
ConfigRow {
uniform: true
ConfigSwitch {
text: "Title bar"
checked: ConfigOptions.windows.showTitlebar
onClicked: checked = !checked;
onCheckedChanged: {
ConfigLoader.setConfigValueAndSave("windows.showTitlebar", checked);
}
}
ConfigSwitch {
text: "Center title"
checked: ConfigOptions.windows.centerTitle
onClicked: checked = !checked;
onCheckedChanged: {
ConfigLoader.setConfigValueAndSave("windows.centerTitle", checked);
}
}
}
}
}
@@ -185,7 +185,7 @@ Rectangle {
buttonIcon: activated ? "inventory" : "content_copy"
onClicked: {
Hyprland.dispatch(`exec wl-copy '${StringUtils.shellSingleQuoteEscape(root.messageData?.content)}'`)
Quickshell.clipboardText = root.messageData?.content
copyButton.activated = true
copyIconTimer.restart()
}
@@ -73,7 +73,7 @@ ColumnLayout {
buttonIcon: activated ? "inventory" : "content_copy"
onClicked: {
Hyprland.dispatch(`exec wl-copy '${StringUtils.shellSingleQuoteEscape(segmentContent)}'`)
Quickshell.clipboardText = segmentContent
copyCodeButton.activated = true
copyIconTimer.restart()
}
@@ -96,8 +96,10 @@ ColumnLayout {
onClicked: {
const downloadPath = FileUtils.trimFileProtocol(Directories.downloads)
Hyprland.dispatch(`exec echo '${StringUtils.shellSingleQuoteEscape(segmentContent)}' > '${downloadPath}/code.${segmentLang || "txt"}'`)
Hyprland.dispatch(`exec notify-send 'Code saved to file' '${downloadPath}/code.${segmentLang || "txt"}' -a Shell`)
Quickshell.execDetached(["bash", "-c",
`echo '${StringUtils.shellSingleQuoteEscape(segmentContent)}' > '${downloadPath}/code.${segmentLang || "txt"}'`
])
Quickshell.execDetached(["bash", "-c", `notify-send 'Code saved to file' '${downloadPath}/code.${segmentLang || "txt"}' -a Shell`])
saveCodeButton.activated = true
saveIconTimer.restart()
}
@@ -180,7 +180,9 @@ Button {
buttonText: Translation.tr("Download")
onClicked: {
root.showActions = false
Hyprland.dispatch(`exec curl '${root.imageData.file_url}' -o '${root.imageData.is_nsfw ? root.nsfwPath : root.downloadPath}/${root.fileName}' && notify-send '${Translation.tr("Download complete")}' '${root.downloadPath}/${root.fileName}' -a 'Shell'`)
Quickshell.execDetached(["bash", "-c",
`curl '${root.imageData.file_url}' -o '${root.imageData.is_nsfw ? root.nsfwPath : root.downloadPath}/${root.fileName}' && notify-send '${qsTr("Download complete")}' '${root.downloadPath}/${root.fileName}' -a 'Shell'`
])
}
}
}
@@ -163,12 +163,7 @@ Rectangle {
Qt.openUrlExternally(link)
Hyprland.dispatch("global quickshell:sidebarLeftClose")
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.NoButton // Only for hover
hoverEnabled: true
cursorShape: parent.hoveredLink !== "" ? Qt.PointingHandCursor : Qt.ArrowCursor
}
PointingHandLinkHover {}
}
Repeater {
@@ -130,15 +130,17 @@ Rectangle {
Layout.topMargin: 10
width: tabBar.width
// Navigation rail buttons
ColumnLayout {
NavigationRailTabArray {
id: tabBar
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 5
id: tabBar
spacing: 15
currentIndex: root.selectedTab
expanded: false
Repeater {
model: root.tabs
NavRailButton {
NavigationRailButton {
showToggledHighlight: false
toggled: root.selectedTab == index
buttonText: modelData.name
buttonIcon: modelData.icon
@@ -3,6 +3,7 @@ import "root:/services"
import "root:/modules/common"
import "root:/modules/common/widgets"
import "root:/modules/common/functions/string_utils.js" as StringUtils
import "root:/modules/common/functions/file_utils.js" as FileUtils
import "./quickToggles/"
import "root:/services/"
import QtQuick
@@ -17,8 +18,10 @@ import Quickshell.Wayland
import Quickshell.Hyprland
Scope {
id: root
property int sidebarWidth: Appearance.sizes.sidebarWidth
property int sidebarPadding: 15
property string settingsQmlPath: FileUtils.trimFileProtocol(`${Directories.config}/quickshell/settings.qml`)
PanelWindow {
id: sidebarRoot
@@ -144,11 +147,11 @@ Scope {
toggled: false
buttonIcon: "settings"
onClicked: {
Hyprland.dispatch(`exec ${ConfigOptions.apps.settings}`)
Hyprland.dispatch(`global quickshell:sidebarRightClose`)
Hyprland.dispatch("global quickshell:sidebarRightClose")
Quickshell.execDetached(["qs", "-p", root.settingsQmlPath])
}
StyledToolTip {
content: Translation.tr("Plasma Settings")
content: Translation.tr("Settings")
}
}
QuickToggleButton {
@@ -15,8 +15,8 @@ QuickToggleButton {
toggleBluetooth.running = true
}
altAction: () => {
Hyprland.dispatch(`exec ${ConfigOptions.apps.bluetooth}`)
Hyprland.dispatch("global quickshell:sidebarRightClose")
Quickshell.execDetached(["bash", "-c", `${ConfigOptions.apps.bluetooth}`])
Hyprland.dispatch("global quickshell:sidebarRightClose")
}
Process {
id: toggleBluetooth
@@ -7,20 +7,27 @@ import Quickshell.Io
import Quickshell.Hyprland
QuickToggleButton {
property bool enabled: false
id: root
buttonIcon: "gamepad"
toggled: enabled
toggled: toggled
onClicked: {
enabled = !enabled
if (enabled) {
// gameModeOn.running = true
Hyprland.dispatch(`exec hyprctl --batch "keyword animations:enabled 0; keyword decoration:shadow:enabled 0; keyword decoration:blur:enabled 0; keyword general:gaps_in 0; keyword general:gaps_out 0; keyword general:border_size 1; keyword decoration:rounding 0; keyword general:allow_tearing 1"`)
root.toggled = !root.toggled
if (root.toggled) {
Quickshell.execDetached(["bash", "-c", `hyprctl --batch "keyword animations:enabled 0; keyword decoration:shadow:enabled 0; keyword decoration:blur:enabled 0; keyword general:gaps_in 0; keyword general:gaps_out 0; keyword general:border_size 1; keyword decoration:rounding 0; keyword general:allow_tearing 1"`])
} else {
Hyprland.dispatch("exec hyprctl reload")
Quickshell.execDetached(["hyprctl", "reload"])
}
}
Process {
id: fetchActiveState
running: true
command: ["bash", "-c", `test "$(hyprctl getoption animations:enabled -j | jq ".int")" -ne 0`]
onExited: (exitCode, exitStatus) => {
console.log("Game mode toggle exited with code:", exitCode, "and status:", exitStatus)
root.toggled = exitCode !== 0 // Inverted because enabled = nonzero exit
}
}
StyledToolTip {
content: Translation.tr("Game mode")
}
@@ -16,7 +16,7 @@ QuickToggleButton {
toggleNetwork.running = true
}
altAction: () => {
Hyprland.dispatch(`exec ${Network.ethernet ? ConfigOptions.apps.networkEthernet : ConfigOptions.apps.network}`)
Quickshell.execDetached(["bash", "-c", `${Network.ethernet ? ConfigOptions.apps.networkEthernet : ConfigOptions.apps.network}`])
Hyprland.dispatch("global quickshell:sidebarRightClose")
}
Process {
@@ -154,31 +154,18 @@ Item {
// + FAB
StyledRectangularShadow {
target: fabButton
radius: Appearance.rounding.normal
radius: fabButton.buttonRadius
blur: 0.6 * Appearance.sizes.elevationMargin
}
Button {
FloatingActionButton {
id: fabButton
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.rightMargin: root.fabMargins
anchors.bottomMargin: root.fabMargins
width: root.fabSize
height: root.fabSize
PointingHandInteraction {}
onClicked: root.showAddDialog = true
background: Rectangle {
id: fabBackground
anchors.fill: parent
radius: Appearance.rounding.normal
color: (fabButton.down) ? Appearance.colors.colPrimaryContainerActive : (fabButton.hovered ? Appearance.colors.colPrimaryContainerHover : Appearance.colors.colPrimaryContainer)
Behavior on color {
animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this)
}
}
contentItem: MaterialSymbol {
text: "add"
horizontalAlignment: Text.AlignHCenter
@@ -219,7 +219,7 @@ switch() {
# Set wallpaper with swww
swww img "$imgpath" --transition-step 100 --transition-fps 120 \
--transition-type grow --transition-angle 30 --transition-duration 1 \
--transition-pos "$cursorposx, $cursorposy_inverted"
--transition-pos "$cursorposx, $cursorposy_inverted" &
remove_restore
fi
fi
+4 -2
View File
@@ -21,10 +21,12 @@ Singleton {
property bool isCriticalAndNotCharging: isCritical && !isCharging
onIsLowAndNotChargingChanged: {
if (available && isLowAndNotCharging) Hyprland.dispatch(`exec notify-send "Low battery" "Consider plugging in your device" -u critical -a "Shell"`)
if (available && isLowAndNotCharging)
Quickshell.execDetached(["bash", "-c", `notify-send "Low battery" "Consider plugging in your device" -u critical -a "Shell"`]);
}
onIsCriticalAndNotChargingChanged: {
if (available && isCriticalAndNotCharging) Hyprland.dispatch(`exec notify-send "Critically low battery" "🙏 I beg for pleas charg\nAutomatic suspend triggers at ${ConfigOptions.battery.suspend}%" -u critical -a "Shell"`)
if (available && isCriticalAndNotCharging)
Quickshell.execDetached(["bash", "-c", `notify-send "Critically low battery" "🙏 I beg for pleas charg\nAutomatic suspend triggers at ${ConfigOptions.battery.suspend}%" -u critical -a "Shell"`]);
}
}
+5 -6
View File
@@ -44,9 +44,8 @@ Singleton {
} catch (e) {
console.error("[ConfigLoader] Error reading file:", e);
console.log("[ConfigLoader] File content was:", fileContent);
Hyprland.dispatch(`exec notify-send "${Translation.tr("Shell configuration failed to load")}" "${root.filePath}"`)
Quickshell.execDetached(["bash", "-c", `notify-send '${Translation.tr("Shell configuration failed to load")}' '${root.filePath}'`])
return;
}
}
@@ -82,7 +81,7 @@ Singleton {
function saveConfig() {
const plainConfig = ObjectUtils.toPlainObject(ConfigOptions)
Hyprland.dispatch(`exec echo '${StringUtils.shellSingleQuoteEscape(JSON.stringify(plainConfig, null, 2))}' > '${root.filePath}'`)
Quickshell.execDetached(["bash", "-c", `echo '${StringUtils.shellSingleQuoteEscape(JSON.stringify(plainConfig, null, 2))}' > '${FileUtils.trimFileProtocol(root.filePath)}'`])
}
function setConfigValueAndSave(nestedKey, value, preventNextNotification = true) {
@@ -105,7 +104,7 @@ Singleton {
} else {
root.applyConfig(configFileView.text())
if (!root.preventNextNotification) {
// Hyprland.dispatch(`exec notify-send "${Translation.tr("Shell configuration reloaded")}" "${root.filePath}"`)
// Quickshell.execDetached(["bash", "-c", `notify-send '${qsTr("Shell configuration reloaded")}' '${root.filePath}'`])
} else {
root.preventNextNotification = false;
}
@@ -129,9 +128,9 @@ Singleton {
if(error == FileViewError.FileNotFound) {
console.log("[ConfigLoader] File not found, creating new file.")
root.saveConfig()
Hyprland.dispatch(`exec notify-send "${Translation.tr("Shell configuration created")}" "${root.filePath}"`)
Quickshell.execDetached(["bash", "-c", `notify-send '${Translation.tr("Shell configuration created")}' '${root.filePath}'`])
} else {
Hyprland.dispatch(`exec notify-send "${Translation.tr("Shell configuration failed to load")}" "${root.filePath}"`)
Quickshell.execDetached(["bash", "-c", `notify-send '${Translation.tr("Shell configuration failed to load")}' '${root.filePath}'`])
}
}
}
+3 -3
View File
@@ -9,9 +9,9 @@ pragma ComponentBehavior: Bound
* A nice wrapper for date and time strings.
*/
Singleton {
property string time: Qt.formatDateTime(clock.date, ConfigOptions?.time.format ?? "hh:mm")
property string date: Qt.formatDateTime(clock.date, ConfigOptions?.time.dateFormat ?? "dddd, dd/MM")
property string collapsedCalendarFormat: Qt.formatDateTime(clock.date, "dd MMMM yyyy")
property string time: Qt.locale().toString(clock.date, ConfigOptions?.time.format ?? "hh:mm")
property string date: Qt.locale().toString(clock.date, ConfigOptions?.time.dateFormat ?? "dddd, dd/MM")
property string collapsedCalendarFormat: Qt.locale().toString(clock.date, "dd MMMM yyyy")
property string uptime: "0h, 0m"
SystemClock {
@@ -20,15 +20,15 @@ Singleton {
}
function enableNextTime() {
Hyprland.dispatch(`exec rm -f '${root.firstRunFilePath}'`)
Quickshell.execDetached(["rm", "-f", root.firstRunFilePath])
}
function disableNextTime() {
Hyprland.dispatch(`exec echo '${root.firstRunFileContent}' > '${root.firstRunFilePath}'`)
Quickshell.execDetached(["bash", "-c", `echo '${root.firstRunFileContent}' > '${root.firstRunFilePath}'`])
}
function handleFirstRun() {
Hyprland.dispatch(`exec swww query | grep 'image' || '${Directories.wallpaperSwitchScriptPath}' '${root.defaultWallpaperPath}'`)
Hyprland.dispatch(`exec qs -p '${root.welcomeQmlPath}'`)
Quickshell.execDetached(["bash", "-c", `swww query | grep 'image' || '${Directories.wallpaperSwitchScriptPath}' '${root.defaultWallpaperPath}'`])
Quickshell.execDetached(["bash", "-c", `qs -p '${root.welcomeQmlPath}'`])
}
FileView {
@@ -243,7 +243,7 @@ Singleton {
root.list = JSON.parse(fileContents).map((notif) => {
return notifComponent.createObject(root, {
"id": notif.id,
"actions": notif.actions,
"actions": [], // Notification actions are meaningless if they're not tracked by the server or the sender is dead
"appIcon": notif.appIcon,
"appName": notif.appName,
"body": notif.body,
+22 -1
View File
@@ -9,10 +9,17 @@ import Quickshell.Io
* Provides some system info: distro, username.
*/
Singleton {
id: root
property string distroName: "Unknown"
property string distroId: "unknown"
property string distroIcon: "linux-symbolic"
property string username: "user"
property string homeUrl: ""
property string documentationUrl: ""
property string supportUrl: ""
property string bugReportUrl: ""
property string privacyPolicyUrl: ""
property string logo: ""
Timer {
triggeredOnStart: true
@@ -33,6 +40,20 @@ Singleton {
const logoMatch = textOsRelease.match(/^LOGO=(.+)$/m)
distroId = logoMatch ? logoMatch[1].replace(/"/g, "") : "unknown"
// Extract additional URLs and logo
const homeUrlMatch = textOsRelease.match(/^HOME_URL="(.+?)"/m)
homeUrl = homeUrlMatch ? homeUrlMatch[1] : ""
const documentationUrlMatch = textOsRelease.match(/^DOCUMENTATION_URL="(.+?)"/m)
documentationUrl = documentationUrlMatch ? documentationUrlMatch[1] : ""
const supportUrlMatch = textOsRelease.match(/^SUPPORT_URL="(.+?)"/m)
supportUrl = supportUrlMatch ? supportUrlMatch[1] : ""
const bugReportUrlMatch = textOsRelease.match(/^BUG_REPORT_URL="(.+?)"/m)
bugReportUrl = bugReportUrlMatch ? bugReportUrlMatch[1] : ""
const privacyPolicyUrlMatch = textOsRelease.match(/^PRIVACY_POLICY_URL="(.+?)"/m)
privacyPolicyUrl = privacyPolicyUrlMatch ? privacyPolicyUrlMatch[1] : ""
const logoFieldMatch = textOsRelease.match(/^LOGO="?(.+?)"?$/m)
logo = logoFieldMatch ? logoFieldMatch[1] : ""
// Update the distroIcon property based on distroId
switch (distroId) {
case "arch": distroIcon = "arch-symbolic"; break;
@@ -57,7 +78,7 @@ Singleton {
command: ["whoami"]
stdout: SplitParser {
onRead: data => {
username = data.trim()
root.username = data.trim()
}
}
}
+20 -7
View File
@@ -20,23 +20,36 @@ Singleton {
function releaseAllKeys() {
const keycodes = Array.from(Array(249).keys());
const releaseCommand = `ydotool key --key-delay 0 ${keycodes.map(keycode => `${keycode}:0`).join(" ")}`
Hyprland.dispatch(`exec ${releaseCommand}`)
Quickshell.execDetached([
"ydotool",
"key", "--key-delay", "0",
...keycodes.map(keycode => `${keycode}:0`)
])
root.shiftMode = 0; // Reset shift mode
}
function releaseShiftKeys() {
const releaseCommand = `ydotool key --key-delay 0 ${root.shiftKeys.map(keycode => `${keycode}:0`).join(" ")}`
Hyprland.dispatch(`exec ${releaseCommand}`)
Quickshell.execDetached([
"ydotool",
"key", "--key-delay", "0",
...root.shiftKeys.map(keycode => `${keycode}:0`)
])
root.shiftMode = 0; // Reset shift mode
}
function press(keycode) {
Hyprland.dispatch(`exec ydotool key --key-delay 0 ${keycode}:1`);
Quickshell.execDetached([
"ydotool",
"key", "--key-delay", "0",
`${keycode}:1`
]);
}
function release(keycode) {
Hyprland.dispatch(`exec ydotool key --key-delay 0 ${keycode}:0`);
Quickshell.execDetached([
"ydotool",
"key", "--key-delay", "0",
`${keycode}:0`
]);
}
}
+219
View File
@@ -0,0 +1,219 @@
//@ pragma UseQApplication
//@ pragma Env QS_NO_RELOAD_POPUP=1
//@ pragma Env QT_QUICK_CONTROLS_STYLE=Basic
// Adjust this to make the app smaller or larger
//@ pragma Env QT_SCALE_FACTOR=1
import Qt5Compat.GraphicalEffects
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Window
import Quickshell
import Quickshell.Io
import Quickshell.Hyprland
import "root:/services/"
import "root:/modules/common/"
import "root:/modules/common/widgets/"
import "root:/modules/common/functions/color_utils.js" as ColorUtils
import "root:/modules/common/functions/file_utils.js" as FileUtils
import "root:/modules/common/functions/string_utils.js" as StringUtils
ApplicationWindow {
id: root
property string firstRunFilePath: FileUtils.trimFileProtocol(`${Directories.state}/user/first_run.txt`)
property string firstRunFileContent: "This file is just here to confirm you've been greeted :>"
property real contentPadding: 8
property bool showNextTime: false
property var pages: [
{
name: "Style",
icon: "palette",
component: "modules/settings/Style.qml"
},
{
name: "Behavior",
icon: "settings",
component: "modules/settings/BehaviorConfig.qml"
},
{
name: "About",
icon: "info",
component: "modules/settings/About.qml"
}
]
property int currentPage: 0
visible: true
onClosing: Qt.quit()
title: "illogical-impulse Settings"
Component.onCompleted: {
MaterialThemeLoader.reapplyTheme()
ConfigLoader.loadConfig()
}
minimumWidth: 600
minimumHeight: 400
width: 900
height: 650
color: Appearance.m3colors.m3background
ColumnLayout {
anchors {
fill: parent
margins: contentPadding
}
Item { // Titlebar
visible: ConfigOptions?.windows.showTitlebar
Layout.fillWidth: true
Layout.fillHeight: false
implicitHeight: Math.max(titleText.implicitHeight, windowControlsRow.implicitHeight)
StyledText {
id: titleText
anchors {
left: ConfigOptions.windows.centerTitle ? undefined : parent.left
horizontalCenter: ConfigOptions.windows.centerTitle ? parent.horizontalCenter : undefined
verticalCenter: parent.verticalCenter
leftMargin: 12
}
color: Appearance.colors.colOnLayer0
text: "Settings"
font.pixelSize: Appearance.font.pixelSize.title
font.family: Appearance.font.family.title
}
RowLayout { // Window controls row
id: windowControlsRow
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
RippleButton {
buttonRadius: Appearance.rounding.full
implicitWidth: 35
implicitHeight: 35
onClicked: root.close()
contentItem: MaterialSymbol {
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
text: "close"
iconSize: 20
}
}
}
}
RowLayout { // Window content with navigation rail and content pane
Layout.fillWidth: true
Layout.fillHeight: true
spacing: contentPadding
Item {
id: navRailWrapper
Layout.fillHeight: true
Layout.margins: 5
implicitWidth: navRail.expanded ? 150 : fab.baseSize
Behavior on implicitWidth {
animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this)
}
NavigationRail { // Window content with navigation rail and content pane
id: navRail
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
}
spacing: 10
expanded: root.width > 900
NavigationRailExpandButton {}
FloatingActionButton {
id: fab
iconText: "edit"
buttonText: "Edit config"
expanded: navRail.expanded
onClicked: {
Qt.openUrlExternally(`${Directories.config}/illogical-impulse/config.json`);
}
StyledToolTip {
extraVisibleCondition: !navRail.expanded
content: "Edit shell config file"
}
}
NavigationRailTabArray {
currentIndex: root.currentPage
expanded: navRail.expanded
Repeater {
model: root.pages
NavigationRailButton {
required property var index
required property var modelData
toggled: root.currentPage === index
onClicked: root.currentPage = index;
expanded: navRail.expanded
buttonIcon: modelData.icon
buttonText: modelData.name
showToggledHighlight: false
}
}
}
Item {
Layout.fillHeight: true
}
}
}
Rectangle { // Content container
Layout.fillWidth: true
Layout.fillHeight: true
color: Appearance.m3colors.m3surfaceContainerLow
radius: Appearance.rounding.windowRounding - root.contentPadding
Loader {
id: pageLoader
anchors.fill: parent
opacity: 1.0
source: root.pages[0].component
Connections {
target: root
function onCurrentPageChanged() {
if (pageLoader.sourceComponent !== root.pages[root.currentPage].component) {
switchAnim.restart();
}
}
}
SequentialAnimation {
id: switchAnim
NumberAnimation {
target: pageLoader
properties: "opacity"
from: 1
to: 0
duration: 150
easing.type: Appearance.animation.elementMoveExit.type
easing.bezierCurve: Appearance.animationCurves.emphasizedFirstHalf
}
PropertyAction {
target: pageLoader
property: "source"
value: root.pages[root.currentPage].component
}
NumberAnimation {
target: pageLoader
properties: "opacity"
from: 0
to: 1
duration: 250
easing.type: Appearance.animation.elementMoveEnter.type
easing.bezierCurve: Appearance.animationCurves.emphasizedLastHalf
}
}
}
}
}
}
}
+1 -1
View File
@@ -32,7 +32,7 @@ ShellRoot {
property bool enableBar: true
property bool enableBackgroundWidgets: true
property bool enableCheatsheet: true
property bool enableDock: true
property bool enableDock: false
property bool enableMediaControls: true
property bool enableNotificationPopup: true
property bool enableOnScreenDisplayBrightness: true
+2 -5
View File
@@ -44,7 +44,6 @@
"Disable NSFW content": "Disable NSFW content",
"Done": "Done",
"Download": "Download",
"Download complete": "Download complete",
"Edit": "Edit",
"Enter text to translate...": "Enter text to translate...",
"Finished tasks will go here": "Finished tasks will go here",
@@ -90,7 +89,6 @@
"Opens session screen on press": "Opens session screen on press",
"Output": "Output",
"Page {0}": "Page {0}",
"Plasma Settings": "Plasma Settings",
"Reboot": "Reboot",
"Reboot to firmware settings": "Reboot to firmware settings",
"Reload Hyprland & Quickshell": "Reload Hyprland & Quickshell",
@@ -110,7 +108,6 @@
"Set the current API provider": "Set the current API provider",
"Shell configuration created": "Shell configuration created",
"Shell configuration failed to load": "Shell configuration failed to load",
"Shell configuration reloaded": "Shell configuration reloaded",
"Shutdown": "Shutdown",
"Silent": "Silent",
"Sleep": "Sleep",
@@ -151,7 +148,6 @@
"Waifus only | Excellent quality, limited quantity": "Waifus only | Excellent quality, limited quantity",
"Waiting for response...": "Waiting for response...",
"Workspace": "Workspace",
"{0} (copied)": "{0} (copied)",
"{0} Safe Storage": "{0} Safe Storage",
"{0} does not require an API key": "{0} does not require an API key",
"{0} queries pending": "{0} queries pending",
@@ -172,5 +168,6 @@
"Switched to search mode. Continue with the user's request.": "Switched to search mode. Continue with the user's request.",
"Experimental | Online | Google's model\nCan do a little more but doesn't search quickly": "Experimental | Online | Google's model\nCan do a little more but doesn't search quickly",
"Message the model... \"{0}\" for commands": "Message the model... \"{0}\" for commands",
"To set an API key, pass it with the command\n\nTo view the key, pass \"get\" with the command<br/>\n\n### For {0}:\n\n**Link**: {1}\n\n{2}": "To set an API key, pass it with the command\n\nTo view the key, pass \"get\" with the command<br/>\n\n### For {0}:\n\n**Link**: {1}\n\n{2}"
"To set an API key, pass it with the command\n\nTo view the key, pass \"get\" with the command<br/>\n\n### For {0}:\n\n**Link**: {1}\n\n{2}": "To set an API key, pass it with the command\n\nTo view the key, pass \"get\" with the command<br/>\n\n### For {0}:\n\n**Link**: {1}\n\n{2}",
"Settings": "Settings"
}
+2 -5
View File
@@ -44,7 +44,6 @@
"Disable NSFW content": "禁用 NSFW 内容",
"Done": "完成",
"Download": "下载",
"Download complete": "下载完成",
"Edit": "编辑",
"Enter text to translate...": "输入要翻译的文本...",
"Finished tasks will go here": "已完成的任务将显示在这里",
@@ -90,7 +89,6 @@
"Opens session screen on press": "按下时打开会话屏幕",
"Output": "输出",
"Page {0}": "第 {0} 页",
"Plasma Settings": "Plasma 设置",
"Reboot": "重启",
"Reboot to firmware settings": "重启到固件设置",
"Reload Hyprland & Quickshell": "重新加载 Hyprland 和 Quickshell",
@@ -110,7 +108,6 @@
"Set the current API provider": "设置当前 API 提供商",
"Shell configuration created": "Shell 配置已创建",
"Shell configuration failed to load": "Shell 配置加载失败",
"Shell configuration reloaded": "Shell 配置已重新加载",
"Shutdown": "关机",
"Silent": "静音",
"Sleep": "睡眠",
@@ -151,7 +148,6 @@
"Waifus only | Excellent quality, limited quantity": "仅限角色 | 优秀质量,数量有限",
"Waiting for response...": "等待响应...",
"Workspace": "工作区",
"{0} (copied)": "{0}(已复制)",
"{0} Safe Storage": "{0} 安全存储",
"{0} does not require an API key": "{0} 不需要 API 密钥",
"{0} queries pending": "{0} 个查询等待中",
@@ -172,5 +168,6 @@
"Enter tags, or \"{0}\" for commands": "输入标签,或 \"{0}\" 查看命令",
"Online via {0} | {1}'s model": "通过 {0} 在线 | {1} 的模型",
"That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "没有找到结果。提示:\n- 检查您的标签和 NSFW 设置\n- 如果没有想到标签,请输入页码",
"Online | Google's model\nGives up-to-date information with search.": "在线 | Google 模型\n通过搜索提供最新信息。"
"Online | Google's model\nGives up-to-date information with search.": "在线 | Google 模型\n通过搜索提供最新信息。",
"Settings": "设置"
}
+212 -451
View File
@@ -24,7 +24,7 @@ ApplicationWindow {
id: root
property string firstRunFilePath: FileUtils.trimFileProtocol(`${Directories.state}/user/first_run.txt`)
property string firstRunFileContent: "This file is just here to confirm you've been greeted :>"
property real contentPadding: 5
property real contentPadding: 8
property bool showNextTime: false
visible: true
onClosing: Qt.quit()
@@ -53,220 +53,27 @@ ApplicationWindow {
}
}
component SelectionConnectedButton: GroupButton {
id: selectionConnectedButtonRoot
horizontalPadding: 12
verticalPadding: 8
bounce: false
property bool leftmost: false
property bool rightmost: false
leftRadius: (toggled || leftmost) ? (height / 2) : Appearance.rounding.unsharpenmore
rightRadius: (toggled || rightmost) ? (height / 2) : Appearance.rounding.unsharpenmore
colBackground: Appearance.colors.colSecondaryContainer
contentItem: StyledText {
color: parent.toggled ? Appearance.colors.colOnPrimary : Appearance.colors.colOnSecondaryContainer
text: selectionConnectedButtonRoot.buttonText
}
}
component Section: ColumnLayout {
id: sectionRoot
property string title
default property alias data: sectionContent.data
Layout.fillWidth: true
spacing: 8
StyledText {
text: sectionRoot.title
font.pixelSize: Appearance.font.pixelSize.larger
}
ColumnLayout {
id: sectionContent
spacing: 5
}
}
component ButtonWithIcon: RippleButton {
id: buttonWithIconRoot
property string nerdIcon
property string iconText
property string mainText: "Button text"
property Component mainContentComponent: Component {
StyledText {
text: buttonWithIconRoot.mainText
font.pixelSize: Appearance.font.pixelSize.small
color: Appearance.colors.colOnSecondaryContainer
}
}
implicitHeight: 35
horizontalPadding: 15
buttonRadius: Appearance.rounding.small
colBackground: Appearance.colors.colLayer2
contentItem: RowLayout {
Item {
implicitWidth: Math.max(materialIconLoader.implicitWidth, nerdIconLoader.implicitWidth)
Loader {
id: materialIconLoader
anchors.centerIn: parent
active: !nerdIcon
sourceComponent: MaterialSymbol {
text: buttonWithIconRoot.iconText
iconSize: Appearance.font.pixelSize.larger
color: Appearance.colors.colOnSecondaryContainer
fill: 1
}
}
Loader {
id: nerdIconLoader
anchors.centerIn: parent
active: nerdIcon
sourceComponent: StyledText {
text: buttonWithIconRoot.nerdIcon
font.pixelSize: Appearance.font.pixelSize.larger
font.family: Appearance.font.family.iconNerd
color: Appearance.colors.colOnSecondaryContainer
}
}
}
Loader {
sourceComponent: buttonWithIconRoot.mainContentComponent
Layout.alignment: Qt.AlignVCenter
}
}
}
component LightDarkPrefButton: GroupButton {
id: lightDarkButtonRoot
required property bool dark
property color previewBg: dark ? ColorUtils.colorWithHueOf("#3f3838", Appearance.m3colors.m3primary) :
ColorUtils.colorWithHueOf("#F7F9FF", Appearance.m3colors.m3primary)
property color previewFg: dark ? Qt.lighter(previewBg, 2.2) : ColorUtils.mix(previewBg, "#292929", 0.85)
padding: 5
Layout.fillWidth: true
colBackground: Appearance.colors.colLayer2
toggled: Appearance.m3colors.darkmode === dark
onClicked: {
Hyprland.dispatch(`exec ${Directories.wallpaperSwitchScriptPath} --mode ${dark ? "dark" : "light"} --noswitch`)
}
contentItem: Item {
anchors.centerIn: parent
implicitWidth: buttonContentLayout.implicitWidth
implicitHeight: buttonContentLayout.implicitHeight
ColumnLayout {
id: buttonContentLayout
anchors.centerIn: parent
Rectangle {
Layout.alignment: Qt.AlignHCenter
implicitWidth: 250
implicitHeight: skeletonColumnLayout.implicitHeight + 10 * 2
radius: lightDarkButtonRoot.buttonRadius - lightDarkButtonRoot.padding
color: lightDarkButtonRoot.previewBg
border {
width: 1
color: Appearance.m3colors.m3outlineVariant
}
// Some skeleton items
ColumnLayout {
id: skeletonColumnLayout
anchors.fill: parent
anchors.margins: 10
spacing: 10
RowLayout {
Rectangle {
radius: Appearance.rounding.full
color: lightDarkButtonRoot.previewFg
implicitWidth: 50
implicitHeight: 50
}
ColumnLayout {
spacing: 4
Rectangle {
radius: Appearance.rounding.unsharpenmore
color: lightDarkButtonRoot.previewFg
Layout.fillWidth: true
implicitHeight: 22
}
Rectangle {
radius: Appearance.rounding.unsharpenmore
color: lightDarkButtonRoot.previewFg
Layout.fillWidth: true
Layout.rightMargin: 45
implicitHeight: 18
}
}
}
StyledProgressBar {
Layout.topMargin: 5
Layout.bottomMargin: 5
Layout.fillWidth: true
value: 0.7
sperm: true
animateSperm: lightDarkButtonRoot.toggled
highlightColor: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3primary : lightDarkButtonRoot.previewFg
trackColor: ColorUtils.mix(lightDarkButtonRoot.previewBg, lightDarkButtonRoot.previewFg, 0.5)
}
RowLayout {
spacing: 2
Rectangle {
radius: Appearance.rounding.full
color: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3primary : lightDarkButtonRoot.previewFg
Layout.fillWidth: true
implicitHeight: 30
MaterialSymbol {
visible: lightDarkButtonRoot.toggled
anchors.centerIn: parent
horizontalAlignment: Text.AlignHCenter
text: "check"
iconSize: 20
color: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3onPrimary : lightDarkButtonRoot.previewBg
}
}
Rectangle {
radius: Appearance.rounding.unsharpenmore
color: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3secondaryContainer : lightDarkButtonRoot.previewFg
Layout.fillWidth: true
implicitHeight: 30
}
Rectangle {
topLeftRadius: Appearance.rounding.unsharpenmore
bottomLeftRadius: Appearance.rounding.unsharpenmore
topRightRadius: Appearance.rounding.full
bottomRightRadius: Appearance.rounding.full
color: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3secondaryContainer : lightDarkButtonRoot.previewFg
Layout.fillWidth: true
implicitHeight: 30
}
}
}
}
StyledText {
Layout.fillWidth: true
text: dark ? "Dark" : "Light"
color: lightDarkButtonRoot.toggled ? Appearance.m3colors.m3onPrimary : Appearance.colors.colOnLayer2
horizontalAlignment: Text.AlignHCenter
}
}
}
}
ColumnLayout {
anchors {
fill: parent
margins: contentPadding
}
Item {
Item { // Titlebar
visible: ConfigOptions?.windows.showTitlebar
Layout.fillWidth: true
implicitHeight: Math.max(welcomeText.implicitHeight, windowControlsRow.implicitHeight)
StyledText {
id: welcomeText
anchors.centerIn: parent
anchors {
left: ConfigOptions.windows.centerTitle ? undefined : parent.left
horizontalCenter: ConfigOptions.windows.centerTitle ? parent.horizontalCenter : undefined
verticalCenter: parent.verticalCenter
leftMargin: 12
}
color: Appearance.colors.colOnLayer0
text: "Yooooo hi there"
font.pixelSize: Appearance.font.pixelSize.hugeass
font.pixelSize: Appearance.font.pixelSize.title
font.family: Appearance.font.family.title
}
RowLayout { // Window controls row
@@ -284,10 +91,9 @@ ApplicationWindow {
Layout.alignment: Qt.AlignVCenter
onCheckedChanged: {
if (checked) {
Hyprland.dispatch(`exec rm '${StringUtils.shellSingleQuoteEscape(root.firstRunFilePath)}'`)
Quickshell.execDetached(["rm", root.firstRunFilePath])
} else {
console.log(`exec echo '${StringUtils.shellSingleQuoteEscape(root.firstRunFileContent)}' > '${StringUtils.shellSingleQuoteEscape(root.firstRunFilePath)}'`)
Hyprland.dispatch(`exec echo '${StringUtils.shellSingleQuoteEscape(root.firstRunFileContent)}' > '${StringUtils.shellSingleQuoteEscape(root.firstRunFilePath)}'`)
Quickshell.execDetached(["bash", "-c", `echo '${StringUtils.shellSingleQuoteEscape(root.firstRunFileContent)}' > '${StringUtils.shellSingleQuoteEscape(root.firstRunFilePath)}'`])
}
}
}
@@ -305,262 +111,217 @@ ApplicationWindow {
}
}
}
Rectangle {
Rectangle { // Content container
color: Appearance.m3colors.m3surfaceContainerLow
radius: Appearance.rounding.windowRounding - root.contentPadding
implicitHeight: contentColumn.implicitHeight
implicitWidth: contentColumn.implicitWidth
Layout.fillWidth: true
Layout.fillHeight: true
radius: Appearance.rounding.windowRounding - root.contentPadding
Flickable {
clip: true
ContentPage {
id: contentColumn
anchors.fill: parent
contentHeight: contentColumn.implicitHeight
implicitWidth: contentColumn.implicitWidth
ColumnLayout {
id: contentColumn
anchors {
top: parent.top
bottom: parent.bottom
horizontalCenter: parent.horizontalCenter
margins: 10
}
spacing: 20
ContentSection {
title: "Style & wallpaper"
Section {
title: "Style & wallpaper"
ButtonGroup {
Layout.fillWidth: true
LightDarkPrefButton {
dark: false
}
LightDarkPrefButton {
dark: true
}
}
RowLayout {
Layout.alignment: Qt.AlignHCenter
ButtonWithIcon {
id: rndWallBtn
Layout.alignment: Qt.AlignHCenter
buttonRadius: Appearance.rounding.small
iconText: "wallpaper"
mainText: konachanWallProc.running ? "Be patient..." : "Random: Konachan"
onClicked: {
console.log(konachanWallProc.command.join(" "))
konachanWallProc.running = true;
}
}
ButtonWithIcon {
iconText: "wallpaper"
onClicked: {
Hyprland.dispatch(`exec ${Directories.wallpaperSwitchScriptPath}`)
}
mainContentComponent: Component {
RowLayout {
spacing: 10
StyledText {
font.pixelSize: Appearance.font.pixelSize.small
text: "Choose file"
color: Appearance.colors.colOnSecondaryContainer
}
RowLayout {
spacing: 3
KeyboardKey {
key: "Ctrl"
}
KeyboardKey {
key: "󰖳"
}
StyledText {
Layout.alignment: Qt.AlignVCenter
text: "+"
}
KeyboardKey {
key: "T"
}
}
}
}
}
}
StyledText {
Layout.alignment: Qt.AlignHCenter
text: "Change any time later with /dark, /light, /img in the launcher"
font.pixelSize: Appearance.font.pixelSize.smaller
color: Appearance.colors.colSubtext
}
}
Section {
title: "Policies"
RowLayout {
Layout.alignment: Qt.AlignHCenter
spacing: 15
ColumnLayout { // Weeb policy
StyledText {
text: "Weeb"
color: Appearance.colors.colSubtext
}
ButtonGroup {
id: weebPolicyBtnGroup
property int selectedPolicy: ConfigOptions.policies.weeb
spacing: 2
SelectionConnectedButton {
property int value: 0
leftmost: true
buttonText: "No"
toggled: (weebPolicyBtnGroup.selectedPolicy === value)
onClicked: {
ConfigLoader.setConfigValueAndSave("policies.weeb", value);
}
}
SelectionConnectedButton {
property int value: 1
buttonText: "Yes"
toggled: (weebPolicyBtnGroup.selectedPolicy === value)
onClicked: {
ConfigLoader.setConfigValueAndSave("policies.weeb", value);
}
}
SelectionConnectedButton {
property int value: 2
rightmost: true
buttonText: "Closet"
toggled: (weebPolicyBtnGroup.selectedPolicy === value)
onClicked: {
ConfigLoader.setConfigValueAndSave("policies.weeb", value);
}
}
}
}
ColumnLayout { // AI policy
StyledText {
text: "AI"
color: Appearance.colors.colSubtext
}
ButtonGroup {
id: aiPolicyBtnGroup
property int selectedPolicy: ConfigOptions.policies.ai
spacing: 2
SelectionConnectedButton {
property int value: 0
leftmost: true
buttonText: "No"
toggled: (aiPolicyBtnGroup.selectedPolicy === value)
onClicked: {
ConfigLoader.setConfigValueAndSave("policies.ai", value);
}
}
SelectionConnectedButton {
property int value: 1
buttonText: "Yes"
toggled: (aiPolicyBtnGroup.selectedPolicy === value)
onClicked: {
ConfigLoader.setConfigValueAndSave("policies.ai", value);
}
}
SelectionConnectedButton {
property int value: 2
rightmost: true
buttonText: "Local only"
toggled: (aiPolicyBtnGroup.selectedPolicy === value)
onClicked: {
ConfigLoader.setConfigValueAndSave("policies.ai", value);
}
}
}
}
}
}
Section {
title: "Info"
Flow {
Layout.fillWidth: true
spacing: 10
ButtonWithIcon {
iconText: "keyboard_alt"
onClicked: {
Hyprland.dispatch("global quickshell:cheatsheetOpen")
}
mainContentComponent: Component {
RowLayout {
spacing: 10
StyledText {
font.pixelSize: Appearance.font.pixelSize.small
text: "Keybinds"
color: Appearance.colors.colOnSecondaryContainer
}
RowLayout {
spacing: 3
KeyboardKey {
key: "󰖳"
}
StyledText {
Layout.alignment: Qt.AlignVCenter
text: "+"
}
KeyboardKey {
key: "/"
}
}
}
}
}
ButtonWithIcon {
iconText: "help"
mainText: "Usage"
onClicked: {
Qt.openUrlExternally("https://end-4.github.io/dots-hyprland-wiki/en/ii-qs/02usage/")
}
}
ButtonWithIcon {
iconText: "construction"
mainText: "Configuration"
onClicked: {
Qt.openUrlExternally("https://end-4.github.io/dots-hyprland-wiki/en/ii-qs/03config/")
}
}
}
}
Section {
title: "Useless buttons"
Flow {
Layout.fillWidth: true
spacing: 10
ButtonWithIcon {
nerdIcon: "󰊤"
mainText: "GitHub"
onClicked: {
Qt.openUrlExternally("https://github.com/end-4/dots-hyprland")
}
}
ButtonWithIcon {
iconText: "favorite"
mainText: "Funny number"
onClicked: {
Qt.openUrlExternally("https://github.com/sponsors/end-4")
}
}
}
}
Item {
ButtonGroup {
Layout.fillWidth: true
Layout.fillHeight: true
LightDarkPreferenceButton {
dark: false
}
LightDarkPreferenceButton {
dark: true
}
}
RowLayout {
Layout.alignment: Qt.AlignHCenter
RippleButtonWithIcon {
id: rndWallBtn
Layout.alignment: Qt.AlignHCenter
buttonRadius: Appearance.rounding.small
materialIcon: "wallpaper"
mainText: konachanWallProc.running ? "Be patient..." : "Random: Konachan"
onClicked: {
console.log(konachanWallProc.command.join(" "))
konachanWallProc.running = true;
}
StyledToolTip {
content: "Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers"
}
}
RippleButtonWithIcon {
materialIcon: "wallpaper"
StyledToolTip {
content: "Pick wallpaper image on your system"
}
onClicked: {
Quickshell.execDetached([`${Directories.wallpaperSwitchScriptPath}`])
}
mainContentComponent: Component {
RowLayout {
spacing: 10
StyledText {
font.pixelSize: Appearance.font.pixelSize.small
text: "Choose file"
color: Appearance.colors.colOnSecondaryContainer
}
RowLayout {
spacing: 3
KeyboardKey {
key: "Ctrl"
}
KeyboardKey {
key: "󰖳"
}
StyledText {
Layout.alignment: Qt.AlignVCenter
text: "+"
}
KeyboardKey {
key: "T"
}
}
}
}
}
}
StyledText {
Layout.alignment: Qt.AlignHCenter
text: "Change any time later with /dark, /light, /img in the launcher"
font.pixelSize: Appearance.font.pixelSize.smaller
color: Appearance.colors.colSubtext
}
}
ContentSection {
title: "Policies"
ConfigRow {
ColumnLayout { // Weeb policy
StyledText {
text: "Weeb"
color: Appearance.colors.colSubtext
}
ConfigSelectionArray {
currentValue: ConfigOptions.policies.weeb
configOptionName: "policies.weeb"
onSelected: (newValue) => {
ConfigLoader.setConfigValueAndSave("policies.weeb", newValue);
}
options: [
{ displayName: "No", value: 0 },
{ displayName: "Yes", value: 1 },
{ displayName: "Closet", value: 2 }
]
}
}
ColumnLayout { // AI policy
StyledText {
text: "AI"
color: Appearance.colors.colSubtext
}
ConfigSelectionArray {
currentValue: ConfigOptions.policies.ai
configOptionName: "policies.ai"
onSelected: (newValue) => {
ConfigLoader.setConfigValueAndSave("policies.ai", newValue);
}
options: [
{ displayName: "No", value: 0 },
{ displayName: "Yes", value: 1 },
{ displayName: "Local only", value: 2 }
]
}
}
}
}
ContentSection {
title: "Info"
Flow {
Layout.fillWidth: true
spacing: 5
RippleButtonWithIcon {
materialIcon: "keyboard_alt"
onClicked: {
Hyprland.dispatch("global quickshell:cheatsheetOpen")
}
mainContentComponent: Component {
RowLayout {
spacing: 10
StyledText {
font.pixelSize: Appearance.font.pixelSize.small
text: "Keybinds"
color: Appearance.colors.colOnSecondaryContainer
}
RowLayout {
spacing: 3
KeyboardKey {
key: "󰖳"
}
StyledText {
Layout.alignment: Qt.AlignVCenter
text: "+"
}
KeyboardKey {
key: "/"
}
}
}
}
}
RippleButtonWithIcon {
materialIcon: "help"
mainText: "Usage"
onClicked: {
Qt.openUrlExternally("https://end-4.github.io/dots-hyprland-wiki/en/ii-qs/02usage/")
}
}
RippleButtonWithIcon {
materialIcon: "construction"
mainText: "Configuration"
onClicked: {
Qt.openUrlExternally("https://end-4.github.io/dots-hyprland-wiki/en/ii-qs/03config/")
}
}
}
}
ContentSection {
title: "Useless buttons"
Flow {
Layout.fillWidth: true
spacing: 5
RippleButtonWithIcon {
nerdIcon: "󰊤"
mainText: "GitHub"
onClicked: {
Qt.openUrlExternally("https://github.com/end-4/dots-hyprland")
}
}
RippleButtonWithIcon {
materialIcon: "favorite"
mainText: "Funny number"
onClicked: {
Qt.openUrlExternally("https://github.com/sponsors/end-4")
}
}
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
}
}
+4 -3
View File
@@ -4,6 +4,7 @@ add_newline = false
# Powerline symbols                                   
# Wedges 🭧🭒 🭣🭧🭓
# Random noise 🬖🬥🬔🬗
# Cool stuff 󰜥   
# format = """
# $cmd_duration$username$hostname $directory $git_branch
@@ -16,8 +17,8 @@ $character
# Replace the "" symbol in the prompt with "➜"
[character] # The name of the module we are configuring is "character"
success_symbol = "[ 󰜥 ](bold fg:blue)"
error_symbol = "[ 󰜥 ](bold fg:red)"
success_symbol = "[ ](bold fg:blue)"
error_symbol = "[ ](bold fg:red)"
# Disable the package module, hiding it from the prompt completely
[package]
@@ -83,7 +84,7 @@ read_only = "  "
style = "bg:green fg:black"
truncation_length = 6
truncation_symbol = " ••/"
format = '[](bold fg:green)[󰉋 $path]($style)[](bold fg:green)'
format = '[](bold fg:green)[󰉋 $path]($style)[](bold fg:green)'
[directory.substitutions]
@@ -0,0 +1,3 @@
[preferred]
default = hyprland;gtk
org.freedesktop.impl.portal.FileChooser = kde
@@ -6,7 +6,7 @@ arch=(any)
license=(None)
depends=(
xdg-desktop-portal
xdg-desktop-portal-gtk
xdg-desktop-portal-kde
xdg-desktop-portal-hyprland
)
@@ -5,11 +5,10 @@ pkgdesc='Illogical Impulse Screenshot and Recording Dependencies'
arch=(any)
license=(None)
depends=(
swappy
wf-recorder
hyprshot
ksnip
slurp
tesseract
tesseract-data-eng
slurp
wf-recorder
)
@@ -12,7 +12,7 @@ depends=(
hyprlock
hyprpicker
nm-connection-editor
quickshell
quickshell-git
swww
translate-shell
wlogout