diff --git a/Assets/ColorScheme/Rosepine.json b/Assets/ColorScheme/Rosepine.json index 37e9b3a..547bb73 100644 --- a/Assets/ColorScheme/Rosepine.json +++ b/Assets/ColorScheme/Rosepine.json @@ -1,19 +1,19 @@ { "dark": { "mPrimary": "#ebbcba", - "mOnPrimary": "#1f1d2e", + "mOnPrimary": "#191724", "mSecondary": "#9ccfd8", - "mOnSecondary": "#1f1d2e", + "mOnSecondary": "#191724", "mTertiary": "#f6c177", - "mOnTertiary": "#1f1d2e", + "mOnTertiary": "#191724", "mError": "#eb6f92", - "mOnError": "#1f1d2e", - "mSurface": "#1f1d2e", + "mOnError": "#191724", + "mSurface": "#191724", "mOnSurface": "#e0def4", "mSurfaceVariant": "#26233a", "mOnSurfaceVariant": "#908caa", "mOutline": "#403d52", - "mShadow": "#1f1d2e" + "mShadow": "#191724" }, "light": { "mPrimary": "#d46e6b", diff --git a/Bin/system-stats.sh b/Bin/system-stats.sh deleted file mode 100755 index 8cf7133..0000000 --- a/Bin/system-stats.sh +++ /dev/null @@ -1,270 +0,0 @@ -#!/usr/bin/env -S bash - -# A Bash script to monitor system stats and output them in JSON format. - -# --- Configuration --- -# Default sleep duration in seconds. Can be overridden by the first argument. -SLEEP_DURATION=3 - -# --- Argument Parsing --- -# Check if a command-line argument is provided for the sleep duration. -if [[ -n "$1" ]]; then - # Basic validation to ensure the argument is a number (integer or float). - if [[ "$1" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then - SLEEP_DURATION=$1 - else - # Output to stderr if the format is invalid. - echo "Warning: Invalid duration format '$1'. Using default of ${SLEEP_DURATION}s." >&2 - fi -fi - -# --- Global Cache Variables --- -# These variables will store the discovered CPU temperature sensor path and type -# to avoid searching for it on every loop iteration. -TEMP_SENSOR_PATH="" -TEMP_SENSOR_TYPE="" - -# Network speed monitoring variables -PREV_RX_BYTES=0 -PREV_TX_BYTES=0 -PREV_TIME=0 - -# --- Data Collection Functions --- - -# -# Gets memory usage in GB, MB, and as a percentage. -# -get_memory_info() { - awk ' - /MemTotal/ {total=$2} - /MemAvailable/ {available=$2} - END { - if (total > 0) { - usage_kb = total - available - usage_gb = usage_kb / 1000000 - usage_percent = (usage_kb / total) * 100 - printf "%.1f %.0f\n", usage_gb, usage_percent - } else { - # Fallback if /proc/meminfo is unreadable or empty. - print "0.0 0 0" - } - } - ' /proc/meminfo -} - -# -# Gets the usage percentage of the root filesystem ("/"). -# -get_disk_usage() { - # df gets disk usage. --output=pcent shows only the percentage for the root path. - # tail -1 gets the data line, and tr removes the '%' sign and whitespace. - df --output=pcent / | tail -1 | tr -d ' %' -} - -# -# Calculates current CPU usage over a short interval. -# -get_cpu_usage() { - # Read all 10 CPU time fields to prevent errors on newer kernels. - read -r cpu prev_user prev_nice prev_system prev_idle prev_iowait prev_irq prev_softirq prev_steal prev_guest prev_guest_nice < /proc/stat - - # Calculate previous total and idle times. - local prev_total_idle=$((prev_idle + prev_iowait)) - local prev_total=$((prev_user + prev_nice + prev_system + prev_idle + prev_iowait + prev_irq + prev_softirq + prev_steal + prev_guest + prev_guest_nice)) - - # Wait for a short period. - sleep 0.05 - - # Read all 10 CPU time fields again for the second measurement. - read -r cpu user nice system idle iowait irq softirq steal guest guest_nice < /proc/stat - - # Calculate new total and idle times. - local total_idle=$((idle + iowait)) - local total=$((user + nice + system + idle + iowait + irq + softirq + steal + guest + guest_nice)) - - # Add a check to prevent division by zero if total hasn't changed. - if (( total <= prev_total )); then - echo "0.0" - return - fi - - # Calculate the difference over the interval. - local diff_total=$((total - prev_total)) - local diff_idle=$((total_idle - prev_total_idle)) - - # Use awk for floating-point calculation and print the percentage. - awk -v total="$diff_total" -v idle="$diff_idle" ' - BEGIN { - if (total > 0) { - # Formula: 100 * (Total - Idle) / Total - usage = 100 * (total - idle) / total - printf "%.1f\n", usage - } else { - print "0.0" - } - }' -} - -# -# Finds and returns the CPU temperature in degrees Celsius. -# Caches the sensor path for efficiency. -# -get_cpu_temp() { - # If the sensor path hasn't been found yet, search for it. - if [[ -z "$TEMP_SENSOR_PATH" ]]; then - for dir in /sys/class/hwmon/hwmon*; do - # Check if the 'name' file exists and read it. - if [[ -f "$dir/name" ]]; then - local name - name=$(<"$dir/name") - # Check for supported sensor types. - if [[ "$name" == "coretemp" || "$name" == "k10temp" || "$name" == "zenpower" ]]; then - TEMP_SENSOR_PATH=$dir - TEMP_SENSOR_TYPE=$name - break # Found it, no need to keep searching. - fi - fi - done - fi - - # If after searching no sensor was found, return 0. - if [[ -z "$TEMP_SENSOR_PATH" ]]; then - echo 0 - return - fi - - # --- Get temp based on sensor type --- - if [[ "$TEMP_SENSOR_TYPE" == "coretemp" ]]; then - # For Intel 'coretemp', average all available temperature sensors. - local total_temp=0 - local sensor_count=0 - - # Use a for loop with a glob to iterate over all temp input files. - # This is more efficient than 'find' for this simple case. - for temp_file in "$TEMP_SENSOR_PATH"/temp*_input; do - # The glob returns the pattern itself if no files match, - # so we must check if the file actually exists. - if [[ -f "$temp_file" ]]; then - total_temp=$((total_temp + $(<"$temp_file"))) - sensor_count=$((sensor_count + 1)) - fi - done - - if (( sensor_count > 0 )); then - # Use awk for the final division to handle potential floating point numbers - # and convert from millidegrees to integer degrees Celsius. - awk -v total="$total_temp" -v count="$sensor_count" 'BEGIN { print int(total / count / 1000) }' - else - # If no sensor files were found, return 0. - echo 0 - fi - - elif [[ "$TEMP_SENSOR_TYPE" == "k10temp" ]]; then - # For AMD 'k10temp', find the 'Tctl' sensor, which is the control temperature. - local tctl_input="" - for label_file in "$TEMP_SENSOR_PATH"/temp*_label; do - if [[ -f "$label_file" ]] && [[ $(<"$label_file") == "Tctl" ]]; then - # The input file has the same name but with '_input' instead of '_label'. - tctl_input="${label_file%_label}_input" - break - fi - done - - if [[ -f "$tctl_input" ]]; then - # Read the temperature and convert from millidegrees to degrees. - echo "$(( $(<"$tctl_input") / 1000 ))" - else - echo 0 # Fallback - fi - elif [[ "$TEMP_SENSOR_TYPE" == "zenpower" ]]; then - # For zenpower, read the first available temp sensor - for temp_file in "$TEMP_SENSOR_PATH"/temp*_input; do - if [[ -f "$temp_file" ]]; then - local temp_value - temp_value=$(cat "$temp_file" | tr -d '\n\r') # Remove any newlines - echo "$((temp_value / 1000))" - return - fi - done - echo 0 - - if [[ -f "$tctl_input" ]]; then - # Read the temperature and convert from millidegrees to degrees. - echo "$(($(<"$tctl_input") / 1000))" - else - echo 0 # Fallback - fi - else - echo 0 # Should not happen if cache logic is correct. - fi -} - - - -# --- Main Loop --- -# This loop runs indefinitely, gathering and printing stats. -while true; do - # Call the functions to gather all the data. - # get_memory_info - read -r mem_gb mem_per <<< "$(get_memory_info)" - - # Command substitution captures the single output from the other functions. - disk_per=$(get_disk_usage) - cpu_usage=$(get_cpu_usage) - cpu_temp=$(get_cpu_temp) - - # Get network speeds - current_time=$(date +%s.%N) - total_rx=0 - total_tx=0 - - # Read total bytes from /proc/net/dev for all interfaces - while IFS=: read -r interface stats; do - # Skip only loopback interface, allow other interfaces - if [[ "$interface" =~ ^lo[[:space:]]*$ ]]; then - continue - fi - - # Extract rx and tx bytes (fields 1 and 9 in the stats part) - rx_bytes=$(echo "$stats" | awk '{print $1}') - tx_bytes=$(echo "$stats" | awk '{print $9}') - - # Add to totals if they are valid numbers - if [[ "$rx_bytes" =~ ^[0-9]+$ ]] && [[ "$tx_bytes" =~ ^[0-9]+$ ]]; then - total_rx=$((total_rx + rx_bytes)) - total_tx=$((total_tx + tx_bytes)) - fi - done < <(tail -n +3 /proc/net/dev) - - # Calculate speeds if we have previous data - rx_speed=0 - tx_speed=0 - - if [[ "$PREV_TIME" != "0" ]]; then - time_diff=$(awk -v current="$current_time" -v prev="$PREV_TIME" 'BEGIN { printf "%.3f", current - prev }') - rx_diff=$((total_rx - PREV_RX_BYTES)) - tx_diff=$((total_tx - PREV_TX_BYTES)) - - # Calculate speeds in bytes per second using awk - rx_speed=$(awk -v rx="$rx_diff" -v time="$time_diff" 'BEGIN { printf "%.0f", rx / time }') - tx_speed=$(awk -v tx="$tx_diff" -v time="$time_diff" 'BEGIN { printf "%.0f", tx / time }') - fi - - # Update previous values for next iteration - PREV_RX_BYTES=$total_rx - PREV_TX_BYTES=$total_tx - PREV_TIME=$current_time - - # Use printf to format the final JSON output string, adding the mem_mb key. - printf '{"cpu": "%s", "cputemp": "%s", "memgb":"%s", "memper": "%s", "diskper": "%s", "rx_speed": "%s", "tx_speed": "%s"}\n' \ - "$cpu_usage" \ - "$cpu_temp" \ - "$mem_gb" \ - "$mem_per" \ - "$disk_per" \ - "$rx_speed" \ - "$tx_speed" - - # Wait for the specified duration before the next update. - sleep "$SLEEP_DURATION" -done diff --git a/Commons/Bootstrap.qml b/Commons/Bootstrap.qml new file mode 100644 index 0000000..efbac74 --- /dev/null +++ b/Commons/Bootstrap.qml @@ -0,0 +1,2090 @@ +pragma Singleton + +import QtQuick +import QtQuick.Controls +import Quickshell +import qs.Commons + +Singleton { + id: root + + property var icons: { + "alarm-fill": "\uF101", + "alarm": "\uF102", + "align-bottom": "\uF103", + "align-center": "\uF104", + "align-end": "\uF105", + "align-middle": "\uF106", + "align-start": "\uF107", + "align-top": "\uF108", + "alt": "\uF109", + "app-indicator": "\uF10A", + "app": "\uF10B", + "archive-fill": "\uF10C", + "archive": "\uF10D", + "arrow-90deg-down": "\uF10E", + "arrow-90deg-left": "\uF10F", + "arrow-90deg-right": "\uF110", + "arrow-90deg-up": "\uF111", + "arrow-bar-down": "\uF112", + "arrow-bar-left": "\uF113", + "arrow-bar-right": "\uF114", + "arrow-bar-up": "\uF115", + "arrow-clockwise": "\uF116", + "arrow-counterclockwise": "\uF117", + "arrow-down-circle-fill": "\uF118", + "arrow-down-circle": "\uF119", + "arrow-down-left-circle-fill": "\uF11A", + "arrow-down-left-circle": "\uF11B", + "arrow-down-left-square-fill": "\uF11C", + "arrow-down-left-square": "\uF11D", + "arrow-down-left": "\uF11E", + "arrow-down-right-circle-fill": "\uF11F", + "arrow-down-right-circle": "\uF120", + "arrow-down-right-square-fill": "\uF121", + "arrow-down-right-square": "\uF122", + "arrow-down-right": "\uF123", + "arrow-down-short": "\uF124", + "arrow-down-square-fill": "\uF125", + "arrow-down-square": "\uF126", + "arrow-down-up": "\uF127", + "arrow-down": "\uF128", + "arrow-left-circle-fill": "\uF129", + "arrow-left-circle": "\uF12A", + "arrow-left-right": "\uF12B", + "arrow-left-short": "\uF12C", + "arrow-left-square-fill": "\uF12D", + "arrow-left-square": "\uF12E", + "arrow-left": "\uF12F", + "arrow-repeat": "\uF130", + "arrow-return-left": "\uF131", + "arrow-return-right": "\uF132", + "arrow-right-circle-fill": "\uF133", + "arrow-right-circle": "\uF134", + "arrow-right-short": "\uF135", + "arrow-right-square-fill": "\uF136", + "arrow-right-square": "\uF137", + "arrow-right": "\uF138", + "arrow-up-circle-fill": "\uF139", + "arrow-up-circle": "\uF13A", + "arrow-up-left-circle-fill": "\uF13B", + "arrow-up-left-circle": "\uF13C", + "arrow-up-left-square-fill": "\uF13D", + "arrow-up-left-square": "\uF13E", + "arrow-up-left": "\uF13F", + "arrow-up-right-circle-fill": "\uF140", + "arrow-up-right-circle": "\uF141", + "arrow-up-right-square-fill": "\uF142", + "arrow-up-right-square": "\uF143", + "arrow-up-right": "\uF144", + "arrow-up-short": "\uF145", + "arrow-up-square-fill": "\uF146", + "arrow-up-square": "\uF147", + "arrow-up": "\uF148", + "arrows-angle-contract": "\uF149", + "arrows-angle-expand": "\uF14A", + "arrows-collapse": "\uF14B", + "arrows-expand": "\uF14C", + "arrows-fullscreen": "\uF14D", + "arrows-move": "\uF14E", + "aspect-ratio-fill": "\uF14F", + "aspect-ratio": "\uF150", + "asterisk": "\uF151", + "at": "\uF152", + "award-fill": "\uF153", + "award": "\uF154", + "back": "\uF155", + "backspace-fill": "\uF156", + "backspace-reverse-fill": "\uF157", + "backspace-reverse": "\uF158", + "backspace": "\uF159", + "badge-3d-fill": "\uF15A", + "badge-3d": "\uF15B", + "badge-4k-fill": "\uF15C", + "badge-4k": "\uF15D", + "badge-8k-fill": "\uF15E", + "badge-8k": "\uF15F", + "badge-ad-fill": "\uF160", + "badge-ad": "\uF161", + "badge-ar-fill": "\uF162", + "badge-ar": "\uF163", + "badge-cc-fill": "\uF164", + "badge-cc": "\uF165", + "badge-hd-fill": "\uF166", + "badge-hd": "\uF167", + "badge-tm-fill": "\uF168", + "badge-tm": "\uF169", + "badge-vo-fill": "\uF16A", + "badge-vo": "\uF16B", + "badge-vr-fill": "\uF16C", + "badge-vr": "\uF16D", + "badge-wc-fill": "\uF16E", + "badge-wc": "\uF16F", + "bag-check-fill": "\uF170", + "bag-check": "\uF171", + "bag-dash-fill": "\uF172", + "bag-dash": "\uF173", + "bag-fill": "\uF174", + "bag-plus-fill": "\uF175", + "bag-plus": "\uF176", + "bag-x-fill": "\uF177", + "bag-x": "\uF178", + "bag": "\uF179", + "bar-chart-fill": "\uF17A", + "bar-chart-line-fill": "\uF17B", + "bar-chart-line": "\uF17C", + "bar-chart-steps": "\uF17D", + "bar-chart": "\uF17E", + "basket-fill": "\uF17F", + "basket": "\uF180", + "basket2-fill": "\uF181", + "basket2": "\uF182", + "basket3-fill": "\uF183", + "basket3": "\uF184", + "battery-charging": "\uF185", + "battery-full": "\uF186", + "battery-half": "\uF187", + "battery": "\uF188", + "bell-fill": "\uF189", + "bell": "\uF18A", + "bezier": "\uF18B", + "bezier2": "\uF18C", + "bicycle": "\uF18D", + "binoculars-fill": "\uF18E", + "binoculars": "\uF18F", + "blockquote-left": "\uF190", + "blockquote-right": "\uF191", + "book-fill": "\uF192", + "book-half": "\uF193", + "book": "\uF194", + "bookmark-check-fill": "\uF195", + "bookmark-check": "\uF196", + "bookmark-dash-fill": "\uF197", + "bookmark-dash": "\uF198", + "bookmark-fill": "\uF199", + "bookmark-heart-fill": "\uF19A", + "bookmark-heart": "\uF19B", + "bookmark-plus-fill": "\uF19C", + "bookmark-plus": "\uF19D", + "bookmark-star-fill": "\uF19E", + "bookmark-star": "\uF19F", + "bookmark-x-fill": "\uF1A0", + "bookmark-x": "\uF1A1", + "bookmark": "\uF1A2", + "bookmarks-fill": "\uF1A3", + "bookmarks": "\uF1A4", + "bookshelf": "\uF1A5", + "bootstrap-fill": "\uF1A6", + "bootstrap-reboot": "\uF1A7", + "bootstrap": "\uF1A8", + "border-all": "\uF1A9", + "border-bottom": "\uF1AA", + "border-center": "\uF1AB", + "border-inner": "\uF1AC", + "border-left": "\uF1AD", + "border-middle": "\uF1AE", + "border-outer": "\uF1AF", + "border-right": "\uF1B0", + "border-style": "\uF1B1", + "border-top": "\uF1B2", + "border-width": "\uF1B3", + "border": "\uF1B4", + "bounding-box-circles": "\uF1B5", + "bounding-box": "\uF1B6", + "box-arrow-down-left": "\uF1B7", + "box-arrow-down-right": "\uF1B8", + "box-arrow-down": "\uF1B9", + "box-arrow-in-down-left": "\uF1BA", + "box-arrow-in-down-right": "\uF1BB", + "box-arrow-in-down": "\uF1BC", + "box-arrow-in-left": "\uF1BD", + "box-arrow-in-right": "\uF1BE", + "box-arrow-in-up-left": "\uF1BF", + "box-arrow-in-up-right": "\uF1C0", + "box-arrow-in-up": "\uF1C1", + "box-arrow-left": "\uF1C2", + "box-arrow-right": "\uF1C3", + "box-arrow-up-left": "\uF1C4", + "box-arrow-up-right": "\uF1C5", + "box-arrow-up": "\uF1C6", + "box-seam": "\uF1C7", + "box": "\uF1C8", + "braces": "\uF1C9", + "bricks": "\uF1CA", + "briefcase-fill": "\uF1CB", + "briefcase": "\uF1CC", + "brightness-alt-high-fill": "\uF1CD", + "brightness-alt-high": "\uF1CE", + "brightness-alt-low-fill": "\uF1CF", + "brightness-alt-low": "\uF1D0", + "brightness-high-fill": "\uF1D1", + "brightness-high": "\uF1D2", + "brightness-low-fill": "\uF1D3", + "brightness-low": "\uF1D4", + "broadcast-pin": "\uF1D5", + "broadcast": "\uF1D6", + "brush-fill": "\uF1D7", + "brush": "\uF1D8", + "bucket-fill": "\uF1D9", + "bucket": "\uF1DA", + "bug-fill": "\uF1DB", + "bug": "\uF1DC", + "building": "\uF1DD", + "bullseye": "\uF1DE", + "calculator-fill": "\uF1DF", + "calculator": "\uF1E0", + "calendar-check-fill": "\uF1E1", + "calendar-check": "\uF1E2", + "calendar-date-fill": "\uF1E3", + "calendar-date": "\uF1E4", + "calendar-day-fill": "\uF1E5", + "calendar-day": "\uF1E6", + "calendar-event-fill": "\uF1E7", + "calendar-event": "\uF1E8", + "calendar-fill": "\uF1E9", + "calendar-minus-fill": "\uF1EA", + "calendar-minus": "\uF1EB", + "calendar-month-fill": "\uF1EC", + "calendar-month": "\uF1ED", + "calendar-plus-fill": "\uF1EE", + "calendar-plus": "\uF1EF", + "calendar-range-fill": "\uF1F0", + "calendar-range": "\uF1F1", + "calendar-week-fill": "\uF1F2", + "calendar-week": "\uF1F3", + "calendar-x-fill": "\uF1F4", + "calendar-x": "\uF1F5", + "calendar": "\uF1F6", + "calendar2-check-fill": "\uF1F7", + "calendar2-check": "\uF1F8", + "calendar2-date-fill": "\uF1F9", + "calendar2-date": "\uF1FA", + "calendar2-day-fill": "\uF1FB", + "calendar2-day": "\uF1FC", + "calendar2-event-fill": "\uF1FD", + "calendar2-event": "\uF1FE", + "calendar2-fill": "\uF1FF", + "calendar2-minus-fill": "\uF200", + "calendar2-minus": "\uF201", + "calendar2-month-fill": "\uF202", + "calendar2-month": "\uF203", + "calendar2-plus-fill": "\uF204", + "calendar2-plus": "\uF205", + "calendar2-range-fill": "\uF206", + "calendar2-range": "\uF207", + "calendar2-week-fill": "\uF208", + "calendar2-week": "\uF209", + "calendar2-x-fill": "\uF20A", + "calendar2-x": "\uF20B", + "calendar2": "\uF20C", + "calendar3-event-fill": "\uF20D", + "calendar3-event": "\uF20E", + "calendar3-fill": "\uF20F", + "calendar3-range-fill": "\uF210", + "calendar3-range": "\uF211", + "calendar3-week-fill": "\uF212", + "calendar3-week": "\uF213", + "calendar3": "\uF214", + "calendar4-event": "\uF215", + "calendar4-range": "\uF216", + "calendar4-week": "\uF217", + "calendar4": "\uF218", + "camera-fill": "\uF219", + "camera-reels-fill": "\uF21A", + "camera-reels": "\uF21B", + "camera-video-fill": "\uF21C", + "camera-video-off-fill": "\uF21D", + "camera-video-off": "\uF21E", + "camera-video": "\uF21F", + "camera": "\uF220", + "camera2": "\uF221", + "capslock-fill": "\uF222", + "capslock": "\uF223", + "card-checklist": "\uF224", + "card-heading": "\uF225", + "card-image": "\uF226", + "card-list": "\uF227", + "card-text": "\uF228", + "caret-down-fill": "\uF229", + "caret-down-square-fill": "\uF22A", + "caret-down-square": "\uF22B", + "caret-down": "\uF22C", + "caret-left-fill": "\uF22D", + "caret-left-square-fill": "\uF22E", + "caret-left-square": "\uF22F", + "caret-left": "\uF230", + "caret-right-fill": "\uF231", + "caret-right-square-fill": "\uF232", + "caret-right-square": "\uF233", + "caret-right": "\uF234", + "caret-up-fill": "\uF235", + "caret-up-square-fill": "\uF236", + "caret-up-square": "\uF237", + "caret-up": "\uF238", + "cart-check-fill": "\uF239", + "cart-check": "\uF23A", + "cart-dash-fill": "\uF23B", + "cart-dash": "\uF23C", + "cart-fill": "\uF23D", + "cart-plus-fill": "\uF23E", + "cart-plus": "\uF23F", + "cart-x-fill": "\uF240", + "cart-x": "\uF241", + "cart": "\uF242", + "cart2": "\uF243", + "cart3": "\uF244", + "cart4": "\uF245", + "cash-stack": "\uF246", + "cash": "\uF247", + "cast": "\uF248", + "chat-dots-fill": "\uF249", + "chat-dots": "\uF24A", + "chat-fill": "\uF24B", + "chat-left-dots-fill": "\uF24C", + "chat-left-dots": "\uF24D", + "chat-left-fill": "\uF24E", + "chat-left-quote-fill": "\uF24F", + "chat-left-quote": "\uF250", + "chat-left-text-fill": "\uF251", + "chat-left-text": "\uF252", + "chat-left": "\uF253", + "chat-quote-fill": "\uF254", + "chat-quote": "\uF255", + "chat-right-dots-fill": "\uF256", + "chat-right-dots": "\uF257", + "chat-right-fill": "\uF258", + "chat-right-quote-fill": "\uF259", + "chat-right-quote": "\uF25A", + "chat-right-text-fill": "\uF25B", + "chat-right-text": "\uF25C", + "chat-right": "\uF25D", + "chat-square-dots-fill": "\uF25E", + "chat-square-dots": "\uF25F", + "chat-square-fill": "\uF260", + "chat-square-quote-fill": "\uF261", + "chat-square-quote": "\uF262", + "chat-square-text-fill": "\uF263", + "chat-square-text": "\uF264", + "chat-square": "\uF265", + "chat-text-fill": "\uF266", + "chat-text": "\uF267", + "chat": "\uF268", + "check-all": "\uF269", + "check-circle-fill": "\uF26A", + "check-circle": "\uF26B", + "check-square-fill": "\uF26C", + "check-square": "\uF26D", + "check": "\uF26E", + "check2-all": "\uF26F", + "check2-circle": "\uF270", + "check2-square": "\uF271", + "check2": "\uF272", + "chevron-bar-contract": "\uF273", + "chevron-bar-down": "\uF274", + "chevron-bar-expand": "\uF275", + "chevron-bar-left": "\uF276", + "chevron-bar-right": "\uF277", + "chevron-bar-up": "\uF278", + "chevron-compact-down": "\uF279", + "chevron-compact-left": "\uF27A", + "chevron-compact-right": "\uF27B", + "chevron-compact-up": "\uF27C", + "chevron-contract": "\uF27D", + "chevron-double-down": "\uF27E", + "chevron-double-left": "\uF27F", + "chevron-double-right": "\uF280", + "chevron-double-up": "\uF281", + "chevron-down": "\uF282", + "chevron-expand": "\uF283", + "chevron-left": "\uF284", + "chevron-right": "\uF285", + "chevron-up": "\uF286", + "circle-fill": "\uF287", + "circle-half": "\uF288", + "circle-square": "\uF289", + "circle": "\uF28A", + "clipboard-check": "\uF28B", + "clipboard-data": "\uF28C", + "clipboard-minus": "\uF28D", + "clipboard-plus": "\uF28E", + "clipboard-x": "\uF28F", + "clipboard": "\uF290", + "clock-fill": "\uF291", + "clock-history": "\uF292", + "clock": "\uF293", + "cloud-arrow-down-fill": "\uF294", + "cloud-arrow-down": "\uF295", + "cloud-arrow-up-fill": "\uF296", + "cloud-arrow-up": "\uF297", + "cloud-check-fill": "\uF298", + "cloud-check": "\uF299", + "cloud-download-fill": "\uF29A", + "cloud-download": "\uF29B", + "cloud-drizzle-fill": "\uF29C", + "cloud-drizzle": "\uF29D", + "cloud-fill": "\uF29E", + "cloud-fog-fill": "\uF29F", + "cloud-fog": "\uF2A0", + "cloud-fog2-fill": "\uF2A1", + "cloud-fog2": "\uF2A2", + "cloud-hail-fill": "\uF2A3", + "cloud-hail": "\uF2A4", + "cloud-haze-fill": "\uF2A6", + "cloud-haze": "\uF2A7", + "cloud-haze2-fill": "\uF2A8", + "cloud-lightning-fill": "\uF2A9", + "cloud-lightning-rain-fill": "\uF2AA", + "cloud-lightning-rain": "\uF2AB", + "cloud-lightning": "\uF2AC", + "cloud-minus-fill": "\uF2AD", + "cloud-minus": "\uF2AE", + "cloud-moon-fill": "\uF2AF", + "cloud-moon": "\uF2B0", + "cloud-plus-fill": "\uF2B1", + "cloud-plus": "\uF2B2", + "cloud-rain-fill": "\uF2B3", + "cloud-rain-heavy-fill": "\uF2B4", + "cloud-rain-heavy": "\uF2B5", + "cloud-rain": "\uF2B6", + "cloud-slash-fill": "\uF2B7", + "cloud-slash": "\uF2B8", + "cloud-sleet-fill": "\uF2B9", + "cloud-sleet": "\uF2BA", + "cloud-snow-fill": "\uF2BB", + "cloud-snow": "\uF2BC", + "cloud-sun-fill": "\uF2BD", + "cloud-sun": "\uF2BE", + "cloud-upload-fill": "\uF2BF", + "cloud-upload": "\uF2C0", + "cloud": "\uF2C1", + "clouds-fill": "\uF2C2", + "clouds": "\uF2C3", + "cloudy-fill": "\uF2C4", + "cloudy": "\uF2C5", + "code-slash": "\uF2C6", + "code-square": "\uF2C7", + "code": "\uF2C8", + "collection-fill": "\uF2C9", + "collection-play-fill": "\uF2CA", + "collection-play": "\uF2CB", + "collection": "\uF2CC", + "columns-gap": "\uF2CD", + "columns": "\uF2CE", + "command": "\uF2CF", + "compass-fill": "\uF2D0", + "compass": "\uF2D1", + "cone-striped": "\uF2D2", + "cone": "\uF2D3", + "controller": "\uF2D4", + "cpu-fill": "\uF2D5", + "cpu": "\uF2D6", + "credit-card-2-back-fill": "\uF2D7", + "credit-card-2-back": "\uF2D8", + "credit-card-2-front-fill": "\uF2D9", + "credit-card-2-front": "\uF2DA", + "credit-card-fill": "\uF2DB", + "credit-card": "\uF2DC", + "crop": "\uF2DD", + "cup-fill": "\uF2DE", + "cup-straw": "\uF2DF", + "cup": "\uF2E0", + "cursor-fill": "\uF2E1", + "cursor-text": "\uF2E2", + "cursor": "\uF2E3", + "dash-circle-dotted": "\uF2E4", + "dash-circle-fill": "\uF2E5", + "dash-circle": "\uF2E6", + "dash-square-dotted": "\uF2E7", + "dash-square-fill": "\uF2E8", + "dash-square": "\uF2E9", + "dash": "\uF2EA", + "diagram-2-fill": "\uF2EB", + "diagram-2": "\uF2EC", + "diagram-3-fill": "\uF2ED", + "diagram-3": "\uF2EE", + "diamond-fill": "\uF2EF", + "diamond-half": "\uF2F0", + "diamond": "\uF2F1", + "dice-1-fill": "\uF2F2", + "dice-1": "\uF2F3", + "dice-2-fill": "\uF2F4", + "dice-2": "\uF2F5", + "dice-3-fill": "\uF2F6", + "dice-3": "\uF2F7", + "dice-4-fill": "\uF2F8", + "dice-4": "\uF2F9", + "dice-5-fill": "\uF2FA", + "dice-5": "\uF2FB", + "dice-6-fill": "\uF2FC", + "dice-6": "\uF2FD", + "disc-fill": "\uF2FE", + "disc": "\uF2FF", + "discord": "\uF300", + "display-fill": "\uF301", + "display": "\uF302", + "distribute-horizontal": "\uF303", + "distribute-vertical": "\uF304", + "door-closed-fill": "\uF305", + "door-closed": "\uF306", + "door-open-fill": "\uF307", + "door-open": "\uF308", + "dot": "\uF309", + "download": "\uF30A", + "droplet-fill": "\uF30B", + "droplet-half": "\uF30C", + "droplet": "\uF30D", + "earbuds": "\uF30E", + "easel-fill": "\uF30F", + "easel": "\uF310", + "egg-fill": "\uF311", + "egg-fried": "\uF312", + "egg": "\uF313", + "eject-fill": "\uF314", + "eject": "\uF315", + "emoji-angry-fill": "\uF316", + "emoji-angry": "\uF317", + "emoji-dizzy-fill": "\uF318", + "emoji-dizzy": "\uF319", + "emoji-expressionless-fill": "\uF31A", + "emoji-expressionless": "\uF31B", + "emoji-frown-fill": "\uF31C", + "emoji-frown": "\uF31D", + "emoji-heart-eyes-fill": "\uF31E", + "emoji-heart-eyes": "\uF31F", + "emoji-laughing-fill": "\uF320", + "emoji-laughing": "\uF321", + "emoji-neutral-fill": "\uF322", + "emoji-neutral": "\uF323", + "emoji-smile-fill": "\uF324", + "emoji-smile-upside-down-fill": "\uF325", + "emoji-smile-upside-down": "\uF326", + "emoji-smile": "\uF327", + "emoji-sunglasses-fill": "\uF328", + "emoji-sunglasses": "\uF329", + "emoji-wink-fill": "\uF32A", + "emoji-wink": "\uF32B", + "envelope-fill": "\uF32C", + "envelope-open-fill": "\uF32D", + "envelope-open": "\uF32E", + "envelope": "\uF32F", + "eraser-fill": "\uF330", + "eraser": "\uF331", + "exclamation-circle-fill": "\uF332", + "exclamation-circle": "\uF333", + "exclamation-diamond-fill": "\uF334", + "exclamation-diamond": "\uF335", + "exclamation-octagon-fill": "\uF336", + "exclamation-octagon": "\uF337", + "exclamation-square-fill": "\uF338", + "exclamation-square": "\uF339", + "exclamation-triangle-fill": "\uF33A", + "exclamation-triangle": "\uF33B", + "exclamation": "\uF33C", + "exclude": "\uF33D", + "eye-fill": "\uF33E", + "eye-slash-fill": "\uF33F", + "eye-slash": "\uF340", + "eye": "\uF341", + "eyedropper": "\uF342", + "eyeglasses": "\uF343", + "facebook": "\uF344", + "file-arrow-down-fill": "\uF345", + "file-arrow-down": "\uF346", + "file-arrow-up-fill": "\uF347", + "file-arrow-up": "\uF348", + "file-bar-graph-fill": "\uF349", + "file-bar-graph": "\uF34A", + "file-binary-fill": "\uF34B", + "file-binary": "\uF34C", + "file-break-fill": "\uF34D", + "file-break": "\uF34E", + "file-check-fill": "\uF34F", + "file-check": "\uF350", + "file-code-fill": "\uF351", + "file-code": "\uF352", + "file-diff-fill": "\uF353", + "file-diff": "\uF354", + "file-earmark-arrow-down-fill": "\uF355", + "file-earmark-arrow-down": "\uF356", + "file-earmark-arrow-up-fill": "\uF357", + "file-earmark-arrow-up": "\uF358", + "file-earmark-bar-graph-fill": "\uF359", + "file-earmark-bar-graph": "\uF35A", + "file-earmark-binary-fill": "\uF35B", + "file-earmark-binary": "\uF35C", + "file-earmark-break-fill": "\uF35D", + "file-earmark-break": "\uF35E", + "file-earmark-check-fill": "\uF35F", + "file-earmark-check": "\uF360", + "file-earmark-code-fill": "\uF361", + "file-earmark-code": "\uF362", + "file-earmark-diff-fill": "\uF363", + "file-earmark-diff": "\uF364", + "file-earmark-easel-fill": "\uF365", + "file-earmark-easel": "\uF366", + "file-earmark-excel-fill": "\uF367", + "file-earmark-excel": "\uF368", + "file-earmark-fill": "\uF369", + "file-earmark-font-fill": "\uF36A", + "file-earmark-font": "\uF36B", + "file-earmark-image-fill": "\uF36C", + "file-earmark-image": "\uF36D", + "file-earmark-lock-fill": "\uF36E", + "file-earmark-lock": "\uF36F", + "file-earmark-lock2-fill": "\uF370", + "file-earmark-lock2": "\uF371", + "file-earmark-medical-fill": "\uF372", + "file-earmark-medical": "\uF373", + "file-earmark-minus-fill": "\uF374", + "file-earmark-minus": "\uF375", + "file-earmark-music-fill": "\uF376", + "file-earmark-music": "\uF377", + "file-earmark-person-fill": "\uF378", + "file-earmark-person": "\uF379", + "file-earmark-play-fill": "\uF37A", + "file-earmark-play": "\uF37B", + "file-earmark-plus-fill": "\uF37C", + "file-earmark-plus": "\uF37D", + "file-earmark-post-fill": "\uF37E", + "file-earmark-post": "\uF37F", + "file-earmark-ppt-fill": "\uF380", + "file-earmark-ppt": "\uF381", + "file-earmark-richtext-fill": "\uF382", + "file-earmark-richtext": "\uF383", + "file-earmark-ruled-fill": "\uF384", + "file-earmark-ruled": "\uF385", + "file-earmark-slides-fill": "\uF386", + "file-earmark-slides": "\uF387", + "file-earmark-spreadsheet-fill": "\uF388", + "file-earmark-spreadsheet": "\uF389", + "file-earmark-text-fill": "\uF38A", + "file-earmark-text": "\uF38B", + "file-earmark-word-fill": "\uF38C", + "file-earmark-word": "\uF38D", + "file-earmark-x-fill": "\uF38E", + "file-earmark-x": "\uF38F", + "file-earmark-zip-fill": "\uF390", + "file-earmark-zip": "\uF391", + "file-earmark": "\uF392", + "file-easel-fill": "\uF393", + "file-easel": "\uF394", + "file-excel-fill": "\uF395", + "file-excel": "\uF396", + "file-fill": "\uF397", + "file-font-fill": "\uF398", + "file-font": "\uF399", + "file-image-fill": "\uF39A", + "file-image": "\uF39B", + "file-lock-fill": "\uF39C", + "file-lock": "\uF39D", + "file-lock2-fill": "\uF39E", + "file-lock2": "\uF39F", + "file-medical-fill": "\uF3A0", + "file-medical": "\uF3A1", + "file-minus-fill": "\uF3A2", + "file-minus": "\uF3A3", + "file-music-fill": "\uF3A4", + "file-music": "\uF3A5", + "file-person-fill": "\uF3A6", + "file-person": "\uF3A7", + "file-play-fill": "\uF3A8", + "file-play": "\uF3A9", + "file-plus-fill": "\uF3AA", + "file-plus": "\uF3AB", + "file-post-fill": "\uF3AC", + "file-post": "\uF3AD", + "file-ppt-fill": "\uF3AE", + "file-ppt": "\uF3AF", + "file-richtext-fill": "\uF3B0", + "file-richtext": "\uF3B1", + "file-ruled-fill": "\uF3B2", + "file-ruled": "\uF3B3", + "file-slides-fill": "\uF3B4", + "file-slides": "\uF3B5", + "file-spreadsheet-fill": "\uF3B6", + "file-spreadsheet": "\uF3B7", + "file-text-fill": "\uF3B8", + "file-text": "\uF3B9", + "file-word-fill": "\uF3BA", + "file-word": "\uF3BB", + "file-x-fill": "\uF3BC", + "file-x": "\uF3BD", + "file-zip-fill": "\uF3BE", + "file-zip": "\uF3BF", + "file": "\uF3C0", + "files-alt": "\uF3C1", + "files": "\uF3C2", + "film": "\uF3C3", + "filter-circle-fill": "\uF3C4", + "filter-circle": "\uF3C5", + "filter-left": "\uF3C6", + "filter-right": "\uF3C7", + "filter-square-fill": "\uF3C8", + "filter-square": "\uF3C9", + "filter": "\uF3CA", + "flag-fill": "\uF3CB", + "flag": "\uF3CC", + "flower1": "\uF3CD", + "flower2": "\uF3CE", + "flower3": "\uF3CF", + "folder-check": "\uF3D0", + "folder-fill": "\uF3D1", + "folder-minus": "\uF3D2", + "folder-plus": "\uF3D3", + "folder-symlink-fill": "\uF3D4", + "folder-symlink": "\uF3D5", + "folder-x": "\uF3D6", + "folder": "\uF3D7", + "folder2-open": "\uF3D8", + "folder2": "\uF3D9", + "fonts": "\uF3DA", + "forward-fill": "\uF3DB", + "forward": "\uF3DC", + "front": "\uF3DD", + "fullscreen-exit": "\uF3DE", + "fullscreen": "\uF3DF", + "funnel-fill": "\uF3E0", + "funnel": "\uF3E1", + "gear-fill": "\uF3E2", + "gear-wide-connected": "\uF3E3", + "gear-wide": "\uF3E4", + "gear": "\uF3E5", + "gem": "\uF3E6", + "geo-alt-fill": "\uF3E7", + "geo-alt": "\uF3E8", + "geo-fill": "\uF3E9", + "geo": "\uF3EA", + "gift-fill": "\uF3EB", + "gift": "\uF3EC", + "github": "\uF3ED", + "globe": "\uF3EE", + "globe2": "\uF3EF", + "google": "\uF3F0", + "graph-down": "\uF3F1", + "graph-up": "\uF3F2", + "grid-1x2-fill": "\uF3F3", + "grid-1x2": "\uF3F4", + "grid-3x2-gap-fill": "\uF3F5", + "grid-3x2-gap": "\uF3F6", + "grid-3x2": "\uF3F7", + "grid-3x3-gap-fill": "\uF3F8", + "grid-3x3-gap": "\uF3F9", + "grid-3x3": "\uF3FA", + "grid-fill": "\uF3FB", + "grid": "\uF3FC", + "grip-horizontal": "\uF3FD", + "grip-vertical": "\uF3FE", + "hammer": "\uF3FF", + "hand-index-fill": "\uF400", + "hand-index-thumb-fill": "\uF401", + "hand-index-thumb": "\uF402", + "hand-index": "\uF403", + "hand-thumbs-down-fill": "\uF404", + "hand-thumbs-down": "\uF405", + "hand-thumbs-up-fill": "\uF406", + "hand-thumbs-up": "\uF407", + "handbag-fill": "\uF408", + "handbag": "\uF409", + "hash": "\uF40A", + "hdd-fill": "\uF40B", + "hdd-network-fill": "\uF40C", + "hdd-network": "\uF40D", + "hdd-rack-fill": "\uF40E", + "hdd-rack": "\uF40F", + "hdd-stack-fill": "\uF410", + "hdd-stack": "\uF411", + "hdd": "\uF412", + "headphones": "\uF413", + "headset": "\uF414", + "heart-fill": "\uF415", + "heart-half": "\uF416", + "heart": "\uF417", + "heptagon-fill": "\uF418", + "heptagon-half": "\uF419", + "heptagon": "\uF41A", + "hexagon-fill": "\uF41B", + "hexagon-half": "\uF41C", + "hexagon": "\uF41D", + "hourglass-bottom": "\uF41E", + "hourglass-split": "\uF41F", + "hourglass-top": "\uF420", + "hourglass": "\uF421", + "house-door-fill": "\uF422", + "house-door": "\uF423", + "house-fill": "\uF424", + "house": "\uF425", + "hr": "\uF426", + "hurricane": "\uF427", + "image-alt": "\uF428", + "image-fill": "\uF429", + "image": "\uF42A", + "images": "\uF42B", + "inbox-fill": "\uF42C", + "inbox": "\uF42D", + "inboxes-fill": "\uF42E", + "inboxes": "\uF42F", + "info-circle-fill": "\uF430", + "info-circle": "\uF431", + "info-square-fill": "\uF432", + "info-square": "\uF433", + "info": "\uF434", + "input-cursor-text": "\uF435", + "input-cursor": "\uF436", + "instagram": "\uF437", + "intersect": "\uF438", + "journal-album": "\uF439", + "journal-arrow-down": "\uF43A", + "journal-arrow-up": "\uF43B", + "journal-bookmark-fill": "\uF43C", + "journal-bookmark": "\uF43D", + "journal-check": "\uF43E", + "journal-code": "\uF43F", + "journal-medical": "\uF440", + "journal-minus": "\uF441", + "journal-plus": "\uF442", + "journal-richtext": "\uF443", + "journal-text": "\uF444", + "journal-x": "\uF445", + "journal": "\uF446", + "journals": "\uF447", + "joystick": "\uF448", + "justify-left": "\uF449", + "justify-right": "\uF44A", + "justify": "\uF44B", + "kanban-fill": "\uF44C", + "kanban": "\uF44D", + "key-fill": "\uF44E", + "key": "\uF44F", + "keyboard-fill": "\uF450", + "keyboard": "\uF451", + "ladder": "\uF452", + "lamp-fill": "\uF453", + "lamp": "\uF454", + "laptop-fill": "\uF455", + "laptop": "\uF456", + "layer-backward": "\uF457", + "layer-forward": "\uF458", + "layers-fill": "\uF459", + "layers-half": "\uF45A", + "layers": "\uF45B", + "layout-sidebar-inset-reverse": "\uF45C", + "layout-sidebar-inset": "\uF45D", + "layout-sidebar-reverse": "\uF45E", + "layout-sidebar": "\uF45F", + "layout-split": "\uF460", + "layout-text-sidebar-reverse": "\uF461", + "layout-text-sidebar": "\uF462", + "layout-text-window-reverse": "\uF463", + "layout-text-window": "\uF464", + "layout-three-columns": "\uF465", + "layout-wtf": "\uF466", + "life-preserver": "\uF467", + "lightbulb-fill": "\uF468", + "lightbulb-off-fill": "\uF469", + "lightbulb-off": "\uF46A", + "lightbulb": "\uF46B", + "lightning-charge-fill": "\uF46C", + "lightning-charge": "\uF46D", + "lightning-fill": "\uF46E", + "lightning": "\uF46F", + "link-45deg": "\uF470", + "link": "\uF471", + "linkedin": "\uF472", + "list-check": "\uF473", + "list-nested": "\uF474", + "list-ol": "\uF475", + "list-stars": "\uF476", + "list-task": "\uF477", + "list-ul": "\uF478", + "list": "\uF479", + "lock-fill": "\uF47A", + "lock": "\uF47B", + "mailbox": "\uF47C", + "mailbox2": "\uF47D", + "map-fill": "\uF47E", + "map": "\uF47F", + "markdown-fill": "\uF480", + "markdown": "\uF481", + "mask": "\uF482", + "megaphone-fill": "\uF483", + "megaphone": "\uF484", + "menu-app-fill": "\uF485", + "menu-app": "\uF486", + "menu-button-fill": "\uF487", + "menu-button-wide-fill": "\uF488", + "menu-button-wide": "\uF489", + "menu-button": "\uF48A", + "menu-down": "\uF48B", + "menu-up": "\uF48C", + "mic-fill": "\uF48D", + "mic-mute-fill": "\uF48E", + "mic-mute": "\uF48F", + "mic": "\uF490", + "minecart-loaded": "\uF491", + "minecart": "\uF492", + "moisture": "\uF493", + "moon-fill": "\uF494", + "moon-stars-fill": "\uF495", + "moon-stars": "\uF496", + "moon": "\uF497", + "mouse-fill": "\uF498", + "mouse": "\uF499", + "mouse2-fill": "\uF49A", + "mouse2": "\uF49B", + "mouse3-fill": "\uF49C", + "mouse3": "\uF49D", + "music-note-beamed": "\uF49E", + "music-note-list": "\uF49F", + "music-note": "\uF4A0", + "music-player-fill": "\uF4A1", + "music-player": "\uF4A2", + "newspaper": "\uF4A3", + "node-minus-fill": "\uF4A4", + "node-minus": "\uF4A5", + "node-plus-fill": "\uF4A6", + "node-plus": "\uF4A7", + "nut-fill": "\uF4A8", + "nut": "\uF4A9", + "octagon-fill": "\uF4AA", + "octagon-half": "\uF4AB", + "octagon": "\uF4AC", + "option": "\uF4AD", + "outlet": "\uF4AE", + "paint-bucket": "\uF4AF", + "palette-fill": "\uF4B0", + "palette": "\uF4B1", + "palette2": "\uF4B2", + "paperclip": "\uF4B3", + "paragraph": "\uF4B4", + "patch-check-fill": "\uF4B5", + "patch-check": "\uF4B6", + "patch-exclamation-fill": "\uF4B7", + "patch-exclamation": "\uF4B8", + "patch-minus-fill": "\uF4B9", + "patch-minus": "\uF4BA", + "patch-plus-fill": "\uF4BB", + "patch-plus": "\uF4BC", + "patch-question-fill": "\uF4BD", + "patch-question": "\uF4BE", + "pause-btn-fill": "\uF4BF", + "pause-btn": "\uF4C0", + "pause-circle-fill": "\uF4C1", + "pause-circle": "\uF4C2", + "pause-fill": "\uF4C3", + "pause": "\uF4C4", + "peace-fill": "\uF4C5", + "peace": "\uF4C6", + "pen-fill": "\uF4C7", + "pen": "\uF4C8", + "pencil-fill": "\uF4C9", + "pencil-square": "\uF4CA", + "pencil": "\uF4CB", + "pentagon-fill": "\uF4CC", + "pentagon-half": "\uF4CD", + "pentagon": "\uF4CE", + "people-fill": "\uF4CF", + "people": "\uF4D0", + "percent": "\uF4D1", + "person-badge-fill": "\uF4D2", + "person-badge": "\uF4D3", + "person-bounding-box": "\uF4D4", + "person-check-fill": "\uF4D5", + "person-check": "\uF4D6", + "person-circle": "\uF4D7", + "person-dash-fill": "\uF4D8", + "person-dash": "\uF4D9", + "person-fill": "\uF4DA", + "person-lines-fill": "\uF4DB", + "person-plus-fill": "\uF4DC", + "person-plus": "\uF4DD", + "person-square": "\uF4DE", + "person-x-fill": "\uF4DF", + "person-x": "\uF4E0", + "person": "\uF4E1", + "phone-fill": "\uF4E2", + "phone-landscape-fill": "\uF4E3", + "phone-landscape": "\uF4E4", + "phone-vibrate-fill": "\uF4E5", + "phone-vibrate": "\uF4E6", + "phone": "\uF4E7", + "pie-chart-fill": "\uF4E8", + "pie-chart": "\uF4E9", + "pin-angle-fill": "\uF4EA", + "pin-angle": "\uF4EB", + "pin-fill": "\uF4EC", + "pin": "\uF4ED", + "pip-fill": "\uF4EE", + "pip": "\uF4EF", + "play-btn-fill": "\uF4F0", + "play-btn": "\uF4F1", + "play-circle-fill": "\uF4F2", + "play-circle": "\uF4F3", + "play-fill": "\uF4F4", + "play": "\uF4F5", + "plug-fill": "\uF4F6", + "plug": "\uF4F7", + "plus-circle-dotted": "\uF4F8", + "plus-circle-fill": "\uF4F9", + "plus-circle": "\uF4FA", + "plus-square-dotted": "\uF4FB", + "plus-square-fill": "\uF4FC", + "plus-square": "\uF4FD", + "plus": "\uF4FE", + "power": "\uF4FF", + "printer-fill": "\uF500", + "printer": "\uF501", + "puzzle-fill": "\uF502", + "puzzle": "\uF503", + "question-circle-fill": "\uF504", + "question-circle": "\uF505", + "question-diamond-fill": "\uF506", + "question-diamond": "\uF507", + "question-octagon-fill": "\uF508", + "question-octagon": "\uF509", + "question-square-fill": "\uF50A", + "question-square": "\uF50B", + "question": "\uF50C", + "rainbow": "\uF50D", + "receipt-cutoff": "\uF50E", + "receipt": "\uF50F", + "reception-0": "\uF510", + "reception-1": "\uF511", + "reception-2": "\uF512", + "reception-3": "\uF513", + "reception-4": "\uF514", + "record-btn-fill": "\uF515", + "record-btn": "\uF516", + "record-circle-fill": "\uF517", + "record-circle": "\uF518", + "record-fill": "\uF519", + "record": "\uF51A", + "record2-fill": "\uF51B", + "record2": "\uF51C", + "reply-all-fill": "\uF51D", + "reply-all": "\uF51E", + "reply-fill": "\uF51F", + "reply": "\uF520", + "rss-fill": "\uF521", + "rss": "\uF522", + "rulers": "\uF523", + "save-fill": "\uF524", + "save": "\uF525", + "save2-fill": "\uF526", + "save2": "\uF527", + "scissors": "\uF528", + "screwdriver": "\uF529", + "search": "\uF52A", + "segmented-nav": "\uF52B", + "server": "\uF52C", + "share-fill": "\uF52D", + "share": "\uF52E", + "shield-check": "\uF52F", + "shield-exclamation": "\uF530", + "shield-fill-check": "\uF531", + "shield-fill-exclamation": "\uF532", + "shield-fill-minus": "\uF533", + "shield-fill-plus": "\uF534", + "shield-fill-x": "\uF535", + "shield-fill": "\uF536", + "shield-lock-fill": "\uF537", + "shield-lock": "\uF538", + "shield-minus": "\uF539", + "shield-plus": "\uF53A", + "shield-shaded": "\uF53B", + "shield-slash-fill": "\uF53C", + "shield-slash": "\uF53D", + "shield-x": "\uF53E", + "shield": "\uF53F", + "shift-fill": "\uF540", + "shift": "\uF541", + "shop-window": "\uF542", + "shop": "\uF543", + "shuffle": "\uF544", + "signpost-2-fill": "\uF545", + "signpost-2": "\uF546", + "signpost-fill": "\uF547", + "signpost-split-fill": "\uF548", + "signpost-split": "\uF549", + "signpost": "\uF54A", + "sim-fill": "\uF54B", + "sim": "\uF54C", + "skip-backward-btn-fill": "\uF54D", + "skip-backward-btn": "\uF54E", + "skip-backward-circle-fill": "\uF54F", + "skip-backward-circle": "\uF550", + "skip-backward-fill": "\uF551", + "skip-backward": "\uF552", + "skip-end-btn-fill": "\uF553", + "skip-end-btn": "\uF554", + "skip-end-circle-fill": "\uF555", + "skip-end-circle": "\uF556", + "skip-end-fill": "\uF557", + "skip-end": "\uF558", + "skip-forward-btn-fill": "\uF559", + "skip-forward-btn": "\uF55A", + "skip-forward-circle-fill": "\uF55B", + "skip-forward-circle": "\uF55C", + "skip-forward-fill": "\uF55D", + "skip-forward": "\uF55E", + "skip-start-btn-fill": "\uF55F", + "skip-start-btn": "\uF560", + "skip-start-circle-fill": "\uF561", + "skip-start-circle": "\uF562", + "skip-start-fill": "\uF563", + "skip-start": "\uF564", + "slack": "\uF565", + "slash-circle-fill": "\uF566", + "slash-circle": "\uF567", + "slash-square-fill": "\uF568", + "slash-square": "\uF569", + "slash": "\uF56A", + "sliders": "\uF56B", + "smartwatch": "\uF56C", + "snow": "\uF56D", + "snow2": "\uF56E", + "snow3": "\uF56F", + "sort-alpha-down-alt": "\uF570", + "sort-alpha-down": "\uF571", + "sort-alpha-up-alt": "\uF572", + "sort-alpha-up": "\uF573", + "sort-down-alt": "\uF574", + "sort-down": "\uF575", + "sort-numeric-down-alt": "\uF576", + "sort-numeric-down": "\uF577", + "sort-numeric-up-alt": "\uF578", + "sort-numeric-up": "\uF579", + "sort-up-alt": "\uF57A", + "sort-up": "\uF57B", + "soundwave": "\uF57C", + "speaker-fill": "\uF57D", + "speaker": "\uF57E", + "speedometer": "\uF57F", + "speedometer2": "\uF580", + "spellcheck": "\uF581", + "square-fill": "\uF582", + "square-half": "\uF583", + "square": "\uF584", + "stack": "\uF585", + "star-fill": "\uF586", + "star-half": "\uF587", + "star": "\uF588", + "stars": "\uF589", + "stickies-fill": "\uF58A", + "stickies": "\uF58B", + "sticky-fill": "\uF58C", + "sticky": "\uF58D", + "stop-btn-fill": "\uF58E", + "stop-btn": "\uF58F", + "stop-circle-fill": "\uF590", + "stop-circle": "\uF591", + "stop-fill": "\uF592", + "stop": "\uF593", + "stoplights-fill": "\uF594", + "stoplights": "\uF595", + "stopwatch-fill": "\uF596", + "stopwatch": "\uF597", + "subtract": "\uF598", + "suit-club-fill": "\uF599", + "suit-club": "\uF59A", + "suit-diamond-fill": "\uF59B", + "suit-diamond": "\uF59C", + "suit-heart-fill": "\uF59D", + "suit-heart": "\uF59E", + "suit-spade-fill": "\uF59F", + "suit-spade": "\uF5A0", + "sun-fill": "\uF5A1", + "sun": "\uF5A2", + "sunglasses": "\uF5A3", + "sunrise-fill": "\uF5A4", + "sunrise": "\uF5A5", + "sunset-fill": "\uF5A6", + "sunset": "\uF5A7", + "symmetry-horizontal": "\uF5A8", + "symmetry-vertical": "\uF5A9", + "table": "\uF5AA", + "tablet-fill": "\uF5AB", + "tablet-landscape-fill": "\uF5AC", + "tablet-landscape": "\uF5AD", + "tablet": "\uF5AE", + "tag-fill": "\uF5AF", + "tag": "\uF5B0", + "tags-fill": "\uF5B1", + "tags": "\uF5B2", + "telegram": "\uF5B3", + "telephone-fill": "\uF5B4", + "telephone-forward-fill": "\uF5B5", + "telephone-forward": "\uF5B6", + "telephone-inbound-fill": "\uF5B7", + "telephone-inbound": "\uF5B8", + "telephone-minus-fill": "\uF5B9", + "telephone-minus": "\uF5BA", + "telephone-outbound-fill": "\uF5BB", + "telephone-outbound": "\uF5BC", + "telephone-plus-fill": "\uF5BD", + "telephone-plus": "\uF5BE", + "telephone-x-fill": "\uF5BF", + "telephone-x": "\uF5C0", + "telephone": "\uF5C1", + "terminal-fill": "\uF5C2", + "terminal": "\uF5C3", + "text-center": "\uF5C4", + "text-indent-left": "\uF5C5", + "text-indent-right": "\uF5C6", + "text-left": "\uF5C7", + "text-paragraph": "\uF5C8", + "text-right": "\uF5C9", + "textarea-resize": "\uF5CA", + "textarea-t": "\uF5CB", + "textarea": "\uF5CC", + "thermometer-half": "\uF5CD", + "thermometer-high": "\uF5CE", + "thermometer-low": "\uF5CF", + "thermometer-snow": "\uF5D0", + "thermometer-sun": "\uF5D1", + "thermometer": "\uF5D2", + "three-dots-vertical": "\uF5D3", + "three-dots": "\uF5D4", + "toggle-off": "\uF5D5", + "toggle-on": "\uF5D6", + "toggle2-off": "\uF5D7", + "toggle2-on": "\uF5D8", + "toggles": "\uF5D9", + "toggles2": "\uF5DA", + "tools": "\uF5DB", + "tornado": "\uF5DC", + "trash-fill": "\uF5DD", + "trash": "\uF5DE", + "trash2-fill": "\uF5DF", + "trash2": "\uF5E0", + "tree-fill": "\uF5E1", + "tree": "\uF5E2", + "triangle-fill": "\uF5E3", + "triangle-half": "\uF5E4", + "triangle": "\uF5E5", + "trophy-fill": "\uF5E6", + "trophy": "\uF5E7", + "tropical-storm": "\uF5E8", + "truck-flatbed": "\uF5E9", + "truck": "\uF5EA", + "tsunami": "\uF5EB", + "tv-fill": "\uF5EC", + "tv": "\uF5ED", + "twitch": "\uF5EE", + "twitter": "\uF5EF", + "type-bold": "\uF5F0", + "type-h1": "\uF5F1", + "type-h2": "\uF5F2", + "type-h3": "\uF5F3", + "type-italic": "\uF5F4", + "type-strikethrough": "\uF5F5", + "type-underline": "\uF5F6", + "type": "\uF5F7", + "ui-checks-grid": "\uF5F8", + "ui-checks": "\uF5F9", + "ui-radios-grid": "\uF5FA", + "ui-radios": "\uF5FB", + "umbrella-fill": "\uF5FC", + "umbrella": "\uF5FD", + "union": "\uF5FE", + "unlock-fill": "\uF5FF", + "unlock": "\uF600", + "upc-scan": "\uF601", + "upc": "\uF602", + "upload": "\uF603", + "vector-pen": "\uF604", + "view-list": "\uF605", + "view-stacked": "\uF606", + "vinyl-fill": "\uF607", + "vinyl": "\uF608", + "voicemail": "\uF609", + "volume-down-fill": "\uF60A", + "volume-down": "\uF60B", + "volume-mute-fill": "\uF60C", + "volume-mute": "\uF60D", + "volume-off-fill": "\uF60E", + "volume-off": "\uF60F", + "volume-up-fill": "\uF610", + "volume-up": "\uF611", + "vr": "\uF612", + "wallet-fill": "\uF613", + "wallet": "\uF614", + "wallet2": "\uF615", + "watch": "\uF616", + "water": "\uF617", + "whatsapp": "\uF618", + "wifi-1": "\uF619", + "wifi-2": "\uF61A", + "wifi-off": "\uF61B", + "wifi": "\uF61C", + "wind": "\uF61D", + "window-dock": "\uF61E", + "window-sidebar": "\uF61F", + "window": "\uF620", + "wrench": "\uF621", + "x-circle-fill": "\uF622", + "x-circle": "\uF623", + "x-diamond-fill": "\uF624", + "x-diamond": "\uF625", + "x-octagon-fill": "\uF626", + "x-octagon": "\uF627", + "x-square-fill": "\uF628", + "x-square": "\uF629", + "x": "\uF62A", + "youtube": "\uF62B", + "zoom-in": "\uF62C", + "zoom-out": "\uF62D", + "bank": "\uF62E", + "bank2": "\uF62F", + "bell-slash-fill": "\uF630", + "bell-slash": "\uF631", + "cash-coin": "\uF632", + "check-lg": "\uF633", + "coin": "\uF634", + "currency-bitcoin": "\uF635", + "currency-dollar": "\uF636", + "currency-euro": "\uF637", + "currency-exchange": "\uF638", + "currency-pound": "\uF639", + "currency-yen": "\uF63A", + "dash-lg": "\uF63B", + "exclamation-lg": "\uF63C", + "file-earmark-pdf-fill": "\uF63D", + "file-earmark-pdf": "\uF63E", + "file-pdf-fill": "\uF63F", + "file-pdf": "\uF640", + "gender-ambiguous": "\uF641", + "gender-female": "\uF642", + "gender-male": "\uF643", + "gender-trans": "\uF644", + "headset-vr": "\uF645", + "info-lg": "\uF646", + "mastodon": "\uF647", + "messenger": "\uF648", + "piggy-bank-fill": "\uF649", + "piggy-bank": "\uF64A", + "pin-map-fill": "\uF64B", + "pin-map": "\uF64C", + "plus-lg": "\uF64D", + "question-lg": "\uF64E", + "recycle": "\uF64F", + "reddit": "\uF650", + "safe-fill": "\uF651", + "safe2-fill": "\uF652", + "safe2": "\uF653", + "sd-card-fill": "\uF654", + "sd-card": "\uF655", + "skype": "\uF656", + "slash-lg": "\uF657", + "translate": "\uF658", + "x-lg": "\uF659", + "safe": "\uF65A", + "apple": "\uF65B", + "microsoft": "\uF65D", + "windows": "\uF65E", + "behance": "\uF65C", + "dribbble": "\uF65F", + "line": "\uF660", + "medium": "\uF661", + "paypal": "\uF662", + "pinterest": "\uF663", + "signal": "\uF664", + "snapchat": "\uF665", + "spotify": "\uF666", + "stack-overflow": "\uF667", + "strava": "\uF668", + "wordpress": "\uF669", + "vimeo": "\uF66A", + "activity": "\uF66B", + "easel2-fill": "\uF66C", + "easel2": "\uF66D", + "easel3-fill": "\uF66E", + "easel3": "\uF66F", + "fan": "\uF670", + "fingerprint": "\uF671", + "graph-down-arrow": "\uF672", + "graph-up-arrow": "\uF673", + "hypnotize": "\uF674", + "magic": "\uF675", + "person-rolodex": "\uF676", + "person-video": "\uF677", + "person-video2": "\uF678", + "person-video3": "\uF679", + "person-workspace": "\uF67A", + "radioactive": "\uF67B", + "webcam-fill": "\uF67C", + "webcam": "\uF67D", + "yin-yang": "\uF67E", + "bandaid-fill": "\uF680", + "bandaid": "\uF681", + "bluetooth": "\uF682", + "body-text": "\uF683", + "boombox": "\uF684", + "boxes": "\uF685", + "dpad-fill": "\uF686", + "dpad": "\uF687", + "ear-fill": "\uF688", + "ear": "\uF689", + "envelope-check-fill": "\uF68B", + "envelope-check": "\uF68C", + "envelope-dash-fill": "\uF68E", + "envelope-dash": "\uF68F", + "envelope-exclamation-fill": "\uF691", + "envelope-exclamation": "\uF692", + "envelope-plus-fill": "\uF693", + "envelope-plus": "\uF694", + "envelope-slash-fill": "\uF696", + "envelope-slash": "\uF697", + "envelope-x-fill": "\uF699", + "envelope-x": "\uF69A", + "explicit-fill": "\uF69B", + "explicit": "\uF69C", + "git": "\uF69D", + "infinity": "\uF69E", + "list-columns-reverse": "\uF69F", + "list-columns": "\uF6A0", + "meta": "\uF6A1", + "nintendo-switch": "\uF6A4", + "pc-display-horizontal": "\uF6A5", + "pc-display": "\uF6A6", + "pc-horizontal": "\uF6A7", + "pc": "\uF6A8", + "playstation": "\uF6A9", + "plus-slash-minus": "\uF6AA", + "projector-fill": "\uF6AB", + "projector": "\uF6AC", + "qr-code-scan": "\uF6AD", + "qr-code": "\uF6AE", + "quora": "\uF6AF", + "quote": "\uF6B0", + "robot": "\uF6B1", + "send-check-fill": "\uF6B2", + "send-check": "\uF6B3", + "send-dash-fill": "\uF6B4", + "send-dash": "\uF6B5", + "send-exclamation-fill": "\uF6B7", + "send-exclamation": "\uF6B8", + "send-fill": "\uF6B9", + "send-plus-fill": "\uF6BA", + "send-plus": "\uF6BB", + "send-slash-fill": "\uF6BC", + "send-slash": "\uF6BD", + "send-x-fill": "\uF6BE", + "send-x": "\uF6BF", + "send": "\uF6C0", + "steam": "\uF6C1", + "terminal-dash": "\uF6C3", + "terminal-plus": "\uF6C4", + "terminal-split": "\uF6C5", + "ticket-detailed-fill": "\uF6C6", + "ticket-detailed": "\uF6C7", + "ticket-fill": "\uF6C8", + "ticket-perforated-fill": "\uF6C9", + "ticket-perforated": "\uF6CA", + "ticket": "\uF6CB", + "tiktok": "\uF6CC", + "window-dash": "\uF6CD", + "window-desktop": "\uF6CE", + "window-fullscreen": "\uF6CF", + "window-plus": "\uF6D0", + "window-split": "\uF6D1", + "window-stack": "\uF6D2", + "window-x": "\uF6D3", + "xbox": "\uF6D4", + "ethernet": "\uF6D5", + "hdmi-fill": "\uF6D6", + "hdmi": "\uF6D7", + "usb-c-fill": "\uF6D8", + "usb-c": "\uF6D9", + "usb-fill": "\uF6DA", + "usb-plug-fill": "\uF6DB", + "usb-plug": "\uF6DC", + "usb-symbol": "\uF6DD", + "usb": "\uF6DE", + "boombox-fill": "\uF6DF", + "displayport": "\uF6E1", + "gpu-card": "\uF6E2", + "memory": "\uF6E3", + "modem-fill": "\uF6E4", + "modem": "\uF6E5", + "motherboard-fill": "\uF6E6", + "motherboard": "\uF6E7", + "optical-audio-fill": "\uF6E8", + "optical-audio": "\uF6E9", + "pci-card": "\uF6EA", + "router-fill": "\uF6EB", + "router": "\uF6EC", + "thunderbolt-fill": "\uF6EF", + "thunderbolt": "\uF6F0", + "usb-drive-fill": "\uF6F1", + "usb-drive": "\uF6F2", + "usb-micro-fill": "\uF6F3", + "usb-micro": "\uF6F4", + "usb-mini-fill": "\uF6F5", + "usb-mini": "\uF6F6", + "cloud-haze2": "\uF6F7", + "device-hdd-fill": "\uF6F8", + "device-hdd": "\uF6F9", + "device-ssd-fill": "\uF6FA", + "device-ssd": "\uF6FB", + "displayport-fill": "\uF6FC", + "mortarboard-fill": "\uF6FD", + "mortarboard": "\uF6FE", + "terminal-x": "\uF6FF", + "arrow-through-heart-fill": "\uF700", + "arrow-through-heart": "\uF701", + "badge-sd-fill": "\uF702", + "badge-sd": "\uF703", + "bag-heart-fill": "\uF704", + "bag-heart": "\uF705", + "balloon-fill": "\uF706", + "balloon-heart-fill": "\uF707", + "balloon-heart": "\uF708", + "balloon": "\uF709", + "box2-fill": "\uF70A", + "box2-heart-fill": "\uF70B", + "box2-heart": "\uF70C", + "box2": "\uF70D", + "braces-asterisk": "\uF70E", + "calendar-heart-fill": "\uF70F", + "calendar-heart": "\uF710", + "calendar2-heart-fill": "\uF711", + "calendar2-heart": "\uF712", + "chat-heart-fill": "\uF713", + "chat-heart": "\uF714", + "chat-left-heart-fill": "\uF715", + "chat-left-heart": "\uF716", + "chat-right-heart-fill": "\uF717", + "chat-right-heart": "\uF718", + "chat-square-heart-fill": "\uF719", + "chat-square-heart": "\uF71A", + "clipboard-check-fill": "\uF71B", + "clipboard-data-fill": "\uF71C", + "clipboard-fill": "\uF71D", + "clipboard-heart-fill": "\uF71E", + "clipboard-heart": "\uF71F", + "clipboard-minus-fill": "\uF720", + "clipboard-plus-fill": "\uF721", + "clipboard-pulse": "\uF722", + "clipboard-x-fill": "\uF723", + "clipboard2-check-fill": "\uF724", + "clipboard2-check": "\uF725", + "clipboard2-data-fill": "\uF726", + "clipboard2-data": "\uF727", + "clipboard2-fill": "\uF728", + "clipboard2-heart-fill": "\uF729", + "clipboard2-heart": "\uF72A", + "clipboard2-minus-fill": "\uF72B", + "clipboard2-minus": "\uF72C", + "clipboard2-plus-fill": "\uF72D", + "clipboard2-plus": "\uF72E", + "clipboard2-pulse-fill": "\uF72F", + "clipboard2-pulse": "\uF730", + "clipboard2-x-fill": "\uF731", + "clipboard2-x": "\uF732", + "clipboard2": "\uF733", + "emoji-kiss-fill": "\uF734", + "emoji-kiss": "\uF735", + "envelope-heart-fill": "\uF736", + "envelope-heart": "\uF737", + "envelope-open-heart-fill": "\uF738", + "envelope-open-heart": "\uF739", + "envelope-paper-fill": "\uF73A", + "envelope-paper-heart-fill": "\uF73B", + "envelope-paper-heart": "\uF73C", + "envelope-paper": "\uF73D", + "filetype-aac": "\uF73E", + "filetype-ai": "\uF73F", + "filetype-bmp": "\uF740", + "filetype-cs": "\uF741", + "filetype-css": "\uF742", + "filetype-csv": "\uF743", + "filetype-doc": "\uF744", + "filetype-docx": "\uF745", + "filetype-exe": "\uF746", + "filetype-gif": "\uF747", + "filetype-heic": "\uF748", + "filetype-html": "\uF749", + "filetype-java": "\uF74A", + "filetype-jpg": "\uF74B", + "filetype-js": "\uF74C", + "filetype-jsx": "\uF74D", + "filetype-key": "\uF74E", + "filetype-m4p": "\uF74F", + "filetype-md": "\uF750", + "filetype-mdx": "\uF751", + "filetype-mov": "\uF752", + "filetype-mp3": "\uF753", + "filetype-mp4": "\uF754", + "filetype-otf": "\uF755", + "filetype-pdf": "\uF756", + "filetype-php": "\uF757", + "filetype-png": "\uF758", + "filetype-ppt": "\uF75A", + "filetype-psd": "\uF75B", + "filetype-py": "\uF75C", + "filetype-raw": "\uF75D", + "filetype-rb": "\uF75E", + "filetype-sass": "\uF75F", + "filetype-scss": "\uF760", + "filetype-sh": "\uF761", + "filetype-svg": "\uF762", + "filetype-tiff": "\uF763", + "filetype-tsx": "\uF764", + "filetype-ttf": "\uF765", + "filetype-txt": "\uF766", + "filetype-wav": "\uF767", + "filetype-woff": "\uF768", + "filetype-xls": "\uF76A", + "filetype-xml": "\uF76B", + "filetype-yml": "\uF76C", + "heart-arrow": "\uF76D", + "heart-pulse-fill": "\uF76E", + "heart-pulse": "\uF76F", + "heartbreak-fill": "\uF770", + "heartbreak": "\uF771", + "hearts": "\uF772", + "hospital-fill": "\uF773", + "hospital": "\uF774", + "house-heart-fill": "\uF775", + "house-heart": "\uF776", + "incognito": "\uF777", + "magnet-fill": "\uF778", + "magnet": "\uF779", + "person-heart": "\uF77A", + "person-hearts": "\uF77B", + "phone-flip": "\uF77C", + "plugin": "\uF77D", + "postage-fill": "\uF77E", + "postage-heart-fill": "\uF77F", + "postage-heart": "\uF780", + "postage": "\uF781", + "postcard-fill": "\uF782", + "postcard-heart-fill": "\uF783", + "postcard-heart": "\uF784", + "postcard": "\uF785", + "search-heart-fill": "\uF786", + "search-heart": "\uF787", + "sliders2-vertical": "\uF788", + "sliders2": "\uF789", + "trash3-fill": "\uF78A", + "trash3": "\uF78B", + "valentine": "\uF78C", + "valentine2": "\uF78D", + "wrench-adjustable-circle-fill": "\uF78E", + "wrench-adjustable-circle": "\uF78F", + "wrench-adjustable": "\uF790", + "filetype-json": "\uF791", + "filetype-pptx": "\uF792", + "filetype-xlsx": "\uF793", + "1-circle-fill": "\uF796", + "1-circle": "\uF797", + "1-square-fill": "\uF798", + "1-square": "\uF799", + "2-circle-fill": "\uF79C", + "2-circle": "\uF79D", + "2-square-fill": "\uF79E", + "2-square": "\uF79F", + "3-circle-fill": "\uF7A2", + "3-circle": "\uF7A3", + "3-square-fill": "\uF7A4", + "3-square": "\uF7A5", + "4-circle-fill": "\uF7A8", + "4-circle": "\uF7A9", + "4-square-fill": "\uF7AA", + "4-square": "\uF7AB", + "5-circle-fill": "\uF7AE", + "5-circle": "\uF7AF", + "5-square-fill": "\uF7B0", + "5-square": "\uF7B1", + "6-circle-fill": "\uF7B4", + "6-circle": "\uF7B5", + "6-square-fill": "\uF7B6", + "6-square": "\uF7B7", + "7-circle-fill": "\uF7BA", + "7-circle": "\uF7BB", + "7-square-fill": "\uF7BC", + "7-square": "\uF7BD", + "8-circle-fill": "\uF7C0", + "8-circle": "\uF7C1", + "8-square-fill": "\uF7C2", + "8-square": "\uF7C3", + "9-circle-fill": "\uF7C6", + "9-circle": "\uF7C7", + "9-square-fill": "\uF7C8", + "9-square": "\uF7C9", + "airplane-engines-fill": "\uF7CA", + "airplane-engines": "\uF7CB", + "airplane-fill": "\uF7CC", + "airplane": "\uF7CD", + "alexa": "\uF7CE", + "alipay": "\uF7CF", + "android": "\uF7D0", + "android2": "\uF7D1", + "box-fill": "\uF7D2", + "box-seam-fill": "\uF7D3", + "browser-chrome": "\uF7D4", + "browser-edge": "\uF7D5", + "browser-firefox": "\uF7D6", + "browser-safari": "\uF7D7", + "c-circle-fill": "\uF7DA", + "c-circle": "\uF7DB", + "c-square-fill": "\uF7DC", + "c-square": "\uF7DD", + "capsule-pill": "\uF7DE", + "capsule": "\uF7DF", + "car-front-fill": "\uF7E0", + "car-front": "\uF7E1", + "cassette-fill": "\uF7E2", + "cassette": "\uF7E3", + "cc-circle-fill": "\uF7E6", + "cc-circle": "\uF7E7", + "cc-square-fill": "\uF7E8", + "cc-square": "\uF7E9", + "cup-hot-fill": "\uF7EA", + "cup-hot": "\uF7EB", + "currency-rupee": "\uF7EC", + "dropbox": "\uF7ED", + "escape": "\uF7EE", + "fast-forward-btn-fill": "\uF7EF", + "fast-forward-btn": "\uF7F0", + "fast-forward-circle-fill": "\uF7F1", + "fast-forward-circle": "\uF7F2", + "fast-forward-fill": "\uF7F3", + "fast-forward": "\uF7F4", + "filetype-sql": "\uF7F5", + "fire": "\uF7F6", + "google-play": "\uF7F7", + "h-circle-fill": "\uF7FA", + "h-circle": "\uF7FB", + "h-square-fill": "\uF7FC", + "h-square": "\uF7FD", + "indent": "\uF7FE", + "lungs-fill": "\uF7FF", + "lungs": "\uF800", + "microsoft-teams": "\uF801", + "p-circle-fill": "\uF804", + "p-circle": "\uF805", + "p-square-fill": "\uF806", + "p-square": "\uF807", + "pass-fill": "\uF808", + "pass": "\uF809", + "prescription": "\uF80A", + "prescription2": "\uF80B", + "r-circle-fill": "\uF80E", + "r-circle": "\uF80F", + "r-square-fill": "\uF810", + "r-square": "\uF811", + "repeat-1": "\uF812", + "repeat": "\uF813", + "rewind-btn-fill": "\uF814", + "rewind-btn": "\uF815", + "rewind-circle-fill": "\uF816", + "rewind-circle": "\uF817", + "rewind-fill": "\uF818", + "rewind": "\uF819", + "train-freight-front-fill": "\uF81A", + "train-freight-front": "\uF81B", + "train-front-fill": "\uF81C", + "train-front": "\uF81D", + "train-lightrail-front-fill": "\uF81E", + "train-lightrail-front": "\uF81F", + "truck-front-fill": "\uF820", + "truck-front": "\uF821", + "ubuntu": "\uF822", + "unindent": "\uF823", + "unity": "\uF824", + "universal-access-circle": "\uF825", + "universal-access": "\uF826", + "virus": "\uF827", + "virus2": "\uF828", + "wechat": "\uF829", + "yelp": "\uF82A", + "sign-stop-fill": "\uF82B", + "sign-stop-lights-fill": "\uF82C", + "sign-stop-lights": "\uF82D", + "sign-stop": "\uF82E", + "sign-turn-left-fill": "\uF82F", + "sign-turn-left": "\uF830", + "sign-turn-right-fill": "\uF831", + "sign-turn-right": "\uF832", + "sign-turn-slight-left-fill": "\uF833", + "sign-turn-slight-left": "\uF834", + "sign-turn-slight-right-fill": "\uF835", + "sign-turn-slight-right": "\uF836", + "sign-yield-fill": "\uF837", + "sign-yield": "\uF838", + "ev-station-fill": "\uF839", + "ev-station": "\uF83A", + "fuel-pump-diesel-fill": "\uF83B", + "fuel-pump-diesel": "\uF83C", + "fuel-pump-fill": "\uF83D", + "fuel-pump": "\uF83E", + "0-circle-fill": "\uF83F", + "0-circle": "\uF840", + "0-square-fill": "\uF841", + "0-square": "\uF842", + "rocket-fill": "\uF843", + "rocket-takeoff-fill": "\uF844", + "rocket-takeoff": "\uF845", + "rocket": "\uF846", + "stripe": "\uF847", + "subscript": "\uF848", + "superscript": "\uF849", + "trello": "\uF84A", + "envelope-at-fill": "\uF84B", + "envelope-at": "\uF84C", + "regex": "\uF84D", + "text-wrap": "\uF84E", + "sign-dead-end-fill": "\uF84F", + "sign-dead-end": "\uF850", + "sign-do-not-enter-fill": "\uF851", + "sign-do-not-enter": "\uF852", + "sign-intersection-fill": "\uF853", + "sign-intersection-side-fill": "\uF854", + "sign-intersection-side": "\uF855", + "sign-intersection-t-fill": "\uF856", + "sign-intersection-t": "\uF857", + "sign-intersection-y-fill": "\uF858", + "sign-intersection-y": "\uF859", + "sign-intersection": "\uF85A", + "sign-merge-left-fill": "\uF85B", + "sign-merge-left": "\uF85C", + "sign-merge-right-fill": "\uF85D", + "sign-merge-right": "\uF85E", + "sign-no-left-turn-fill": "\uF85F", + "sign-no-left-turn": "\uF860", + "sign-no-parking-fill": "\uF861", + "sign-no-parking": "\uF862", + "sign-no-right-turn-fill": "\uF863", + "sign-no-right-turn": "\uF864", + "sign-railroad-fill": "\uF865", + "sign-railroad": "\uF866", + "building-add": "\uF867", + "building-check": "\uF868", + "building-dash": "\uF869", + "building-down": "\uF86A", + "building-exclamation": "\uF86B", + "building-fill-add": "\uF86C", + "building-fill-check": "\uF86D", + "building-fill-dash": "\uF86E", + "building-fill-down": "\uF86F", + "building-fill-exclamation": "\uF870", + "building-fill-gear": "\uF871", + "building-fill-lock": "\uF872", + "building-fill-slash": "\uF873", + "building-fill-up": "\uF874", + "building-fill-x": "\uF875", + "building-fill": "\uF876", + "building-gear": "\uF877", + "building-lock": "\uF878", + "building-slash": "\uF879", + "building-up": "\uF87A", + "building-x": "\uF87B", + "buildings-fill": "\uF87C", + "buildings": "\uF87D", + "bus-front-fill": "\uF87E", + "bus-front": "\uF87F", + "ev-front-fill": "\uF880", + "ev-front": "\uF881", + "globe-americas": "\uF882", + "globe-asia-australia": "\uF883", + "globe-central-south-asia": "\uF884", + "globe-europe-africa": "\uF885", + "house-add-fill": "\uF886", + "house-add": "\uF887", + "house-check-fill": "\uF888", + "house-check": "\uF889", + "house-dash-fill": "\uF88A", + "house-dash": "\uF88B", + "house-down-fill": "\uF88C", + "house-down": "\uF88D", + "house-exclamation-fill": "\uF88E", + "house-exclamation": "\uF88F", + "house-gear-fill": "\uF890", + "house-gear": "\uF891", + "house-lock-fill": "\uF892", + "house-lock": "\uF893", + "house-slash-fill": "\uF894", + "house-slash": "\uF895", + "house-up-fill": "\uF896", + "house-up": "\uF897", + "house-x-fill": "\uF898", + "house-x": "\uF899", + "person-add": "\uF89A", + "person-down": "\uF89B", + "person-exclamation": "\uF89C", + "person-fill-add": "\uF89D", + "person-fill-check": "\uF89E", + "person-fill-dash": "\uF89F", + "person-fill-down": "\uF8A0", + "person-fill-exclamation": "\uF8A1", + "person-fill-gear": "\uF8A2", + "person-fill-lock": "\uF8A3", + "person-fill-slash": "\uF8A4", + "person-fill-up": "\uF8A5", + "person-fill-x": "\uF8A6", + "person-gear": "\uF8A7", + "person-lock": "\uF8A8", + "person-slash": "\uF8A9", + "person-up": "\uF8AA", + "scooter": "\uF8AB", + "taxi-front-fill": "\uF8AC", + "taxi-front": "\uF8AD", + "amd": "\uF8AE", + "database-add": "\uF8AF", + "database-check": "\uF8B0", + "database-dash": "\uF8B1", + "database-down": "\uF8B2", + "database-exclamation": "\uF8B3", + "database-fill-add": "\uF8B4", + "database-fill-check": "\uF8B5", + "database-fill-dash": "\uF8B6", + "database-fill-down": "\uF8B7", + "database-fill-exclamation": "\uF8B8", + "database-fill-gear": "\uF8B9", + "database-fill-lock": "\uF8BA", + "database-fill-slash": "\uF8BB", + "database-fill-up": "\uF8BC", + "database-fill-x": "\uF8BD", + "database-fill": "\uF8BE", + "database-gear": "\uF8BF", + "database-lock": "\uF8C0", + "database-slash": "\uF8C1", + "database-up": "\uF8C2", + "database-x": "\uF8C3", + "database": "\uF8C4", + "houses-fill": "\uF8C5", + "houses": "\uF8C6", + "nvidia": "\uF8C7", + "person-vcard-fill": "\uF8C8", + "person-vcard": "\uF8C9", + "sina-weibo": "\uF8CA", + "tencent-qq": "\uF8CB", + "wikipedia": "\uF8CC", + "alphabet-uppercase": "\uF2A5", + "alphabet": "\uF68A", + "amazon": "\uF695", + "arrows-collapse-vertical": "\uF698", + "arrows-expand-vertical": "\uF69D", + "arrows-vertical": "\uF6A0", + "arrows": "\uF6A2", + "ban-fill": "\uF6A3", + "ban": "\uF6B6", + "bing": "\uF6C2", + "cake": "\uF6E0", + "cake2": "\uF6ED", + "cookie": "\uF6EE", + "copy": "\uF759", + "crosshair": "\uF769", + "crosshair2": "\uF794", + "emoji-astonished-fill": "\uF795", + "emoji-astonished": "\uF79A", + "emoji-grimace-fill": "\uF79B", + "emoji-grimace": "\uF7A0", + "emoji-grin-fill": "\uF7A1", + "emoji-grin": "\uF7A6", + "emoji-surprise-fill": "\uF7A7", + "emoji-surprise": "\uF7AC", + "emoji-tear-fill": "\uF7AD", + "emoji-tear": "\uF7B2", + "envelope-arrow-down-fill": "\uF7B3", + "envelope-arrow-down": "\uF7B8", + "envelope-arrow-up-fill": "\uF7B9", + "envelope-arrow-up": "\uF7BE", + "feather": "\uF7BF", + "feather2": "\uF7C4", + "floppy-fill": "\uF7C5", + "floppy": "\uF7D8", + "floppy2-fill": "\uF7D9", + "floppy2": "\uF7E4", + "gitlab": "\uF7E5", + "highlighter": "\uF7F8", + "marker-tip": "\uF802", + "nvme-fill": "\uF803", + "nvme": "\uF80C", + "opencollective": "\uF80D", + "pci-card-network": "\uF8CD", + "pci-card-sound": "\uF8CE", + "radar": "\uF8CF", + "send-arrow-down-fill": "\uF8D0", + "send-arrow-down": "\uF8D1", + "send-arrow-up-fill": "\uF8D2", + "send-arrow-up": "\uF8D3", + "sim-slash-fill": "\uF8D4", + "sim-slash": "\uF8D5", + "sourceforge": "\uF8D6", + "substack": "\uF8D7", + "threads-fill": "\uF8D8", + "threads": "\uF8D9", + "transparency": "\uF8DA", + "twitter-x": "\uF8DB", + "type-h4": "\uF8DC", + "type-h5": "\uF8DD", + "type-h6": "\uF8DE", + "backpack-fill": "\uF8DF", + "backpack": "\uF8E0", + "backpack2-fill": "\uF8E1", + "backpack2": "\uF8E2", + "backpack3-fill": "\uF8E3", + "backpack3": "\uF8E4", + "backpack4-fill": "\uF8E5", + "backpack4": "\uF8E6", + "brilliance": "\uF8E7", + "cake-fill": "\uF8E8", + "cake2-fill": "\uF8E9", + "duffle-fill": "\uF8EA", + "duffle": "\uF8EB", + "exposure": "\uF8EC", + "gender-neuter": "\uF8ED", + "highlights": "\uF8EE", + "luggage-fill": "\uF8EF", + "luggage": "\uF8F0", + "mailbox-flag": "\uF8F1", + "mailbox2-flag": "\uF8F2", + "noise-reduction": "\uF8F3", + "passport-fill": "\uF8F4", + "passport": "\uF8F5", + "person-arms-up": "\uF8F6", + "person-raised-hand": "\uF8F7", + "person-standing-dress": "\uF8F8", + "person-standing": "\uF8F9", + "person-walking": "\uF8FA", + "person-wheelchair": "\uF8FB", + "shadows": "\uF8FC", + "suitcase-fill": "\uF8FD", + "suitcase-lg-fill": "\uF8FE", + "suitcase-lg": "\uF8FF", + "suitcase": "\uF900", + "suitcase2-fill": "\uF901", + "suitcase2": "\uF902", + "vignette": "\uF903", + "bluesky": "\uF7F9", + "tux": "\uF904", + "beaker-fill": "\uF905", + "beaker": "\uF906", + "flask-fill": "\uF907", + "flask-florence-fill": "\uF908", + "flask-florence": "\uF909", + "flask": "\uF90A", + "leaf-fill": "\uF90B", + "leaf": "\uF90C", + "measuring-cup-fill": "\uF90D", + "measuring-cup": "\uF90E", + "unlock2-fill": "\uF90F", + "unlock2": "\uF910", + "battery-low": "\uF911", + "anthropic": "\uF912", + "apple-music": "\uF913", + "claude": "\uF914", + "openai": "\uF915", + "perplexity": "\uF916", + "css": "\uF917", + "javascript": "\uF918", + "typescript": "\uF919", + "fork-knife": "\uF91A", + "globe-americas-fill": "\uF91B", + "globe-asia-australia-fill": "\uF91C", + "globe-central-south-asia-fill": "\uF91D", + "globe-europe-africa-fill": "\uF91E" + } +} diff --git a/Modules/ArchUpdaterPanel/ArchUpdaterPanel.qml b/Modules/ArchUpdaterPanel/ArchUpdaterPanel.qml index c282e11..f8a65ce 100644 --- a/Modules/ArchUpdaterPanel/ArchUpdaterPanel.qml +++ b/Modules/ArchUpdaterPanel/ArchUpdaterPanel.qml @@ -28,7 +28,7 @@ NPanel { spacing: Style.marginM * scaling NIcon { - text: "system_update_alt" + icon: "box" font.pointSize: Style.fontSizeXXL * scaling color: Color.mPrimary } @@ -44,7 +44,7 @@ NPanel { // Reset button (only show if update failed) NIconButton { visible: ArchUpdaterService.updateFailed - icon: "refresh" + icon: "arrow-repeat" tooltipText: "Reset update state" sizeRatio: 0.8 colorBg: Color.mError @@ -55,7 +55,7 @@ NPanel { } NIconButton { - icon: "close" + icon: "x-lg" tooltipText: "Close" sizeRatio: 0.8 onClicked: root.close() @@ -103,7 +103,7 @@ NPanel { } // Spacer NIcon { - text: "hourglass_empty" + icon: "hourglass" font.pointSize: Style.fontSizeXXXL * scaling color: Color.mPrimary Layout.alignment: Qt.AlignHCenter @@ -143,7 +143,7 @@ NPanel { spacing: Style.marginM * scaling NIcon { - text: "terminal" + icon: "terminal" font.pointSize: Style.fontSizeXXXL * scaling color: Color.mError Layout.alignment: Qt.AlignHCenter @@ -181,7 +181,7 @@ NPanel { spacing: Style.marginM * scaling NIcon { - text: "package" + icon: "box" font.pointSize: Style.fontSizeXXXL * scaling color: Color.mError Layout.alignment: Qt.AlignHCenter @@ -219,7 +219,7 @@ NPanel { spacing: Style.marginM * scaling NIcon { - text: "error" + icon: "exclamation-triangle" font.pointSize: Style.fontSizeXXXL * scaling color: Color.mError Layout.alignment: Qt.AlignHCenter @@ -245,7 +245,7 @@ NPanel { // Prominent refresh button NIconButton { - icon: "refresh" + icon: "arrow-repeat" tooltipText: "Try checking again" sizeRatio: 1.2 colorBg: Color.mPrimary @@ -270,7 +270,7 @@ NPanel { spacing: Style.marginM * scaling NIcon { - text: "error_outline" + icon: "exclamation-triangle" font.pointSize: Style.fontSizeXXXL * scaling color: Color.mError Layout.alignment: Qt.AlignHCenter @@ -295,7 +295,7 @@ NPanel { // Prominent refresh button NIconButton { - icon: "refresh" + icon: "arrow-repeat" tooltipText: "Refresh and try again" sizeRatio: 1.2 colorBg: Color.mPrimary @@ -323,7 +323,7 @@ NPanel { spacing: Style.marginM * scaling NIcon { - text: "check_circle" + icon: "check-lg" font.pointSize: Style.fontSizeXXXL * scaling color: Color.mPrimary Layout.alignment: Qt.AlignHCenter @@ -483,7 +483,7 @@ NPanel { spacing: Style.marginL * scaling NIconButton { - icon: "refresh" + icon: "arrow-repeat" tooltipText: ArchUpdaterService.aurBusy ? "Checking for updates..." : (!ArchUpdaterService.canPoll ? "Refresh available soon" : "Refresh package lists") onClicked: { ArchUpdaterService.forceRefresh() @@ -495,7 +495,7 @@ NPanel { } NIconButton { - icon: "system_update_alt" + icon: "box-fill" tooltipText: "Update all packages" enabled: ArchUpdaterService.totalUpdates > 0 onClicked: { @@ -508,7 +508,7 @@ NPanel { } NIconButton { - icon: "check_box" + icon: "box" tooltipText: "Update selected packages" enabled: ArchUpdaterService.selectedPackagesCount > 0 onClicked: { diff --git a/Modules/Background/Background.qml b/Modules/Background/Background.qml index 56d184a..61d4fa4 100644 --- a/Modules/Background/Background.qml +++ b/Modules/Background/Background.qml @@ -139,7 +139,7 @@ Variants { property real screenWidth: width property real screenHeight: height - fragmentShader: Qt.resolvedUrl("../../Shaders/qsb/wp_fade.frag.qsb") + fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/wp_fade.frag.qsb") } // Wipe transition shader @@ -164,7 +164,7 @@ Variants { property real screenWidth: width property real screenHeight: height - fragmentShader: Qt.resolvedUrl("../../Shaders/qsb/wp_wipe.frag.qsb") + fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/wp_wipe.frag.qsb") } // Disc reveal transition shader @@ -191,7 +191,7 @@ Variants { property real screenWidth: width property real screenHeight: height - fragmentShader: Qt.resolvedUrl("../../Shaders/qsb/wp_disc.frag.qsb") + fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/wp_disc.frag.qsb") } // Diagonal stripes transition shader @@ -218,7 +218,7 @@ Variants { property real screenWidth: width property real screenHeight: height - fragmentShader: Qt.resolvedUrl("../../Shaders/qsb/wp_stripes.frag.qsb") + fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/wp_stripes.frag.qsb") } // Animation for the transition progress diff --git a/Modules/Bar/Widgets/ActiveWindow.qml b/Modules/Bar/Widgets/ActiveWindow.qml index 8fb5961..473bdfe 100644 --- a/Modules/Bar/Widgets/ActiveWindow.qml +++ b/Modules/Bar/Widgets/ActiveWindow.qml @@ -33,28 +33,34 @@ RowLayout { readonly property bool showIcon: (widgetSettings.showIcon !== undefined) ? widgetSettings.showIcon : widgetMetadata.showIcon - readonly property real minWidth: 160 - readonly property real maxWidth: 400 - Layout.alignment: Qt.AlignVCenter - spacing: Style.marginS * scaling - visible: getTitle() !== "" + // 6% of total width + readonly property real minWidth: Math.max(1, screen.width * 0.06) + readonly property real maxWidth: minWidth * 2 function getTitle() { return CompositorService.focusedWindowTitle !== "(No active window)" ? CompositorService.focusedWindowTitle : "" } + Layout.alignment: Qt.AlignVCenter + spacing: Style.marginS * scaling + visible: getTitle() !== "" + function getAppIcon() { // Try CompositorService first const focusedWindow = CompositorService.getFocusedWindow() if (focusedWindow && focusedWindow.appId) { - return Icons.iconForAppId(focusedWindow.appId.toLowerCase()) + const idValue = focusedWindow.appId + const normalizedId = (typeof idValue === 'string') ? idValue : String(idValue) + return Icons.iconForAppId(normalizedId.toLowerCase()) } // Fallback to ToplevelManager if (ToplevelManager && ToplevelManager.activeToplevel) { const activeToplevel = ToplevelManager.activeToplevel if (activeToplevel.appId) { - return Icons.iconForAppId(activeToplevel.appId.toLowerCase()) + const idValue2 = activeToplevel.appId + const normalizedId2 = (typeof idValue2 === 'string') ? idValue2 : String(idValue2) + return Icons.iconForAppId(normalizedId2.toLowerCase()) } } @@ -123,7 +129,7 @@ RowLayout { font.weight: Style.fontWeightMedium elide: mouseArea.containsMouse ? Text.ElideNone : Text.ElideRight verticalAlignment: Text.AlignVCenter - color: Color.mSecondary + color: Color.mPrimary clip: true Behavior on Layout.preferredWidth { diff --git a/Modules/Bar/Widgets/ArchUpdater.qml b/Modules/Bar/Widgets/ArchUpdater.qml index 3fb7c85..7a1d888 100644 --- a/Modules/Bar/Widgets/ArchUpdater.qml +++ b/Modules/Bar/Widgets/ArchUpdater.qml @@ -29,15 +29,15 @@ NIconButton { return "terminal" } if (!ArchUpdaterService.aurHelperAvailable) { - return "package" + return "box" } if (ArchUpdaterService.aurBusy) { - return "sync" + return "arrow-repeat" } if (ArchUpdaterService.totalUpdates > 0) { - return "system_update_alt" + return "box-fill" } - return "task_alt" + return "box" } // Tooltip with repo vs AUR breakdown and sample lists diff --git a/Modules/Bar/Widgets/Battery.qml b/Modules/Bar/Widgets/Battery.qml index 9b8aef5..bb112e0 100644 --- a/Modules/Bar/Widgets/Battery.qml +++ b/Modules/Bar/Widgets/Battery.qml @@ -39,7 +39,7 @@ Item { // Test mode readonly property bool testMode: false readonly property int testPercent: 50 - readonly property bool testCharging: true + readonly property bool testCharging: false // Main properties readonly property var battery: UPower.displayDevice @@ -57,9 +57,7 @@ Item { // Only notify once we are a below threshold if (!charging && !root.hasNotifiedLowBattery && percent <= warningThreshold) { root.hasNotifiedLowBattery = true - // Maybe go with toast ? - Quickshell.execDetached( - ["notify-send", "-u", "critical", "-i", "battery-caution", "Low Battery", `Battery is at ${p}%. Please connect charger.`]) + ToastService.showWarning("Low Battery", `Battery is at ${Math.round(percent)}%. Please connect the charger.`) } else if (root.hasNotifiedLowBattery && (charging || percent > warningThreshold + 5)) { // Reset when charging starts or when battery recovers 5% above threshold root.hasNotifiedLowBattery = false @@ -87,11 +85,7 @@ Item { rightOpen: BarWidgetRegistry.getNPillDirection(root) icon: testMode ? BatteryService.getIcon(testPercent, testCharging, true) : BatteryService.getIcon(percent, charging, isReady) - iconRotation: -90 - text: ((isReady && battery.isLaptopBattery) || testMode) ? Math.round(percent) + "%" : "-" - textColor: charging ? Color.mPrimary : Color.mOnSurface - iconCircleColor: Color.mPrimary - collapsedIconColor: Color.mOnSurface + text: (isReady || testMode) ? Math.round(percent) + "%" : "-" autoHide: false forceOpen: isReady && (testMode || battery.isLaptopBattery) && alwaysShowPercentage disableOpen: (!isReady || (!testMode && !battery.isLaptopBattery)) diff --git a/Modules/Bar/Widgets/Brightness.qml b/Modules/Bar/Widgets/Brightness.qml index 30948c3..477cf19 100644 --- a/Modules/Bar/Widgets/Brightness.qml +++ b/Modules/Bar/Widgets/Brightness.qml @@ -46,8 +46,7 @@ Item { function getIcon() { var monitor = getMonitor() var brightness = monitor ? monitor.brightness : 0 - return brightness <= 0 ? "brightness_1" : brightness < 0.33 ? "brightness_low" : brightness - < 0.66 ? "brightness_medium" : "brightness_high" + return brightness <= 0.5 ? "brightness-low" : "brightness-high" } // Connection used to open the pill when brightness changes @@ -80,8 +79,6 @@ Item { rightOpen: BarWidgetRegistry.getNPillDirection(root) icon: getIcon() - iconCircleColor: Color.mPrimary - collapsedIconColor: Color.mOnSurface autoHide: false // Important to be false so we can hover as long as we want text: { var monitor = getMonitor() diff --git a/Modules/Bar/Widgets/Clock.qml b/Modules/Bar/Widgets/Clock.qml index 3b472d9..71526e2 100644 --- a/Modules/Bar/Widgets/Clock.qml +++ b/Modules/Bar/Widgets/Clock.qml @@ -60,6 +60,7 @@ Rectangle { anchors.centerIn: parent font.pointSize: Style.fontSizeS * scaling font.weight: Style.fontWeightBold + color: Color.mPrimary } NTooltip { diff --git a/Modules/Bar/Widgets/DarkModeToggle.qml b/Modules/Bar/Widgets/DarkModeToggle.qml index ae7d933..4c3ec1e 100644 --- a/Modules/Bar/Widgets/DarkModeToggle.qml +++ b/Modules/Bar/Widgets/DarkModeToggle.qml @@ -9,12 +9,12 @@ NIconButton { property ShellScreen screen property real scaling: 1.0 - icon: "contrast" + icon: "transparency" tooltipText: "Toggle light/dark mode" sizeRatio: 0.8 - colorBg: Color.mSurfaceVariant - colorFg: Color.mOnSurface + colorBg: Settings.data.colorSchemes.darkMode ? Color.mSurfaceVariant : Color.mPrimary + colorFg: Settings.data.colorSchemes.darkMode ? Color.mOnSurface : Color.mOnPrimary colorBorder: Color.transparent colorBorderHover: Color.transparent diff --git a/Modules/Bar/Widgets/KeepAwake.qml b/Modules/Bar/Widgets/KeepAwake.qml index 31c6525..ebd880c 100644 --- a/Modules/Bar/Widgets/KeepAwake.qml +++ b/Modules/Bar/Widgets/KeepAwake.qml @@ -13,10 +13,10 @@ NIconButton { sizeRatio: 0.8 - icon: "coffee" + icon: "cup" tooltipText: IdleInhibitorService.isInhibited ? "Disable keep awake" : "Enable keep awake" - colorBg: Color.mSurfaceVariant - colorFg: IdleInhibitorService.isInhibited ? Color.mPrimary : Color.mOnSurface + colorBg: IdleInhibitorService.isInhibited ? Color.mPrimary : Color.mSurfaceVariant + colorFg: IdleInhibitorService.isInhibited ? Color.mOnPrimary : Color.mOnSurface colorBorder: Color.transparent onClicked: { IdleInhibitorService.manualToggle() diff --git a/Modules/Bar/Widgets/KeyboardLayout.qml b/Modules/Bar/Widgets/KeyboardLayout.qml index 7de3a6d..a09d1e6 100644 --- a/Modules/Bar/Widgets/KeyboardLayout.qml +++ b/Modules/Bar/Widgets/KeyboardLayout.qml @@ -24,9 +24,7 @@ Item { anchors.verticalCenter: parent.verticalCenter rightOpen: BarWidgetRegistry.getNPillDirection(root) - icon: "keyboard_alt" - iconCircleColor: Color.mPrimary - collapsedIconColor: Color.mOnSurface + icon: "keyboard" autoHide: false // Important to be false so we can hover as long as we want text: currentLayout tooltipText: "Keyboard layout: " + currentLayout diff --git a/Modules/Bar/Widgets/MediaMini.qml b/Modules/Bar/Widgets/MediaMini.qml index 141698a..aa00cb9 100644 --- a/Modules/Bar/Widgets/MediaMini.qml +++ b/Modules/Bar/Widgets/MediaMini.qml @@ -38,8 +38,9 @@ RowLayout { readonly property string visualizerType: (widgetSettings.visualizerType !== undefined && widgetSettings.visualizerType !== "") ? widgetSettings.visualizerType : widgetMetadata.visualizerType - readonly property real minWidth: 160 - readonly property real maxWidth: 400 + // 6% of total width + readonly property real minWidth: Math.max(1, screen.width * 0.06) + readonly property real maxWidth: minWidth * 2 function getTitle() { return MediaService.trackTitle + (MediaService.trackArtist !== "" ? ` - ${MediaService.trackArtist}` : "") @@ -134,7 +135,7 @@ RowLayout { NIcon { id: windowIcon - text: MediaService.isPlaying ? "pause" : "play_arrow" + text: MediaService.isPlaying ? "pause" : "play" font.pointSize: Style.fontSizeL * scaling verticalAlignment: Text.AlignVCenter Layout.alignment: Qt.AlignVCenter @@ -154,7 +155,8 @@ RowLayout { id: trackArt anchors.fill: parent imagePath: MediaService.trackArtUrl - fallbackIcon: MediaService.isPlaying ? "pause" : "play_arrow" + fallbackIcon: MediaService.isPlaying ? "pause" : "play" + fallbackIconSize: 10 * scaling borderWidth: 0 border.color: Color.transparent } @@ -178,7 +180,7 @@ RowLayout { font.weight: Style.fontWeightMedium elide: Text.ElideRight verticalAlignment: Text.AlignVCenter - color: Color.mTertiary + color: Color.mSecondary Behavior on Layout.preferredWidth { NumberAnimation { diff --git a/Modules/Bar/Widgets/Microphone.qml b/Modules/Bar/Widgets/Microphone.qml index 15f4437..3851785 100644 --- a/Modules/Bar/Widgets/Microphone.qml +++ b/Modules/Bar/Widgets/Microphone.qml @@ -43,9 +43,9 @@ Item { function getIcon() { if (AudioService.inputMuted) { - return "mic_off" + return "mic-mute" } - return AudioService.inputVolume <= Number.EPSILON ? "mic_off" : (AudioService.inputVolume < 0.33 ? "mic" : "mic") + return AudioService.inputVolume <= Number.EPSILON ? "mic-mute" : (AudioService.inputVolume < 0.33 ? "mic" : "mic") } // Connection used to open the pill when input volume changes @@ -92,8 +92,6 @@ Item { rightOpen: BarWidgetRegistry.getNPillDirection(root) icon: getIcon() - iconCircleColor: Color.mPrimary - collapsedIconColor: Color.mOnSurface autoHide: false // Important to be false so we can hover as long as we want text: Math.floor(AudioService.inputVolume * 100) + "%" forceOpen: alwaysShowPercentage diff --git a/Modules/Bar/Widgets/NightLight.qml b/Modules/Bar/Widgets/NightLight.qml index c9f302e..3712f76 100644 --- a/Modules/Bar/Widgets/NightLight.qml +++ b/Modules/Bar/Widgets/NightLight.qml @@ -15,12 +15,12 @@ NIconButton { property real scaling: 1.0 sizeRatio: 0.8 - colorBg: Color.mSurfaceVariant - colorFg: Color.mOnSurface + colorBg: Settings.data.nightLight.enabled ? Color.mPrimary : Color.mSurfaceVariant + colorFg: Settings.data.nightLight.enabled ? Color.mOnPrimary : Color.mOnSurface colorBorder: Color.transparent colorBorderHover: Color.transparent - icon: Settings.data.nightLight.enabled ? "bedtime" : "bedtime_off" + icon: "moon-stars" tooltipText: `Night light: ${Settings.data.nightLight.enabled ? "enabled." : "disabled."}\nLeft click to toggle.\nRight click to access settings.` onClicked: Settings.data.nightLight.enabled = !Settings.data.nightLight.enabled diff --git a/Modules/Bar/Widgets/NotificationHistory.qml b/Modules/Bar/Widgets/NotificationHistory.qml index 31657f1..39b8c8f 100644 --- a/Modules/Bar/Widgets/NotificationHistory.qml +++ b/Modules/Bar/Widgets/NotificationHistory.qml @@ -53,10 +53,10 @@ NIconButton { } sizeRatio: 0.8 - icon: Settings.data.notifications.doNotDisturb ? "notifications_off" : "notifications" + icon: Settings.data.notifications.doNotDisturb ? "bell-slash" : "bell" tooltipText: Settings.data.notifications.doNotDisturb ? "Notification history.\nRight-click to disable 'Do Not Disturb'." : "Notification history.\nRight-click to enable 'Do Not Disturb'." colorBg: Color.mSurfaceVariant - colorFg: Settings.data.notifications.doNotDisturb ? Color.mError : Color.mOnSurface + colorFg: Color.mOnSurface colorBorder: Color.transparent colorBorderHover: Color.transparent diff --git a/Modules/Bar/Widgets/PowerProfile.qml b/Modules/Bar/Widgets/PowerProfile.qml index d29b180..6968413 100644 --- a/Modules/Bar/Widgets/PowerProfile.qml +++ b/Modules/Bar/Widgets/PowerProfile.qml @@ -19,13 +19,13 @@ NIconButton { function profileIcon() { if (!hasPP) - return "balance" + return "yin-yang" if (powerProfiles.profile === PowerProfile.Performance) - return "speed" + return "speedometer2" if (powerProfiles.profile === PowerProfile.Balanced) - return "balance" + return "yin-yang" if (powerProfiles.profile === PowerProfile.PowerSaver) - return "eco" + return "leaf" } function profileName() { diff --git a/Modules/Bar/Widgets/PowerToggle.qml b/Modules/Bar/Widgets/PowerToggle.qml index 219202a..25e380c 100644 --- a/Modules/Bar/Widgets/PowerToggle.qml +++ b/Modules/Bar/Widgets/PowerToggle.qml @@ -13,7 +13,7 @@ NIconButton { sizeRatio: 0.8 - icon: "power_settings_new" + icon: "power" tooltipText: "Power Settings" colorBg: Color.mSurfaceVariant colorFg: Color.mError diff --git a/Modules/Bar/Widgets/ScreenRecorderIndicator.qml b/Modules/Bar/Widgets/ScreenRecorderIndicator.qml index 58c1208..00a785b 100644 --- a/Modules/Bar/Widgets/ScreenRecorderIndicator.qml +++ b/Modules/Bar/Widgets/ScreenRecorderIndicator.qml @@ -11,7 +11,7 @@ NIconButton { property real scaling: 1.0 visible: ScreenRecorderService.isRecording - icon: "videocam" + icon: "camera-video" tooltipText: "Screen recording is active\nClick to stop recording" sizeRatio: 0.8 colorBg: Color.mPrimary diff --git a/Modules/Bar/Widgets/SidePanelToggle.qml b/Modules/Bar/Widgets/SidePanelToggle.qml index 14a8c6f..326d7b1 100644 --- a/Modules/Bar/Widgets/SidePanelToggle.qml +++ b/Modules/Bar/Widgets/SidePanelToggle.qml @@ -33,7 +33,7 @@ NIconButton { readonly property bool useDistroLogo: (widgetSettings.useDistroLogo !== undefined) ? widgetSettings.useDistroLogo : widgetMetadata.useDistroLogo - icon: useDistroLogo ? "" : "widgets" + icon: useDistroLogo ? "" : "layout-sidebar-inset-reverse" tooltipText: "Open side panel." sizeRatio: 0.8 diff --git a/Modules/Bar/Widgets/Spacer.qml b/Modules/Bar/Widgets/Spacer.qml index dc2651c..fcb8cfe 100644 --- a/Modules/Bar/Widgets/Spacer.qml +++ b/Modules/Bar/Widgets/Spacer.qml @@ -38,12 +38,4 @@ Item { implicitHeight: Style.barHeight * scaling width: implicitWidth height: implicitHeight - - // Optional: Add a subtle visual indicator in debug mode - Rectangle { - anchors.fill: parent - color: Qt.rgba(1, 0, 0, 0.1) // Very subtle red tint - visible: Settings.data.general.debugMode || false - radius: Style.radiusXXS * scaling - } } diff --git a/Modules/Bar/Widgets/SystemMonitor.qml b/Modules/Bar/Widgets/SystemMonitor.qml index 91f3fd8..f6e0968 100644 --- a/Modules/Bar/Widgets/SystemMonitor.qml +++ b/Modules/Bar/Widgets/SystemMonitor.qml @@ -38,6 +38,8 @@ RowLayout { !== undefined) ? widgetSettings.showMemoryAsPercent : widgetMetadata.showMemoryAsPercent readonly property bool showNetworkStats: (widgetSettings.showNetworkStats !== undefined) ? widgetSettings.showNetworkStats : widgetMetadata.showNetworkStats + readonly property bool showDiskUsage: (widgetSettings.showDiskUsage + !== undefined) ? widgetSettings.showDiskUsage : widgetMetadata.showDiskUsage Layout.alignment: Qt.AlignVCenter spacing: Style.marginS * scaling @@ -52,126 +54,188 @@ RowLayout { RowLayout { id: mainLayout - anchors.fill: parent - anchors.leftMargin: Style.marginS * scaling - anchors.rightMargin: Style.marginS * scaling + anchors.centerIn: parent // Better centering than margins + width: parent.width - Style.marginM * scaling * 2 spacing: Style.marginS * scaling // CPU Usage Component - RowLayout { - id: cpuUsageLayout - spacing: Style.marginXS * scaling + Item { + Layout.preferredWidth: cpuUsageRow.implicitWidth + Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) Layout.alignment: Qt.AlignVCenter visible: showCpuUsage - NIcon { - id: cpuUsageIcon - text: "speed" - Layout.alignment: Qt.AlignVCenter - } + RowLayout { + id: cpuUsageRow + anchors.centerIn: parent + spacing: Style.marginXS * scaling - NText { - id: cpuUsageText - text: `${SystemStatService.cpuUsage}%` - font.family: Settings.data.ui.fontFixed - font.pointSize: Style.fontSizeS * scaling - font.weight: Style.fontWeightMedium - Layout.alignment: Qt.AlignVCenter - verticalAlignment: Text.AlignVCenter - color: Color.mPrimary + NIcon { + icon: "speedometer2" + font.pointSize: Style.fontSizeM * scaling + Layout.alignment: Qt.AlignVCenter + } + + NText { + text: `${SystemStatService.cpuUsage}%` + font.family: Settings.data.ui.fontFixed + font.pointSize: Style.fontSizeS * scaling + font.weight: Style.fontWeightMedium + Layout.alignment: Qt.AlignVCenter + verticalAlignment: Text.AlignVCenter + color: Color.mPrimary + } } } // CPU Temperature Component - RowLayout { - id: cpuTempLayout - // spacing is thin here to compensate for the vertical thermometer icon - spacing: Style.marginXXS * scaling + Item { + Layout.preferredWidth: cpuTempRow.implicitWidth + Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) Layout.alignment: Qt.AlignVCenter visible: showCpuTemp - NIcon { - text: "thermometer" - Layout.alignment: Qt.AlignVCenter - } + RowLayout { + id: cpuTempRow + anchors.centerIn: parent + spacing: Style.marginXS * scaling - NText { - text: `${SystemStatService.cpuTemp}°C` - font.family: Settings.data.ui.fontFixed - font.pointSize: Style.fontSizeS * scaling - font.weight: Style.fontWeightMedium - Layout.alignment: Qt.AlignVCenter - verticalAlignment: Text.AlignVCenter - color: Color.mPrimary + NIcon { + icon: "fire" + // Fire is so tall, we need to make it smaller + font.pointSize: Style.fontSizeS * scaling + Layout.alignment: Qt.AlignVCenter + } + + NText { + text: `${SystemStatService.cpuTemp}°C` + font.family: Settings.data.ui.fontFixed + font.pointSize: Style.fontSizeS * scaling + font.weight: Style.fontWeightMedium + Layout.alignment: Qt.AlignVCenter + verticalAlignment: Text.AlignVCenter + color: Color.mPrimary + } } } // Memory Usage Component - RowLayout { - id: memoryUsageLayout - spacing: Style.marginXS * scaling + Item { + Layout.preferredWidth: memoryUsageRow.implicitWidth + Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) Layout.alignment: Qt.AlignVCenter visible: showMemoryUsage - NIcon { - text: "memory" - Layout.alignment: Qt.AlignVCenter - } + RowLayout { + id: memoryUsageRow + anchors.centerIn: parent + spacing: Style.marginXS * scaling - NText { - text: showMemoryAsPercent ? `${SystemStatService.memPercent}%` : `${SystemStatService.memGb}G` - font.family: Settings.data.ui.fontFixed - font.pointSize: Style.fontSizeS * scaling - font.weight: Style.fontWeightMedium - Layout.alignment: Qt.AlignVCenter - verticalAlignment: Text.AlignVCenter - color: Color.mPrimary + NIcon { + icon: "cpu" + font.pointSize: Style.fontSizeM * scaling + Layout.alignment: Qt.AlignVCenter + } + + NText { + text: showMemoryAsPercent ? `${SystemStatService.memPercent}%` : `${SystemStatService.memGb}G` + font.family: Settings.data.ui.fontFixed + font.pointSize: Style.fontSizeS * scaling + font.weight: Style.fontWeightMedium + Layout.alignment: Qt.AlignVCenter + verticalAlignment: Text.AlignVCenter + color: Color.mPrimary + } } } // Network Download Speed Component - RowLayout { - id: networkDownloadLayout - spacing: Style.marginXS * scaling + Item { + Layout.preferredWidth: networkDownloadRow.implicitWidth + Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) Layout.alignment: Qt.AlignVCenter visible: showNetworkStats - NIcon { - text: "download" - Layout.alignment: Qt.AlignVCenter - } + RowLayout { + id: networkDownloadRow + anchors.centerIn: parent + spacing: Style.marginXS * scaling - NText { - text: SystemStatService.formatSpeed(SystemStatService.rxSpeed) - font.family: Settings.data.ui.fontFixed - font.pointSize: Style.fontSizeS * scaling - font.weight: Style.fontWeightMedium - Layout.alignment: Qt.AlignVCenter - verticalAlignment: Text.AlignVCenter - color: Color.mPrimary + NIcon { + icon: "cloud-arrow-down" + font.pointSize: Style.fontSizeM * scaling + Layout.alignment: Qt.AlignVCenter + } + + NText { + text: SystemStatService.formatSpeed(SystemStatService.rxSpeed) + font.family: Settings.data.ui.fontFixed + font.pointSize: Style.fontSizeS * scaling + font.weight: Style.fontWeightMedium + Layout.alignment: Qt.AlignVCenter + verticalAlignment: Text.AlignVCenter + color: Color.mPrimary + } } } // Network Upload Speed Component - RowLayout { - id: networkUploadLayout - spacing: Style.marginXS * scaling + Item { + Layout.preferredWidth: networkUploadRow.implicitWidth + Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) Layout.alignment: Qt.AlignVCenter visible: showNetworkStats - NIcon { - text: "upload" - Layout.alignment: Qt.AlignVCenter - } + RowLayout { + id: networkUploadRow + anchors.centerIn: parent + spacing: Style.marginXS * scaling - NText { - text: SystemStatService.formatSpeed(SystemStatService.txSpeed) - font.family: Settings.data.ui.fontFixed - font.pointSize: Style.fontSizeS * scaling - font.weight: Style.fontWeightMedium - Layout.alignment: Qt.AlignVCenter - verticalAlignment: Text.AlignVCenter - color: Color.mPrimary + NIcon { + icon: "cloud-arrow-up" + font.pointSize: Style.fontSizeM * scaling + Layout.alignment: Qt.AlignVCenter + } + + NText { + text: SystemStatService.formatSpeed(SystemStatService.txSpeed) + font.family: Settings.data.ui.fontFixed + font.pointSize: Style.fontSizeS * scaling + font.weight: Style.fontWeightMedium + Layout.alignment: Qt.AlignVCenter + verticalAlignment: Text.AlignVCenter + color: Color.mPrimary + } + } + } + + // Disk Usage Component (primary drive) + Item { + Layout.preferredWidth: diskUsageRow.implicitWidth + Layout.preferredHeight: Math.round(Style.capsuleHeight * scaling) + Layout.alignment: Qt.AlignVCenter + visible: showDiskUsage + + RowLayout { + id: diskUsageRow + anchors.centerIn: parent + spacing: Style.marginXS * scaling + + NIcon { + icon: "hdd" + font.pointSize: Style.fontSizeM * scaling + Layout.alignment: Qt.AlignVCenter + } + + NText { + text: `${SystemStatService.diskPercent}%` + font.family: Settings.data.ui.fontFixed + font.pointSize: Style.fontSizeS * scaling + font.weight: Style.fontWeightMedium + Layout.alignment: Qt.AlignVCenter + verticalAlignment: Text.AlignVCenter + color: Color.mPrimary + } } } } diff --git a/Modules/Bar/Widgets/Volume.qml b/Modules/Bar/Widgets/Volume.qml index 80e79db..b61554c 100644 --- a/Modules/Bar/Widgets/Volume.qml +++ b/Modules/Bar/Widgets/Volume.qml @@ -43,9 +43,9 @@ Item { function getIcon() { if (AudioService.muted) { - return "volume_off" + return "volume-mute" } - return AudioService.volume <= Number.EPSILON ? "volume_off" : (AudioService.volume < 0.33 ? "volume_down" : "volume_up") + return AudioService.volume <= 0.2 ? "volume-off" : (AudioService.volume < 0.6 ? "volume-down" : "volume-up") } // Connection used to open the pill when volume changes @@ -77,8 +77,6 @@ Item { rightOpen: BarWidgetRegistry.getNPillDirection(root) icon: getIcon() - iconCircleColor: Color.mPrimary - collapsedIconColor: Color.mOnSurface autoHide: false // Important to be false so we can hover as long as we want text: Math.floor(AudioService.volume * 100) + "%" forceOpen: alwaysShowPercentage diff --git a/Modules/Bar/Widgets/WiFi.qml b/Modules/Bar/Widgets/WiFi.qml index fe8ff75..4148f0a 100644 --- a/Modules/Bar/Widgets/WiFi.qml +++ b/Modules/Bar/Widgets/WiFi.qml @@ -23,7 +23,7 @@ NIconButton { icon: { try { if (NetworkService.ethernetConnected) { - return "lan" + return "ethernet" } let connected = false let signalStrength = 0 @@ -34,7 +34,7 @@ NIconButton { break } } - return connected ? NetworkService.signalIcon(signalStrength) : "wifi_find" + return connected ? NetworkService.signalIcon(signalStrength) : "wifi-off" } catch (error) { Logger.error("Wi-Fi", "Error getting icon:", error) return "signal_wifi_bad" diff --git a/Modules/BluetoothPanel/BluetoothDevicesList.qml b/Modules/BluetoothPanel/BluetoothDevicesList.qml index 390b709..05fd4d6 100644 --- a/Modules/BluetoothPanel/BluetoothDevicesList.qml +++ b/Modules/BluetoothPanel/BluetoothDevicesList.qml @@ -66,7 +66,7 @@ ColumnLayout { // One device BT icon NIcon { - text: BluetoothService.getDeviceIcon(modelData) + icon: BluetoothService.getDeviceIcon(modelData) font.pointSize: Style.fontSizeXXL * scaling color: getContentColor(Color.mOnSurface) Layout.alignment: Qt.AlignVCenter @@ -164,7 +164,7 @@ ColumnLayout { } return "Connect" } - icon: (isBusy ? "hourglass_full" : null) + icon: (isBusy ? "hourglass-split" : null) onClicked: { if (modelData.connected) { BluetoothService.disconnectDevice(modelData) diff --git a/Modules/BluetoothPanel/BluetoothPanel.qml b/Modules/BluetoothPanel/BluetoothPanel.qml index 751a0c4..12d1b50 100644 --- a/Modules/BluetoothPanel/BluetoothPanel.qml +++ b/Modules/BluetoothPanel/BluetoothPanel.qml @@ -28,7 +28,7 @@ NPanel { spacing: Style.marginM * scaling NIcon { - text: "bluetooth" + icon: "bluetooth" font.pointSize: Style.fontSizeXXL * scaling color: Color.mPrimary } @@ -42,7 +42,7 @@ NPanel { } NIconButton { - icon: BluetoothService.adapter && BluetoothService.adapter.discovering ? "stop_circle" : "refresh" + icon: BluetoothService.adapter && BluetoothService.adapter.discovering ? "stop" : "arrow-repeat" tooltipText: "Refresh Devices" sizeRatio: 0.8 onClicked: { @@ -53,7 +53,7 @@ NPanel { } NIconButton { - icon: "close" + icon: "x-lg" tooltipText: "Close" sizeRatio: 0.8 onClicked: { diff --git a/Modules/IPC/IPCManager.qml b/Modules/IPC/IPCManager.qml index 8c541a3..204dbbe 100644 --- a/Modules/IPC/IPCManager.qml +++ b/Modules/IPC/IPCManager.qml @@ -22,7 +22,9 @@ Item { IpcHandler { target: "screenRecorder" function toggle() { - ScreenRecorderService.toggleRecording() + if (ScreenRecorderService.isAvailable) { + ScreenRecorderService.toggleRecording() + } } } diff --git a/Modules/LockScreen/LockScreen.qml b/Modules/LockScreen/LockScreen.qml index 25b69b1..725a30b 100644 --- a/Modules/LockScreen/LockScreen.qml +++ b/Modules/LockScreen/LockScreen.qml @@ -418,7 +418,7 @@ Loader { font.weight: Style.fontWeightBold } NIcon { - text: "keyboard_alt" + icon: "keyboard" font.pointSize: Style.fontSizeM * scaling color: Color.mOnSurface } @@ -428,7 +428,7 @@ Loader { spacing: Style.marginS * scaling visible: batteryIndicator.batteryVisible NIcon { - text: BatteryService.getIcon(batteryIndicator.percent, batteryIndicator.charging, + icon: BatteryService.getIcon(batteryIndicator.percent, batteryIndicator.charging, batteryIndicator.isReady) font.pointSize: Style.fontSizeM * scaling color: batteryIndicator.charging ? Color.mPrimary : Color.mOnSurface @@ -718,21 +718,47 @@ Loader { anchors.margins: 50 * scaling spacing: 20 * scaling + // Shutdown Rectangle { - Layout.preferredWidth: 60 * scaling - Layout.preferredHeight: 60 * scaling + Layout.preferredWidth: iconPower.implicitWidth + Style.marginXL * scaling + Layout.preferredHeight: Layout.preferredWidth radius: width * 0.5 color: powerButtonArea.containsMouse ? Color.mError : Qt.alpha(Color.mError, 0.2) border.color: Color.mError border.width: Math.max(1, Style.borderM * scaling) NIcon { + id: iconPower anchors.centerIn: parent - text: "power_settings_new" - font.pointSize: Style.fontSizeXL * scaling + icon: "power" + font.pointSize: Style.fontSizeXXXL * scaling color: powerButtonArea.containsMouse ? Color.mOnError : Color.mError } + // Tooltip (inline rectangle to avoid separate Window during lock) + Rectangle { + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.top + anchors.bottomMargin: 12 * scaling + radius: Style.radiusM * scaling + color: Color.mSurface + border.color: Color.mOutline + border.width: Math.max(1, Style.borderS * scaling) + visible: powerButtonArea.containsMouse + z: 1 + NText { + id: shutdownTooltipText + anchors.margins: Style.marginM * scaling + anchors.fill: parent + text: "Shut down the computer." + font.pointSize: Style.fontSizeM * scaling + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + implicitWidth: shutdownTooltipText.implicitWidth + Style.marginM * 2 * scaling + implicitHeight: shutdownTooltipText.implicitHeight + Style.marginM * 2 * scaling + } + MouseArea { id: powerButtonArea anchors.fill: parent @@ -743,21 +769,47 @@ Loader { } } + // Reboot Rectangle { - Layout.preferredWidth: 60 * scaling - Layout.preferredHeight: 60 * scaling + Layout.preferredWidth: iconReboot.implicitWidth + Style.marginXL * scaling + Layout.preferredHeight: Layout.preferredWidth radius: width * 0.5 color: restartButtonArea.containsMouse ? Color.mPrimary : Qt.alpha(Color.mPrimary, Style.opacityLight) border.color: Color.mPrimary border.width: Math.max(1, Style.borderM * scaling) NIcon { + id: iconReboot anchors.centerIn: parent - text: "restart_alt" - font.pointSize: Style.fontSizeXL * scaling + icon: "arrow-repeat" + font.pointSize: Style.fontSizeXXXL * scaling color: restartButtonArea.containsMouse ? Color.mOnPrimary : Color.mPrimary } + // Tooltip + Rectangle { + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.top + anchors.bottomMargin: 12 * scaling + radius: Style.radiusM * scaling + color: Color.mSurface + border.color: Color.mOutline + border.width: Math.max(1, Style.borderS * scaling) + visible: restartButtonArea.containsMouse + z: 1 + NText { + id: restartTooltipText + anchors.margins: Style.marginM * scaling + anchors.fill: parent + text: "Restart the computer." + font.pointSize: Style.fontSizeM * scaling + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + implicitWidth: restartTooltipText.implicitWidth + Style.marginM * 2 * scaling + implicitHeight: restartTooltipText.implicitHeight + Style.marginM * 2 * scaling + } + MouseArea { id: restartButtonArea anchors.fill: parent @@ -765,24 +817,51 @@ Loader { onClicked: { CompositorService.reboot() } + // Tooltip handled via inline rectangle visibility } } + // Suspend Rectangle { - Layout.preferredWidth: 60 * scaling - Layout.preferredHeight: 60 * scaling + Layout.preferredWidth: iconSuspend.implicitWidth + Style.marginXL * scaling + Layout.preferredHeight: Layout.preferredWidth radius: width * 0.5 color: suspendButtonArea.containsMouse ? Color.mSecondary : Qt.alpha(Color.mSecondary, 0.2) border.color: Color.mSecondary border.width: Math.max(1, Style.borderM * scaling) NIcon { + id: iconSuspend anchors.centerIn: parent - text: "bedtime" - font.pointSize: Style.fontSizeXL * scaling + icon: "pause-fill" + font.pointSize: Style.fontSizeXXXL * scaling color: suspendButtonArea.containsMouse ? Color.mOnSecondary : Color.mSecondary } + // Tooltip + Rectangle { + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.top + anchors.bottomMargin: 12 * scaling + radius: Style.radiusM * scaling + color: Color.mSurface + border.color: Color.mOutline + border.width: Math.max(1, Style.borderS * scaling) + visible: suspendButtonArea.containsMouse + z: 1 + NText { + id: suspendTooltipText + anchors.margins: Style.marginM * scaling + anchors.fill: parent + text: "Suspend the system." + font.pointSize: Style.fontSizeM * scaling + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + implicitWidth: suspendTooltipText.implicitWidth + Style.marginM * 2 * scaling + implicitHeight: suspendTooltipText.implicitHeight + Style.marginM * 2 * scaling + } + MouseArea { id: suspendButtonArea anchors.fill: parent @@ -790,6 +869,7 @@ Loader { onClicked: { CompositorService.suspend() } + // Tooltip handled via inline rectangle visibility } } } diff --git a/Modules/Notification/Notification.qml b/Modules/Notification/Notification.qml index fdbe0d2..ab16610 100644 --- a/Modules/Notification/Notification.qml +++ b/Modules/Notification/Notification.qml @@ -205,14 +205,12 @@ Variants { Layout.fillWidth: true spacing: Style.marginM * scaling - // Avatar + // Image NImageCircled { - id: appAvatar Layout.preferredWidth: 40 * scaling Layout.preferredHeight: 40 * scaling Layout.alignment: Qt.AlignTop imagePath: model.image && model.image !== "" ? model.image : "" - fallbackIcon: "" borderColor: Color.transparent borderWidth: 0 visible: (model.image && model.image !== "") @@ -294,7 +292,7 @@ Variants { // Close button positioned absolutely NIconButton { - icon: "close" + icon: "x-lg" tooltipText: "Close" sizeRatio: 0.6 anchors.top: parent.top diff --git a/Modules/Notification/NotificationHistoryPanel.qml b/Modules/Notification/NotificationHistoryPanel.qml index 39686df..bb97b84 100644 --- a/Modules/Notification/NotificationHistoryPanel.qml +++ b/Modules/Notification/NotificationHistoryPanel.qml @@ -31,7 +31,7 @@ NPanel { spacing: Style.marginM * scaling NIcon { - text: "notifications" + icon: "bell" font.pointSize: Style.fontSizeXXL * scaling color: Color.mPrimary } @@ -45,21 +45,21 @@ NPanel { } NIconButton { - icon: Settings.data.notifications.doNotDisturb ? "notifications_off" : "notifications_active" + icon: Settings.data.notifications.doNotDisturb ? "bell-slash" : "bell" tooltipText: Settings.data.notifications.doNotDisturb ? "'Do Not Disturb' is enabled." : "'Do Not Disturb' is disabled." sizeRatio: 0.8 onClicked: Settings.data.notifications.doNotDisturb = !Settings.data.notifications.doNotDisturb } NIconButton { - icon: "delete" + icon: "trash" tooltipText: "Clear history" sizeRatio: 0.8 onClicked: NotificationService.clearHistory() } NIconButton { - icon: "close" + icon: "x-lg" tooltipText: "Close" sizeRatio: 0.8 onClicked: { @@ -85,7 +85,7 @@ NPanel { } NIcon { - text: "notifications_off" + icon: "bell-slash" font.pointSize: 64 * scaling color: Color.mOnSurfaceVariant Layout.alignment: Qt.AlignHCenter @@ -175,7 +175,7 @@ NPanel { // Delete button NIconButton { - icon: "delete" + icon: "trash" tooltipText: "Delete notification" sizeRatio: 0.7 Layout.alignment: Qt.AlignTop diff --git a/Modules/PowerPanel/PowerPanel.qml b/Modules/PowerPanel/PowerPanel.qml index efb475e..f4f5758 100644 --- a/Modules/PowerPanel/PowerPanel.qml +++ b/Modules/PowerPanel/PowerPanel.qml @@ -29,27 +29,27 @@ NPanel { property int selectedIndex: 0 readonly property var powerOptions: [{ "action": "lock", - "icon": "lock_outline", + "icon": "lock", "title": "Lock", "subtitle": "Lock your session" }, { "action": "suspend", - "icon": "bedtime", + "icon": "pause-circle", "title": "Suspend", "subtitle": "Put the system to sleep" }, { "action": "reboot", - "icon": "refresh", + "icon": "arrow-repeat", "title": "Reboot", "subtitle": "Restart the system" }, { "action": "logout", - "icon": "exit_to_app", + "icon": "escape", "title": "Logout", "subtitle": "End your session" }, { "action": "shutdown", - "icon": "power_settings_new", + "icon": "power", "title": "Shutdown", "subtitle": "Turn off the system", "isShutdown": true @@ -276,7 +276,7 @@ NPanel { } NIconButton { - icon: timerActive ? "back_hand" : "close" + icon: timerActive ? "x-square" : "x-lg" tooltipText: timerActive ? "Cancel Timer" : "Close" Layout.alignment: Qt.AlignVCenter colorBg: timerActive ? Qt.alpha(Color.mError, 0.08) : Color.transparent @@ -360,7 +360,7 @@ NPanel { id: iconElement anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter - text: buttonRoot.icon + icon: buttonRoot.icon color: { if (buttonRoot.pending) return Color.mPrimary diff --git a/Modules/SettingsPanel/Bar/BarSectionEditor.qml b/Modules/SettingsPanel/Bar/BarSectionEditor.qml index 7a1684a..7e27096 100644 --- a/Modules/SettingsPanel/Bar/BarSectionEditor.qml +++ b/Modules/SettingsPanel/Bar/BarSectionEditor.qml @@ -85,7 +85,7 @@ NBox { } NIconButton { - icon: "add" + icon: "plus-lg" colorBg: Color.mPrimary colorFg: Color.mOnPrimary @@ -170,7 +170,7 @@ NBox { Loader { active: BarWidgetRegistry.widgetHasUserSettings(modelData.id) sourceComponent: NIconButton { - icon: "settings" + icon: "gear" sizeRatio: 0.6 colorBorder: Qt.alpha(Color.mOutline, Style.opacityLight) colorBg: Color.mOnSurface @@ -210,7 +210,7 @@ NBox { } NIconButton { - icon: "close" + icon: "x-lg" sizeRatio: 0.6 colorBorder: Qt.alpha(Color.mOutline, Style.opacityLight) colorBg: Color.mOnSurface diff --git a/Modules/SettingsPanel/Bar/BarWidgetSettingsDialog.qml b/Modules/SettingsPanel/Bar/BarWidgetSettingsDialog.qml index 9ba0045..b80c156 100644 --- a/Modules/SettingsPanel/Bar/BarWidgetSettingsDialog.qml +++ b/Modules/SettingsPanel/Bar/BarWidgetSettingsDialog.qml @@ -84,7 +84,7 @@ Popup { } NIconButton { - icon: "close" + icon: "x-lg" onClicked: settingsPopup.close() } } @@ -107,6 +107,7 @@ Popup { RowLayout { Layout.fillWidth: true Layout.topMargin: Style.marginM * scaling + spacing: Style.marginM * scaling Item { Layout.fillWidth: true @@ -120,7 +121,7 @@ Popup { NButton { text: "Apply" - icon: "check" + icon: "check-lg" onClicked: { if (settingsLoader.item && settingsLoader.item.saveSettings) { var newSettings = settingsLoader.item.saveSettings() diff --git a/Modules/SettingsPanel/Bar/WidgetSettings/BatterySettings.qml b/Modules/SettingsPanel/Bar/WidgetSettings/BatterySettings.qml index 54b589a..4e66f65 100644 --- a/Modules/SettingsPanel/Bar/WidgetSettings/BatterySettings.qml +++ b/Modules/SettingsPanel/Bar/WidgetSettings/BatterySettings.qml @@ -16,10 +16,13 @@ ColumnLayout { // Local state property bool valueAlwaysShowPercentage: widgetData.alwaysShowPercentage !== undefined ? widgetData.alwaysShowPercentage : widgetMetadata.alwaysShowPercentage + property int valueWarningThreshold: widgetData.warningThreshold + !== undefined ? widgetData.warningThreshold : widgetMetadata.warningThreshold function saveSettings() { var settings = Object.assign({}, widgetData || {}) settings.alwaysShowPercentage = valueAlwaysShowPercentage + settings.warningThreshold = valueWarningThreshold return settings } @@ -28,4 +31,14 @@ ColumnLayout { checked: root.valueAlwaysShowPercentage onToggled: checked => root.valueAlwaysShowPercentage = checked } + + NSpinBox { + label: "Low battery warning threshold" + description: "Show a warning when battery falls below this percentage." + value: valueWarningThreshold + suffix: "%" + minimum: 5 + maximum: 50 + onValueChanged: valueWarningThreshold = value + } } diff --git a/Modules/SettingsPanel/Bar/WidgetSettings/CustomButtonSettings.qml b/Modules/SettingsPanel/Bar/WidgetSettings/CustomButtonSettings.qml index b7c896f..7e5c78a 100644 --- a/Modules/SettingsPanel/Bar/WidgetSettings/CustomButtonSettings.qml +++ b/Modules/SettingsPanel/Bar/WidgetSettings/CustomButtonSettings.qml @@ -1,6 +1,7 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts +import QtQuick.Window import qs.Commons import qs.Widgets import qs.Services @@ -9,7 +10,6 @@ ColumnLayout { id: root spacing: Style.marginM * scaling - // Properties to receive data from parent property var widgetData: null property var widgetMetadata: null @@ -22,16 +22,190 @@ ColumnLayout { return settings } - // Icon setting NTextInput { id: iconInput Layout.fillWidth: true label: "Icon Name" - description: "Choose a name from the Material Icon set." - placeholderText: "Enter icon name (e.g., favorite, home, settings)" + description: "Pick from Bootstrap Icons or type a name." + placeholderText: "Enter icon name (e.g., speedometer2, gear, house)" text: widgetData?.icon || widgetMetadata.icon } + RowLayout { + spacing: Style.marginS * scaling + Layout.alignment: Qt.AlignLeft + NIcon { + Layout.alignment: Qt.AlignVCenter + icon: iconInput.text + visible: iconInput.text !== "" + } + NButton { + text: "Browse" + onClicked: iconPicker.open() + } + } + + Popup { + id: iconPicker + modal: true + property real panelWidth: { + var w = Math.round(Math.max(Screen.width * 0.35, 900) * scaling) + w = Math.min(w, Screen.width - Style.marginL * 2) + return w + } + property real panelHeight: { + var h = Math.round(Math.max(Screen.height * 0.65, 700) * scaling) + h = Math.min(h, Screen.height - Style.barHeight * scaling - Style.marginL * 2) + return h + } + width: panelWidth + height: panelHeight + anchors.centerIn: Overlay.overlay + padding: Style.marginXL * scaling + + property string query: "" + property string selectedIcon: "" + property var allIcons: Object.keys(Bootstrap.icons) + property var filteredIcons: allIcons.filter(function (name) { + return query === "" || name.toLowerCase().indexOf(query.toLowerCase()) !== -1 + }) + readonly property int tileBase: Math.round(112 * scaling) + readonly property int columns: Math.max(3, Math.floor(grid.width / (tileBase + Style.marginS * 2))) + readonly property int cellW: Math.floor(grid.width / columns) + readonly property int cellH: Math.round(cellW * 0.7 + 36 * scaling) + + background: Rectangle { + color: Color.mSurface + radius: Style.radiusL * scaling + border.color: Color.mPrimary + border.width: Style.borderM * scaling + } + + ColumnLayout { + anchors.fill: parent + spacing: Style.marginM * scaling + + // Title row + RowLayout { + Layout.fillWidth: true + NText { + text: "Icon Picker" + font.pointSize: Style.fontSizeL * scaling + font.weight: Style.fontWeightBold + color: Color.mPrimary + Layout.fillWidth: true + } + NIconButton { + icon: "x-lg" + onClicked: iconPicker.close() + } + } + + NDivider { + Layout.fillWidth: true + } + + RowLayout { + Layout.fillWidth: true + spacing: Style.marginS * scaling + NTextInput { + Layout.fillWidth: true + label: "Search" + placeholderText: "Search (e.g., arrow, battery, cloud)" + text: iconPicker.query + onTextChanged: iconPicker.query = text.trim().toLowerCase() + } + } + + // Icon grid + ScrollView { + Layout.fillWidth: true + Layout.fillHeight: true + clip: true + + GridView { + id: grid + anchors.fill: parent + anchors.margins: Style.marginM * scaling + cellWidth: iconPicker.cellW + cellHeight: iconPicker.cellH + model: iconPicker.filteredIcons + delegate: Rectangle { + width: grid.cellWidth + height: grid.cellHeight + radius: Style.radiusS * scaling + clip: true + color: (iconPicker.selectedIcon === modelData) ? Qt.alpha(Color.mPrimary, 0.15) : "transparent" + border.color: (iconPicker.selectedIcon === modelData) ? Color.mPrimary : Qt.rgba(0, 0, 0, 0) + border.width: (iconPicker.selectedIcon === modelData) ? Style.borderS * scaling : 0 + + MouseArea { + anchors.fill: parent + onClicked: iconPicker.selectedIcon = modelData + onDoubleClicked: { + iconPicker.selectedIcon = modelData + iconInput.text = iconPicker.selectedIcon + iconPicker.close() + } + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: Style.marginS * scaling + spacing: Style.marginS * scaling + Item { + Layout.fillHeight: true + Layout.preferredHeight: 4 * scaling + } + NIcon { + Layout.alignment: Qt.AlignHCenter + icon: modelData + font.pointSize: Style.fontSizeXXXL * scaling + } + NText { + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + Layout.topMargin: Style.marginXS * scaling + elide: Text.ElideRight + wrapMode: Text.NoWrap + maximumLineCount: 1 + horizontalAlignment: Text.AlignHCenter + color: Color.mOnSurfaceVariant + font.pointSize: Style.fontSizeXS * scaling + text: modelData + } + Item { + Layout.fillHeight: true + } + } + } + } + } + + RowLayout { + Layout.fillWidth: true + spacing: Style.marginM * scaling + Item { + Layout.fillWidth: true + } + NButton { + text: "Cancel" + outlined: true + onClicked: iconPicker.close() + } + NButton { + text: "Apply" + icon: "check-lg" + enabled: iconPicker.selectedIcon !== "" + onClicked: { + iconInput.text = iconPicker.selectedIcon + iconPicker.close() + } + } + } + } + } + NTextInput { id: leftClickExecInput Layout.fillWidth: true diff --git a/Modules/SettingsPanel/Bar/WidgetSettings/SystemMonitorSettings.qml b/Modules/SettingsPanel/Bar/WidgetSettings/SystemMonitorSettings.qml index 4f2459b..39e4614 100644 --- a/Modules/SettingsPanel/Bar/WidgetSettings/SystemMonitorSettings.qml +++ b/Modules/SettingsPanel/Bar/WidgetSettings/SystemMonitorSettings.qml @@ -21,6 +21,7 @@ ColumnLayout { !== undefined ? widgetData.showMemoryAsPercent : widgetMetadata.showMemoryAsPercent property bool valueShowNetworkStats: widgetData.showNetworkStats !== undefined ? widgetData.showNetworkStats : widgetMetadata.showNetworkStats + property bool valueShowDiskUsage: widgetData.showDiskUsage !== undefined ? widgetData.showDiskUsage : widgetMetadata.showDiskUsage function saveSettings() { var settings = Object.assign({}, widgetData || {}) @@ -29,6 +30,7 @@ ColumnLayout { settings.showMemoryUsage = valueShowMemoryUsage settings.showMemoryAsPercent = valueShowMemoryAsPercent settings.showNetworkStats = valueShowNetworkStats + settings.showDiskUsage = valueShowDiskUsage return settings } @@ -59,7 +61,7 @@ ColumnLayout { NToggle { id: showMemoryAsPercent Layout.fillWidth: true - label: "Show memory as percentage" + label: "Memory as percentage" checked: valueShowMemoryAsPercent onToggled: checked => valueShowMemoryAsPercent = checked } @@ -71,4 +73,12 @@ ColumnLayout { checked: valueShowNetworkStats onToggled: checked => valueShowNetworkStats = checked } + + NToggle { + id: showDiskUsage + Layout.fillWidth: true + label: "Storage usage" + checked: valueShowDiskUsage + onToggled: checked => valueShowDiskUsage = checked + } } diff --git a/Modules/SettingsPanel/SettingsPanel.qml b/Modules/SettingsPanel/SettingsPanel.qml index ea8c701..6b5e4bd 100644 --- a/Modules/SettingsPanel/SettingsPanel.qml +++ b/Modules/SettingsPanel/SettingsPanel.qml @@ -123,42 +123,42 @@ NPanel { let newTabs = [{ "id": SettingsPanel.Tab.General, "label": "General", - "icon": "tune", + "icon": "box", "source": generalTab }, { "id": SettingsPanel.Tab.Bar, "label": "Bar", - "icon": "web_asset", + "icon": "segmented-nav", "source": barTab }, { "id": SettingsPanel.Tab.Launcher, "label": "Launcher", - "icon": "apps", + "icon": "rocket", "source": launcherTab }, { "id": SettingsPanel.Tab.Audio, "label": "Audio", - "icon": "volume_up", + "icon": "speaker", "source": audioTab }, { "id": SettingsPanel.Tab.Display, "label": "Display", - "icon": "monitor", + "icon": "display", "source": displayTab }, { "id": SettingsPanel.Tab.Network, "label": "Network", - "icon": "lan", + "icon": "ethernet", "source": networkTab }, { "id": SettingsPanel.Tab.Brightness, "label": "Brightness", - "icon": "brightness_6", + "icon": "brightness-high", "source": brightnessTab }, { "id": SettingsPanel.Tab.Weather, "label": "Weather", - "icon": "partly_cloudy_day", + "icon": "cloud-sun", "source": weatherTab }, { "id": SettingsPanel.Tab.ColorScheme, @@ -168,7 +168,7 @@ NPanel { }, { "id": SettingsPanel.Tab.Wallpaper, "label": "Wallpaper", - "icon": "image", + "icon": "easel", "source": wallpaperTab }] @@ -177,7 +177,7 @@ NPanel { newTabs.push({ "id": SettingsPanel.Tab.WallpaperSelector, "label": "Wallpaper Selector", - "icon": "wallpaper_slideshow", + "icon": "image", "source": wallpaperSelectorTab }) } @@ -185,17 +185,17 @@ NPanel { newTabs.push({ "id": SettingsPanel.Tab.ScreenRecorder, "label": "Screen Recorder", - "icon": "videocam", + "icon": "camera-video", "source": screenRecorderTab }, { "id": SettingsPanel.Tab.Hooks, "label": "Hooks", - "icon": "cable", + "icon": "link-45deg", "source": hooksTab }, { "id": SettingsPanel.Tab.About, "label": "About", - "icon": "info", + "icon": "info-circle", "source": aboutTab }) @@ -400,15 +400,13 @@ NPanel { anchors.fill: parent anchors.leftMargin: Style.marginS * scaling anchors.rightMargin: Style.marginS * scaling - spacing: Style.marginS * scaling + spacing: Style.marginM * scaling - // Tab icon NIcon { - text: modelData.icon + text: Bootstrap.icons[modelData.icon] color: tabTextColor font.pointSize: Style.fontSizeL * scaling } - // Tab label NText { text: modelData.label @@ -416,6 +414,7 @@ NPanel { font.pointSize: Style.fontSizeM * scaling font.weight: Style.fontWeightBold Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter } } @@ -473,7 +472,7 @@ NPanel { // Close button NIconButton { - icon: "close" + icon: "x-lg" tooltipText: "Close" Layout.alignment: Qt.AlignVCenter onClicked: root.close() diff --git a/Modules/SettingsPanel/Tabs/AudioTab.qml b/Modules/SettingsPanel/Tabs/AudioTab.qml index d60ae38..16e6781 100644 --- a/Modules/SettingsPanel/Tabs/AudioTab.qml +++ b/Modules/SettingsPanel/Tabs/AudioTab.qml @@ -272,7 +272,7 @@ ColumnLayout { // Button aligned to the center of the actual input field NIconButton { - icon: "add" + icon: "plus-lg" Layout.alignment: Qt.AlignBottom Layout.bottomMargin: blacklistInput.description ? Style.marginS * scaling : 0 onClicked: { @@ -322,7 +322,7 @@ ColumnLayout { } NIconButton { - icon: "close" + icon: "x-lg" sizeRatio: 0.8 Layout.alignment: Qt.AlignVCenter Layout.rightMargin: Style.marginXS * scaling diff --git a/Modules/SettingsPanel/Tabs/DisplayTab.qml b/Modules/SettingsPanel/Tabs/DisplayTab.qml index 9a5c4e3..534b62c 100644 --- a/Modules/SettingsPanel/Tabs/DisplayTab.qml +++ b/Modules/SettingsPanel/Tabs/DisplayTab.qml @@ -181,7 +181,7 @@ ColumnLayout { } NIconButton { - icon: "refresh" + icon: "arrow-repeat" tooltipText: "Reset scaling" onClicked: ScalingService.setScreenScale(modelData, 1.0) } diff --git a/Modules/SettingsPanel/Tabs/WallpaperSelectorTab.qml b/Modules/SettingsPanel/Tabs/WallpaperSelectorTab.qml index 6952c71..8d3e77b 100644 --- a/Modules/SettingsPanel/Tabs/WallpaperSelectorTab.qml +++ b/Modules/SettingsPanel/Tabs/WallpaperSelectorTab.qml @@ -96,7 +96,7 @@ ColumnLayout { } NIconButton { - icon: "refresh" + icon: "arrow-repeat" tooltipText: "Refresh wallpaper list" onClicked: { WallpaperService.refreshWallpapersList() @@ -181,7 +181,7 @@ ColumnLayout { visible: isSelected NIcon { - text: "check" + icon: "check-lg" font.pointSize: Style.fontSizeM * scaling font.weight: Style.fontWeightBold color: Color.mOnSecondary diff --git a/Modules/SidePanel/Cards/MediaCard.qml b/Modules/SidePanel/Cards/MediaCard.qml index 65f7211..7d3f5cf 100644 --- a/Modules/SidePanel/Cards/MediaCard.qml +++ b/Modules/SidePanel/Cards/MediaCard.qml @@ -31,7 +31,7 @@ NBox { } NIcon { - text: "album" + icon: "disc" font.pointSize: Style.fontSizeXXXL * 2.5 * scaling color: Color.mPrimary Layout.alignment: Qt.AlignHCenter @@ -162,14 +162,14 @@ NBox { anchors.fill: parent anchors.margins: Style.marginXS * scaling imagePath: MediaService.trackArtUrl - fallbackIcon: "music_note" + fallbackIcon: "disc" borderColor: Color.mOutline borderWidth: Math.max(1, Style.borderS * scaling) } // Fallback icon when no album art available NIcon { - text: "album" + icon: "disc" color: Color.mPrimary font.pointSize: Style.fontSizeL * 12 * scaling visible: !trackArt.visible @@ -307,7 +307,7 @@ NBox { // Previous button NIconButton { - icon: "skip_previous" + icon: "skip-start" tooltipText: "Previous Media" visible: MediaService.canGoPrevious onClicked: MediaService.canGoPrevious ? MediaService.previous() : {} @@ -315,7 +315,7 @@ NBox { // Play/Pause button NIconButton { - icon: MediaService.isPlaying ? "pause" : "play_arrow" + icon: MediaService.isPlaying ? "pause" : "play" tooltipText: MediaService.isPlaying ? "Pause" : "Play" visible: (MediaService.canPlay || MediaService.canPause) onClicked: (MediaService.canPlay || MediaService.canPause) ? MediaService.playPause() : {} @@ -323,7 +323,7 @@ NBox { // Next button NIconButton { - icon: "skip_next" + icon: "skip-end" tooltipText: "Next media" visible: MediaService.canGoNext onClicked: MediaService.canGoNext ? MediaService.next() : {} diff --git a/Modules/SidePanel/Cards/PowerProfilesCard.qml b/Modules/SidePanel/Cards/PowerProfilesCard.qml index 8eb28e8..842d432 100644 --- a/Modules/SidePanel/Cards/PowerProfilesCard.qml +++ b/Modules/SidePanel/Cards/PowerProfilesCard.qml @@ -28,7 +28,7 @@ NBox { } // Performance NIconButton { - icon: "speed" + icon: "speedometer2" tooltipText: "Set performance power profile." enabled: hasPP opacity: enabled ? Style.opacityFull : Style.opacityMedium @@ -37,12 +37,13 @@ NBox { onClicked: { if (enabled) { powerProfiles.profile = PowerProfile.Performance + ToastService.showNotice("Power Profile", "Performance") } } } // Balanced NIconButton { - icon: "balance" + icon: "yin-yang" tooltipText: "Set balanced power profile." enabled: hasPP opacity: enabled ? Style.opacityFull : Style.opacityMedium @@ -51,12 +52,13 @@ NBox { onClicked: { if (enabled) { powerProfiles.profile = PowerProfile.Balanced + ToastService.showNotice("Power Profile", "Balanced") } } } // Eco NIconButton { - icon: "eco" + icon: "leaf" tooltipText: "Set eco power profile." enabled: hasPP opacity: enabled ? Style.opacityFull : Style.opacityMedium @@ -65,6 +67,7 @@ NBox { onClicked: { if (enabled) { powerProfiles.profile = PowerProfile.PowerSaver + ToastService.showNotice("Power Profile", "Power Saver") } } } diff --git a/Modules/SidePanel/Cards/ProfileCard.qml b/Modules/SidePanel/Cards/ProfileCard.qml index 4c2d1ce..66ecaa2 100644 --- a/Modules/SidePanel/Cards/ProfileCard.qml +++ b/Modules/SidePanel/Cards/ProfileCard.qml @@ -47,7 +47,8 @@ NBox { } NText { text: `System uptime: ${uptimeText}` - color: Color.mOnSurface + font.pointSize: Style.fontSizeS * scaling + color: Color.mOnSurfaceVariant } } @@ -58,7 +59,7 @@ NBox { Layout.fillWidth: true } NIconButton { - icon: "settings" + icon: "gear" tooltipText: "Open settings." onClicked: { settingsPanel.requestedTab = SettingsPanel.Tab.General @@ -68,7 +69,7 @@ NBox { NIconButton { id: powerButton - icon: "power_settings_new" + icon: "power" tooltipText: "Power menu." onClicked: { powerPanel.open(screen) @@ -78,7 +79,7 @@ NBox { NIconButton { id: closeButton - icon: "close" + icon: "x-lg" tooltipText: "Close side panel." onClicked: { sidePanel.close() diff --git a/Modules/SidePanel/Cards/SystemMonitorCard.qml b/Modules/SidePanel/Cards/SystemMonitorCard.qml index 9d3154d..d9af228 100644 --- a/Modules/SidePanel/Cards/SystemMonitorCard.qml +++ b/Modules/SidePanel/Cards/SystemMonitorCard.qml @@ -24,7 +24,7 @@ NBox { NCircleStat { value: SystemStatService.cpuUsage - icon: "speed" + icon: "speedometer2" flat: true contentScale: 0.8 width: 72 * scaling @@ -33,7 +33,7 @@ NBox { NCircleStat { value: SystemStatService.cpuTemp suffix: "°C" - icon: "device_thermostat" + icon: "fire" flat: true contentScale: 0.8 width: 72 * scaling @@ -41,7 +41,7 @@ NBox { } NCircleStat { value: SystemStatService.memPercent - icon: "memory" + icon: "cpu" flat: true contentScale: 0.8 width: 72 * scaling @@ -49,7 +49,7 @@ NBox { } NCircleStat { value: SystemStatService.diskPercent - icon: "hard_drive" + icon: "hdd" flat: true contentScale: 0.8 width: 72 * scaling diff --git a/Modules/SidePanel/Cards/UtilitiesCard.qml b/Modules/SidePanel/Cards/UtilitiesCard.qml index 78fc702..f4bde37 100644 --- a/Modules/SidePanel/Cards/UtilitiesCard.qml +++ b/Modules/SidePanel/Cards/UtilitiesCard.qml @@ -25,11 +25,14 @@ NBox { } // Screen Recorder NIconButton { - icon: "videocam" - tooltipText: ScreenRecorderService.isRecording ? "Stop screen recording." : "Start screen recording." + icon: "camera-video" + enabled: ScreenRecorderService.isAvailable + tooltipText: ScreenRecorderService.isAvailable ? (ScreenRecorderService.isRecording ? "Stop screen recording." : "Start screen recording.") : "Screen recorder not installed." colorBg: ScreenRecorderService.isRecording ? Color.mPrimary : Color.mSurfaceVariant colorFg: ScreenRecorderService.isRecording ? Color.mOnPrimary : Color.mPrimary onClicked: { + if (!ScreenRecorderService.isAvailable) + return ScreenRecorderService.toggleRecording() // If we were not recording and we just initiated a start, close the panel if (!ScreenRecorderService.isRecording) { @@ -41,7 +44,7 @@ NBox { // Idle Inhibitor NIconButton { - icon: "coffee" + icon: "cup" tooltipText: IdleInhibitorService.isInhibited ? "Disable keep awake." : "Enable keep awake." colorBg: IdleInhibitorService.isInhibited ? Color.mPrimary : Color.mSurfaceVariant colorFg: IdleInhibitorService.isInhibited ? Color.mOnPrimary : Color.mPrimary diff --git a/Modules/SidePanel/Cards/WeatherCard.qml b/Modules/SidePanel/Cards/WeatherCard.qml index 3751478..baa04c8 100644 --- a/Modules/SidePanel/Cards/WeatherCard.qml +++ b/Modules/SidePanel/Cards/WeatherCard.qml @@ -27,7 +27,7 @@ NBox { RowLayout { spacing: Style.marginS * scaling NIcon { - text: weatherReady ? LocationService.weatherSymbolFromCode( + icon: weatherReady ? LocationService.weatherSymbolFromCode( LocationService.data.weather.current_weather.weathercode) : "" font.pointSize: Style.fontSizeXXXL * 1.75 * scaling color: Color.mPrimary @@ -98,7 +98,7 @@ NBox { color: Color.mOnSurface } NIcon { - text: LocationService.weatherSymbolFromCode(LocationService.data.weather.daily.weathercode[index]) + icon: LocationService.weatherSymbolFromCode(LocationService.data.weather.daily.weathercode[index]) font.pointSize: Style.fontSizeXXL * scaling color: Color.mPrimary } diff --git a/Modules/WiFiPanel/WiFiPanel.qml b/Modules/WiFiPanel/WiFiPanel.qml index 627be65..25ea245 100644 --- a/Modules/WiFiPanel/WiFiPanel.qml +++ b/Modules/WiFiPanel/WiFiPanel.qml @@ -34,7 +34,7 @@ NPanel { spacing: Style.marginM * scaling NIcon { - text: Settings.data.network.wifiEnabled ? "wifi" : "wifi_off" + icon: Settings.data.network.wifiEnabled ? "wifi" : "wifi-off" font.pointSize: Style.fontSizeXXL * scaling color: Settings.data.network.wifiEnabled ? Color.mPrimary : Color.mOnSurfaceVariant } @@ -55,7 +55,7 @@ NPanel { } NIconButton { - icon: "refresh" + icon: "arrow-repeat" tooltipText: "Refresh" sizeRatio: 0.8 enabled: Settings.data.network.wifiEnabled && !NetworkService.scanning @@ -63,7 +63,7 @@ NPanel { } NIconButton { - icon: "close" + icon: "x-lg" tooltipText: "Close" sizeRatio: 0.8 onClicked: root.close() @@ -91,7 +91,7 @@ NPanel { spacing: Style.marginS * scaling NIcon { - text: "error" + icon: "exclamation-triangle" font.pointSize: Style.fontSizeL * scaling color: Color.mError } @@ -105,7 +105,7 @@ NPanel { } NIconButton { - icon: "close" + icon: "x-lg" sizeRatio: 0.6 onClicked: NetworkService.lastError = "" } @@ -129,7 +129,7 @@ NPanel { } NIcon { - text: "wifi_off" + icon: "wifi-off" font.pointSize: 64 * scaling color: Color.mOnSurfaceVariant Layout.alignment: Qt.AlignHCenter @@ -245,7 +245,7 @@ NPanel { spacing: Style.marginS * scaling NIcon { - text: NetworkService.signalIcon(modelData.signal) + icon: NetworkService.signalIcon(modelData.signal) font.pointSize: Style.fontSizeXXL * scaling color: modelData.connected ? Color.mPrimary : Color.mOnSurface } @@ -377,7 +377,7 @@ NPanel { && NetworkService.connectingTo !== modelData.ssid && NetworkService.forgettingNetwork !== modelData.ssid && NetworkService.disconnectingFrom !== modelData.ssid - icon: "delete" + icon: "trash" tooltipText: "Forget network" sizeRatio: 0.7 onClicked: expandedSsid = expandedSsid === modelData.ssid ? "" : modelData.ssid @@ -492,7 +492,7 @@ NPanel { } NIconButton { - icon: "close" + icon: "x-lg" sizeRatio: 0.8 onClicked: { passwordSsid = "" @@ -547,7 +547,7 @@ NPanel { } NIconButton { - icon: "close" + icon: "x-lg" sizeRatio: 0.8 onClicked: expandedSsid = "" } @@ -571,7 +571,7 @@ NPanel { } NIcon { - text: "wifi_find" + icon: "search" font.pointSize: 64 * scaling color: Color.mOnSurfaceVariant Layout.alignment: Qt.AlignHCenter @@ -586,7 +586,7 @@ NPanel { NButton { text: "Scan again" - icon: "refresh" + icon: "arrow-repeat" Layout.alignment: Qt.AlignHCenter onClicked: NetworkService.scan() } diff --git a/README.md b/README.md index dcdcd20..5212782 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,11 @@

+ +> ⚠️ **BREAKING CHANGE:** +> We transitioned from using Material Symbols to Bootstrap Icons, that means you will have to install the new font. +--- + A sleek and minimal desktop shell thoughtfully crafted for Wayland, built with Quickshell. Features a modern modular architecture with a status bar, notification system, control panel, comprehensive system integration, and more — all styled with a warm lavender palette, or your favorite color scheme! @@ -66,7 +71,7 @@ Features a modern modular architecture with a status bar, notification system, c - `quickshell-git` - Core shell framework - `ttf-roboto` - The default font used for most of the UI - `inter-font` - The default font used for Headers (ex: clock on the LockScreen) -- `ttf-material-symbols-variable-git` - Icon font for UI elements +- `ttf-bootstrap-icons` - Icon font for UI elements - `gpu-screen-recorder` - Screen recording functionality - `brightnessctl` - For internal/laptop monitor brightness - `ddcutil` - For desktop monitor brightness (might introduce some system instability with certain monitors) diff --git a/Services/AudioService.qml b/Services/AudioService.qml index c6ec05c..9f526ea 100644 --- a/Services/AudioService.qml +++ b/Services/AudioService.qml @@ -62,6 +62,8 @@ Singleton { function onMutedChanged() { root._muted = (sink?.audio.muted ?? true) Logger.log("AudioService", "OnMuteChanged:", root._muted) + // Toast: audio output mute toggle + ToastService.showNotice("Audio Output", root._muted ? "Muted" : "Unmuted") } } @@ -79,6 +81,8 @@ Singleton { function onMutedChanged() { root._inputMuted = (source?.audio.muted ?? true) Logger.log("AudioService", "OnInputMuteChanged:", root._inputMuted) + // Toast: microphone mute toggle + ToastService.showNotice("Microphone", root._inputMuted ? "Muted" : "Unmuted") } } diff --git a/Services/BarWidgetRegistry.qml b/Services/BarWidgetRegistry.qml index b82a3e1..fc99a0e 100644 --- a/Services/BarWidgetRegistry.qml +++ b/Services/BarWidgetRegistry.qml @@ -84,7 +84,8 @@ Singleton { "showCpuTemp": true, "showMemoryUsage": true, "showMemoryAsPercent": false, - "showNetworkStats": false + "showNetworkStats": false, + "showDiskUsage": false }, "Workspace": { "allowUserSettings": true, diff --git a/Services/BatteryService.qml b/Services/BatteryService.qml index 6ceb872..4a4b606 100644 --- a/Services/BatteryService.qml +++ b/Services/BatteryService.qml @@ -2,6 +2,8 @@ pragma Singleton import Quickshell import Quickshell.Services.UPower +import qs.Commons +import qs.Services Singleton { id: root @@ -9,41 +11,20 @@ Singleton { // Choose icon based on charge and charging state function getIcon(percent, charging, isReady) { if (!isReady) { - return "battery_error" + return "exclamation-triangle" } if (charging) { - if (percent >= 95) - return "battery_full" - if (percent >= 85) - return "battery_charging_90" - if (percent >= 65) - return "battery_charging_80" - if (percent >= 55) - return "battery_charging_60" - if (percent >= 45) - return "battery_charging_50" - if (percent >= 25) - return "battery_charging_30" - if (percent >= 0) - return "battery_charging_20" + return "battery-charging" } else { - if (percent >= 95) - return "battery_full" if (percent >= 85) - return "battery_6_bar" - if (percent >= 70) - return "battery_5_bar" - if (percent >= 55) - return "battery_4_bar" - if (percent >= 40) - return "battery_3_bar" + return "battery-full" + if (percent >= 45) + return "battery-half" if (percent >= 25) - return "battery_2_bar" - if (percent >= 10) - return "battery_1_bar" + return "battery-low" if (percent >= 0) - return "battery_0_bar" + return "battery" } } } diff --git a/Services/BluetoothService.qml b/Services/BluetoothService.qml index d5e2d51..b029b70 100644 --- a/Services/BluetoothService.qml +++ b/Services/BluetoothService.qml @@ -62,17 +62,17 @@ Singleton { } if (icon.includes("mouse") || name.includes("mouse")) { - return "mouse" + return "mouse-2" } if (icon.includes("keyboard") || name.includes("keyboard")) { return "keyboard" } if (icon.includes("phone") || name.includes("phone") || name.includes("iphone") || name.includes("android") || name.includes("samsung")) { - return "smartphone" + return "phone" } if (icon.includes("watch") || name.includes("watch")) { - return "watch" + return "smartwatch" } if (icon.includes("speaker") || name.includes("speaker")) { return "speaker" diff --git a/Services/ColorSchemeService.qml b/Services/ColorSchemeService.qml index 096f8f9..ade585f 100644 --- a/Services/ColorSchemeService.qml +++ b/Services/ColorSchemeService.qml @@ -23,6 +23,11 @@ Singleton { // Re-apply current scheme to pick the right variant applyScheme(Settings.data.colorSchemes.predefinedScheme) } + // Toast: dark/light mode switched + const enabled = !!Settings.data.colorSchemes.darkMode + const label = enabled ? "Dark Mode" : "Light Mode" + const description = enabled ? "Enabled" : "Enabled" + ToastService.showNotice(label, description) } } diff --git a/Services/CompositorService.qml b/Services/CompositorService.qml index 173d3d6..7e46e31 100644 --- a/Services/CompositorService.qml +++ b/Services/CompositorService.qml @@ -192,9 +192,11 @@ Singleton { } windowsList.push({ - "id": toplevel.address || "", - "title": toplevel.title || "", - "appId": appId, + "id": (toplevel.address !== undefined + && toplevel.address !== null) ? String(toplevel.address) : "", + "title": (toplevel.title !== undefined && toplevel.title !== null) ? String( + toplevel.title) : "", + "appId": (appId !== undefined && appId !== null) ? String(appId) : "", "workspaceId": toplevel.workspace?.id || null, "isFocused": toplevel.activated === true }) diff --git a/Services/FontService.qml b/Services/FontService.qml index a030452..ebf03e3 100644 --- a/Services/FontService.qml +++ b/Services/FontService.qml @@ -13,6 +13,7 @@ Singleton { property ListModel displayFonts: ListModel {} property bool fontsLoaded: false + // ------------------------------------------- function init() { Logger.log("Font", "Service started") loadSystemFonts() diff --git a/Services/LocationService.qml b/Services/LocationService.qml index fef295c..0cbdbd5 100644 --- a/Services/LocationService.qml +++ b/Services/LocationService.qml @@ -231,21 +231,23 @@ Singleton { // -------------------------------- function weatherSymbolFromCode(code) { if (code === 0) - return "sunny" + return "sun" if (code === 1 || code === 2) - return "partly_cloudy_day" + return "cloud-sun" if (code === 3) return "cloud" if (code >= 45 && code <= 48) - return "foggy" + return "cloud-haze" if (code >= 51 && code <= 67) - return "rainy" + return "cloud-rain" if (code >= 71 && code <= 77) - return "weather_snowy" - if (code >= 80 && code <= 82) - return "rainy" + return "cloud-snow" + if (code >= 71 && code <= 77) + return "cloud-snow" + if (code >= 85 && code <= 86) + return "cloud-snow" if (code >= 95 && code <= 99) - return "thunderstorm" + return "cloud-lightning" return "cloud" } diff --git a/Services/NetworkService.qml b/Services/NetworkService.qml index 6bf7ac5..97304c1 100644 --- a/Services/NetworkService.qml +++ b/Services/NetworkService.qml @@ -202,14 +202,12 @@ Singleton { // Helper functions function signalIcon(signal) { if (signal >= 80) - return "network_wifi" - if (signal >= 60) - return "network_wifi_3_bar" - if (signal >= 40) - return "network_wifi_2_bar" + return "wifi" + if (signal >= 50) + return "wifi-2" if (signal >= 20) - return "network_wifi_1_bar" - return "signal_wifi_0_bar" + return "wifi-1" + return "dot" } function isSecured(security) { diff --git a/Services/NightLightService.qml b/Services/NightLightService.qml index 3719a29..77084f2 100644 --- a/Services/NightLightService.qml +++ b/Services/NightLightService.qml @@ -50,6 +50,9 @@ Singleton { target: Settings.data.nightLight function onEnabledChanged() { apply() + // Toast: night light toggled + const enabled = !!Settings.data.nightLight.enabled + ToastService.showNotice("Night Light", enabled ? "Enabled" : "Disabled") } function onNightTempChanged() { apply() diff --git a/Services/ScreenRecorderService.qml b/Services/ScreenRecorderService.qml index 08d6503..7642542 100644 --- a/Services/ScreenRecorderService.qml +++ b/Services/ScreenRecorderService.qml @@ -13,6 +13,17 @@ Singleton { property bool isRecording: false property bool isPending: false property string outputPath: "" + property bool isAvailable: false + + Component.onCompleted: { + checkAvailability() + } + + function checkAvailability() { + // Detect native or Flatpak gpu-screen-recorder + availabilityCheckProcess.command = ["sh", "-c", "command -v gpu-screen-recorder >/dev/null 2>&1 || (command -v flatpak >/dev/null 2>&1 && flatpak list --app | grep -q 'com.dec05eba.gpu_screen_recorder')"] + availabilityCheckProcess.running = true + } // Start or Stop recording function toggleRecording() { @@ -21,6 +32,9 @@ Singleton { // Start screen recording using Quickshell.execDetached function startRecording() { + if (!isAvailable) { + return + } if (isRecording || isPending) { return } @@ -88,6 +102,18 @@ Singleton { } } + // Availability check process + Process { + id: availabilityCheckProcess + command: ["sh", "-c", "true"] + onExited: function (exitCode, exitStatus) { + // exitCode 0 means available, non-zero means unavailable + root.isAvailable = (exitCode === 0) + } + stdout: StdioCollector {} + stderr: StdioCollector {} + } + Timer { id: pendingTimer interval: 2000 // Wait 2 seconds to see if process stays alive diff --git a/Services/SystemStatService.qml b/Services/SystemStatService.qml index 7328f71..11a62cf 100644 --- a/Services/SystemStatService.qml +++ b/Services/SystemStatService.qml @@ -321,10 +321,8 @@ Singleton { // ------------------------------------------------------- // Helper function to format network speeds function formatSpeed(bytesPerSecond) { - if (bytesPerSecond < 1024) { - return bytesPerSecond.toFixed(0) + "B/s" - } else if (bytesPerSecond < 1024 * 1024) { - return (bytesPerSecond / 1024).toFixed(0) + "KB/s" + if (bytesPerSecond < 1024 * 1024) { + return (bytesPerSecond / 1024).toFixed(1) + "KB/s" } else if (bytesPerSecond < 1024 * 1024 * 1024) { return (bytesPerSecond / (1024 * 1024)).toFixed(1) + "MB/s" } else { diff --git a/Services/ToastService.qml b/Services/ToastService.qml index edff04b..ee9fb24 100644 --- a/Services/ToastService.qml +++ b/Services/ToastService.qml @@ -185,6 +185,11 @@ Singleton { // Process the message queue function processQueue() { if (messageQueue.length === 0 || allToasts.length === 0) { + // Added this so we don't accidentally get duplicate toasts + // if it causes issues, remove it and we'll find a different solution + if (allToasts.length === 0 && messageQueue.length > 0) { + messageQueue = [] + } isShowingToast = false return } diff --git a/Widgets/NButton.qml b/Widgets/NButton.qml index 75c9bc5..1bf82b0 100644 --- a/Widgets/NButton.qml +++ b/Widgets/NButton.qml @@ -80,27 +80,29 @@ Rectangle { spacing: Style.marginXS * scaling // Icon (optional) - NIcon { - Layout.alignment: Qt.AlignVCenter - layoutTopMargin: 1 * scaling - visible: root.icon !== "" - text: root.icon - font.pointSize: root.iconSize - color: { - if (!root.enabled) - return Color.mOnSurfaceVariant - if (root.outlined) { - if (root.pressed || root.hovered) - return root.backgroundColor - return root.backgroundColor - } - return root.textColor - } + Loader { + active: root.icon !== "" + sourceComponent: NIcon { + Layout.alignment: Qt.AlignVCenter - Behavior on color { - ColorAnimation { - duration: Style.animationFast - easing.type: Easing.OutCubic + icon: root.icon + font.pointSize: root.iconSize + color: { + if (!root.enabled) + return Color.mOnSurfaceVariant + if (root.outlined) { + if (root.pressed || root.hovered) + return root.backgroundColor + return root.backgroundColor + } + return root.textColor + } + + Behavior on color { + ColorAnimation { + duration: Style.animationFast + easing.type: Easing.OutCubic + } } } } diff --git a/Widgets/NCheckbox.qml b/Widgets/NCheckbox.qml index 4b5962d..3dd9783 100644 --- a/Widgets/NCheckbox.qml +++ b/Widgets/NCheckbox.qml @@ -57,7 +57,7 @@ RowLayout { NIcon { visible: root.checked anchors.centerIn: parent - text: "check" + icon: "check-lg" color: root.activeOnColor font.pointSize: Math.max(Style.fontSizeS, root.baseSize * 0.7) * scaling } diff --git a/Widgets/NCircleStat.qml b/Widgets/NCircleStat.qml index e16cb12..211acdf 100644 --- a/Widgets/NCircleStat.qml +++ b/Widgets/NCircleStat.qml @@ -88,20 +88,21 @@ Rectangle { // Tiny circular badge for the icon, positioned using anchors within the gauge Rectangle { id: iconBadge - width: 28 * scaling * contentScale + width: iconText.implicitWidth + Style.marginXS * scaling height: width radius: width / 2 - color: Color.mSurface + color: Color.mPrimary anchors.right: parent.right anchors.top: parent.top - anchors.rightMargin: -6 * scaling * contentScale - anchors.topMargin: Style.marginXXS * scaling * contentScale + anchors.rightMargin: Style.marginXXS * scaling * contentScale + anchors.topMargin: Style.marginXS * scaling * contentScale NIcon { + id: iconText anchors.centerIn: parent - text: root.icon - font.pointSize: Style.fontSizeLargeXL * scaling * contentScale - color: Color.mOnSurface + icon: root.icon + font.pointSize: Style.fontSizeS * scaling * contentScale + color: Color.mOnPrimary horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } diff --git a/Widgets/NColorPicker.qml b/Widgets/NColorPicker.qml index 830ba84..0e80080 100644 --- a/Widgets/NColorPicker.qml +++ b/Widgets/NColorPicker.qml @@ -2,6 +2,7 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts import qs.Commons +import qs.Services import qs.Widgets Rectangle { @@ -58,7 +59,7 @@ Rectangle { } NIcon { - text: "palette" + icon: "paint-bucket" color: Color.mOnSurfaceVariant } } diff --git a/Widgets/NColorPickerDialog.qml b/Widgets/NColorPickerDialog.qml index 324e5b6..60f5fd1 100644 --- a/Widgets/NColorPickerDialog.qml +++ b/Widgets/NColorPickerDialog.qml @@ -2,6 +2,7 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts import qs.Commons +import qs.Services import qs.Widgets Popup { @@ -129,7 +130,7 @@ Popup { spacing: Style.marginS * scaling NIcon { - text: "palette" + icon: "eyedropper" font.pointSize: Style.fontSizeXXL * scaling color: Color.mPrimary } @@ -147,7 +148,7 @@ Popup { } NIconButton { - icon: "close" + icon: "x-lg" onClicked: root.close() } } @@ -491,7 +492,7 @@ Popup { NButton { id: cancelButton text: "Cancel" - icon: "close" + icon: "x-lg" outlined: cancelButton.hovered ? false : true customHeight: 36 * scaling customWidth: 100 * scaling @@ -502,7 +503,7 @@ Popup { NButton { text: "Apply" - icon: "check" + icon: "check-lg" customHeight: 36 * scaling customWidth: 100 * scaling onClicked: { diff --git a/Widgets/NComboBox.qml b/Widgets/NComboBox.qml index 57bc0bb..ab3e708 100644 --- a/Widgets/NComboBox.qml +++ b/Widgets/NComboBox.qml @@ -85,8 +85,8 @@ RowLayout { indicator: NIcon { x: combo.width - width - Style.marginM * scaling y: combo.topPadding + (combo.availableHeight - height) / 2 - text: "arrow_drop_down" - font.pointSize: Style.fontSizeXXL * scaling + icon: "chevron-down" + font.pointSize: Style.fontSizeL * scaling } popup: Popup { diff --git a/Widgets/NIcon.qml b/Widgets/NIcon.qml index ac5a0ec..fedbaa0 100644 --- a/Widgets/NIcon.qml +++ b/Widgets/NIcon.qml @@ -1,19 +1,22 @@ import QtQuick +import QtQuick.Layouts import qs.Commons import qs.Widgets -import QtQuick.Layouts Text { - // Optional layout nudge for optical alignment when used inside Layouts - property real layoutTopMargin: 0 - text: "question_mark" - font.family: "Material Symbols Rounded" - font.pointSize: Style.fontSizeL * scaling - font.variableAxes: { - "wght"// slightly bold to ensure all lines looks good - : (Font.Normal + Font.Bold) / 2.5 + readonly property string defaultIcon: "balloon" + property string icon: defaultIcon + + text: { + if (icon === undefined || Bootstrap.icons[icon] === undefined) { + Logger.warn("Icon", `"${icon}"`, "doesn't exist in the bootstrap font") + Logger.callStack() + return Bootstrap.icons[defaultIcon] + } + return Bootstrap.icons[icon] } + font.family: "bootstrap-icons" + font.pointSize: Style.fontSizeL * scaling color: Color.mOnSurface verticalAlignment: Text.AlignVCenter - Layout.topMargin: layoutTopMargin } diff --git a/Widgets/NIconButton.qml b/Widgets/NIconButton.qml index c9755b3..296279e 100644 --- a/Widgets/NIconButton.qml +++ b/Widgets/NIconButton.qml @@ -35,17 +35,31 @@ Rectangle { opacity: root.enabled ? Style.opacityFull : Style.opacityMedium color: root.enabled && root.hovering ? colorBgHover : colorBg radius: width * 0.5 - border.color: root.hovering ? colorBorderHover : colorBorder + border.color: root.enabled && root.hovering ? colorBorderHover : colorBorder border.width: Math.max(1, Style.borderS * scaling) + Behavior on color { + ColorAnimation { + duration: Style.animationNormal + easing.type: Easing.InOutQuad + } + } + NIcon { - text: root.icon - font.pointSize: Style.fontSizeM * scaling - color: root.hovering ? colorFgHover : colorFg + icon: root.icon + font.pointSize: Math.max(1, root.width * 0.4) + color: root.enabled && root.hovering ? colorFgHover : colorFg // Center horizontally x: (root.width - width) / 2 // Center vertically accounting for font metrics y: (root.height - height) / 2 + (height - contentHeight) / 2 + + Behavior on color { + ColorAnimation { + duration: Style.animationFast + easing.type: Easing.InOutQuad + } + } } NTooltip { @@ -56,13 +70,14 @@ Rectangle { } MouseArea { - enabled: root.enabled + // Always enabled to allow hover/tooltip even when the button is disabled + enabled: true anchors.fill: parent cursorShape: root.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton hoverEnabled: true onEntered: { - hovering = true + hovering = root.enabled ? true : false if (tooltipText) { tooltip.show() } @@ -79,6 +94,9 @@ Rectangle { if (tooltipText) { tooltip.hide() } + if (!root.enabled) { + return + } if (mouse.button === Qt.LeftButton) { root.clicked() } else if (mouse.button === Qt.RightButton) { diff --git a/Widgets/NImageCircled.qml b/Widgets/NImageCircled.qml index 7279c08..61190ea 100644 --- a/Widgets/NImageCircled.qml +++ b/Widgets/NImageCircled.qml @@ -9,9 +9,10 @@ Rectangle { id: root property string imagePath: "" - property string fallbackIcon: "" property color borderColor: Color.transparent property real borderWidth: 0 + property string fallbackIcon: "" + property real fallbackIconSize: Style.fontSizeXXL * scaling color: Color.transparent radius: parent.width * 0.5 @@ -45,18 +46,20 @@ Rectangle { } property real imageOpacity: root.opacity - fragmentShader: Qt.resolvedUrl("../Shaders/qsb/circled_image.frag.qsb") + fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/circled_image.frag.qsb") supportsAtlasTextures: false blending: true } // Fallback icon - NIcon { - anchors.centerIn: parent - text: fallbackIcon - font.pointSize: Style.fontSizeXXL * scaling - visible: fallbackIcon !== undefined && fallbackIcon !== "" && (imagePath === undefined || imagePath === "") - z: 0 + Loader { + active: fallbackIcon !== undefined && fallbackIcon !== "" && (imagePath === undefined || imagePath === "") + sourceComponent: NIcon { + anchors.centerIn: parent + icon: fallbackIcon + font.pointSize: fallbackIconSize + z: 0 + } } } diff --git a/Widgets/NImageRounded.qml b/Widgets/NImageRounded.qml index 76654fc..b1950f3 100644 --- a/Widgets/NImageRounded.qml +++ b/Widgets/NImageRounded.qml @@ -9,10 +9,11 @@ Rectangle { id: root property string imagePath: "" - property string fallbackIcon: "" property color borderColor: Color.transparent property real borderWidth: 0 property real imageRadius: width * 0.5 + property string fallbackIcon: "" + property real fallbackIconSize: Style.fontSizeXXL * scaling property real scaledRadius: imageRadius * Settings.data.general.radiusRatio @@ -56,7 +57,7 @@ Rectangle { property real itemHeight: root.height property real cornerRadius: root.radius property real imageOpacity: root.opacity - fragmentShader: Qt.resolvedUrl("../Shaders/qsb/rounded_image.frag.qsb") + fragmentShader: Qt.resolvedUrl(Quickshell.shellDir + "/Shaders/qsb/rounded_image.frag.qsb") // Qt6 specific properties - ensure proper blending supportsAtlasTextures: false @@ -71,12 +72,14 @@ Rectangle { } // Fallback icon - NIcon { - anchors.centerIn: parent - text: fallbackIcon - font.pointSize: Style.fontSizeXXL * scaling - visible: fallbackIcon !== undefined && fallbackIcon !== "" && (imagePath === undefined || imagePath === "") - z: 0 + Loader { + active: fallbackIcon !== undefined && fallbackIcon !== "" && (imagePath === undefined || imagePath === "") + sourceComponent: NIcon { + anchors.centerIn: parent + icon: fallbackIcon + font.pointSize: fallbackIconSize + z: 0 + } } } diff --git a/Widgets/NInputAction.qml b/Widgets/NInputAction.qml index 785b5b0..1ae0629 100644 --- a/Widgets/NInputAction.qml +++ b/Widgets/NInputAction.qml @@ -2,6 +2,7 @@ import QtQuick import QtQuick.Layouts import qs.Commons import qs.Widgets +import qs.Services // Input and button row RowLayout { @@ -13,7 +14,7 @@ RowLayout { property string placeholderText: "" property string text: "" property string actionButtonText: "Test" - property string actionButtonIcon: "play_arrow" + property string actionButtonIcon: "play" property bool actionButtonEnabled: text !== "" // Signals diff --git a/Widgets/NPill.qml b/Widgets/NPill.qml index 2011c9d..7b57ad8 100644 --- a/Widgets/NPill.qml +++ b/Widgets/NPill.qml @@ -9,21 +9,15 @@ Item { property string icon: "" property string text: "" property string tooltipText: "" - property color pillColor: Color.mSurfaceVariant - property color textColor: Color.mOnSurface - property color iconCircleColor: Color.mPrimary - property color iconTextColor: Color.mSurface - property color collapsedIconColor: Color.mOnSurface - - property real iconRotation: 0 property real sizeRatio: 0.8 property bool autoHide: false property bool forceOpen: false property bool disableOpen: false property bool rightOpen: false + property bool hovered: false // Effective shown state (true if hovered/animated open or forced) - readonly property bool effectiveShown: forceOpen || showPill + readonly property bool revealed: forceOpen || showPill signal shown signal hidden @@ -50,14 +44,14 @@ Item { Rectangle { id: pill - width: effectiveShown ? maxPillWidth : 1 + width: revealed ? maxPillWidth : 1 height: pillHeight x: rightOpen ? (iconCircle.x + iconCircle.width / 2) : // Opens right (iconCircle.x + iconCircle.width / 2) - width // Opens left - opacity: effectiveShown ? Style.opacityFull : Style.opacityNone - color: pillColor + opacity: revealed ? Style.opacityFull : Style.opacityNone + color: Color.mSurfaceVariant topLeftRadius: rightOpen ? 0 : pillHeight * 0.5 bottomLeftRadius: rightOpen ? 0 : pillHeight * 0.5 @@ -77,8 +71,8 @@ Item { text: root.text font.pointSize: Style.fontSizeXS * scaling font.weight: Style.fontWeightBold - color: textColor - visible: effectiveShown + color: Color.mPrimary + visible: revealed } Behavior on width { @@ -102,11 +96,8 @@ Item { width: iconSize height: iconSize radius: width * 0.5 - // When forced shown, match pill background; otherwise use accent when hovered - color: forceOpen ? pillColor : (showPill ? iconCircleColor : Color.mSurfaceVariant) + color: hovered && !forceOpen ? Color.mPrimary : Color.mSurfaceVariant anchors.verticalCenter: parent.verticalCenter - border.width: Math.max(1, Style.borderS * scaling) - border.color: forceOpen ? Qt.alpha(Color.mOutline, 0.5) : Color.transparent x: rightOpen ? 0 : (parent.width - width) @@ -118,11 +109,9 @@ Item { } NIcon { - text: root.icon - rotation: root.iconRotation + icon: root.icon font.pointSize: Style.fontSizeM * scaling - // When forced shown, use pill text color; otherwise accent color when hovered - color: forceOpen ? textColor : (showPill ? iconTextColor : Color.mOnSurface) + color: hovered && !forceOpen ? Color.mOnPrimary : Color.mOnSurface // Center horizontally x: (iconCircle.width - width) / 2 // Center vertically accounting for font metrics @@ -220,6 +209,7 @@ Item { hoverEnabled: true acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton onEntered: { + hovered = true root.entered() tooltip.show() if (disableOpen) { @@ -230,6 +220,7 @@ Item { } } onExited: { + hovered = false root.exited() if (!forceOpen) { hide() diff --git a/Widgets/NSpinBox.qml b/Widgets/NSpinBox.qml index 8049cf6..cbc1561 100644 --- a/Widgets/NSpinBox.qml +++ b/Widgets/NSpinBox.qml @@ -95,7 +95,7 @@ RowLayout { NIcon { anchors.centerIn: parent - text: "remove" + icon: "chevron-left" font.pointSize: Style.fontSizeS * scaling color: decreaseArea.containsMouse ? Color.mOnPrimary : Color.mPrimary } @@ -130,7 +130,7 @@ RowLayout { NIcon { anchors.centerIn: parent - text: "add" + icon: "chevron-right" font.pointSize: Style.fontSizeS * scaling color: increaseArea.containsMouse ? Color.mOnPrimary : Color.mPrimary } diff --git a/Widgets/NToast.qml b/Widgets/NToast.qml index 7a60c6c..c6ccbbc 100644 --- a/Widgets/NToast.qml +++ b/Widgets/NToast.qml @@ -112,23 +112,13 @@ Item { RowLayout { id: contentLayout anchors.fill: parent - anchors.margins: Style.marginM * scaling - spacing: Style.marginS * scaling + anchors.margins: Style.marginL * scaling + spacing: Style.marginL * scaling // Icon NIcon { id: icon - text: { - switch (root.type) { - case "warning": - return "warning" - case "notice": - return "info" - default: - return "info" - } - } - + icon: (root.type == "warning") ? "exclamation-triangle" : "check-circle" color: { switch (root.type) { case "warning": @@ -172,7 +162,7 @@ Item { // Close button (only if persistent or manual dismiss needed) NIconButton { - icon: "close" + icon: "x-lg" visible: root.persistent || root.duration === 0 colorBg: Color.mSurfaceVariant diff --git a/flake.nix b/flake.nix index b61342f..bb16caa 100644 --- a/flake.nix +++ b/flake.nix @@ -1,16 +1,13 @@ { description = "Noctalia shell - a Wayland desktop shell built with Quickshell"; - inputs = { nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; systems.url = "github:nix-systems/default"; - quickshell = { url = "git+https://git.outfoxxed.me/outfoxxed/quickshell"; inputs.nixpkgs.follows = "nixpkgs"; }; }; - outputs = { self, nixpkgs, @@ -24,7 +21,6 @@ system: nixpkgs.legacyPackages.${system}.alejandra ); - packages = eachSystem ( system: let pkgs = nixpkgs.legacyPackages.${system}; @@ -32,7 +28,33 @@ withX11 = false; withI3 = false; }; - + + # Custom ttf-bootstrap-icons package + ttf-bootstrap-icons = pkgs.stdenvNoCC.mkDerivation rec { + pname = "ttf-bootstrap-icons"; + version = "1.13.1"; + + src = pkgs.fetchzip { + url = "https://github.com/twbs/icons/releases/download/v${version}/bootstrap-icons-${version}.zip"; + sha256 = "999021e12fab5c9ede5e4e7072eb176122be798b2f99195acf5dda47aef8fc93"; + stripRoot = false; + }; + + installPhase = '' + runHook preInstall + install -Dm644 fonts/bootstrap-icons.ttf $out/share/fonts/truetype/bootstrap-icons.ttf + runHook postInstall + ''; + + meta = with pkgs.lib; { + description = "Official open source SVG icon library for Bootstrap"; + homepage = "https://icons.getbootstrap.com/"; + license = licenses.mit; + platforms = platforms.all; + maintainers = []; + }; + }; + runtimeDeps = with pkgs; [ bash bluez @@ -49,12 +71,12 @@ networkmanager wl-clipboard ]; - fontconfig = pkgs.makeFontsConf { fontDirectories = [ pkgs.material-symbols pkgs.roboto pkgs.inter-nerdfont + ttf-bootstrap-icons # Add the custom font package here ]; }; in { @@ -62,21 +84,17 @@ pname = "noctalia-shell"; version = self.rev or self.dirtyRev or "dirty"; src = ./.; - nativeBuildInputs = [pkgs.gcc pkgs.makeWrapper pkgs.qt6.wrapQtAppsHook]; buildInputs = [qs pkgs.xkeyboard-config pkgs.qt6.qtbase]; propagatedBuildInputs = runtimeDeps; - installPhase = '' mkdir -p $out/share/noctalia-shell cp -r ./* $out/share/noctalia-shell - makeWrapper ${qs}/bin/qs $out/bin/noctalia-shell \ --prefix PATH : "${pkgs.lib.makeBinPath runtimeDeps}" \ --set FONTCONFIG_FILE "${fontconfig}" \ --add-flags "-p $out/share/noctalia-shell" ''; - meta = { description = "A sleek and minimal desktop shell thoughtfully crafted for Wayland, built with Quickshell."; homepage = "https://github.com/noctalia-dev/noctalia-shell"; @@ -84,9 +102,11 @@ mainProgram = "noctalia-shell"; }; }; + + # Expose the custom font as a separate package (optional) + ttf-bootstrap-icons = ttf-bootstrap-icons; } ); - defaultPackage = eachSystem (system: self.packages.${system}.default); }; -} +} \ No newline at end of file