Merge pull request #18 from tr1xem/macos
Misc Fix
No files matched your search
@@ -1,3 +1,6 @@
|
||||
<svg width="24" height="24" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m11.5 5c-0.277 0-0.5 0.223-0.5 0.5v4.875l-1.719-1.5c-0.312-0.273-0.757-0.22-1.03 0.094a0.753 0.753 0 0 0 0.062 1.062l2.25 1.969-2.25 1.969a0.753 0.753 0 0 0-0.063 1.062c0.274 0.313 0.719 0.367 1.031 0.094l1.72-1.5v4.875c0 0.277 0.222 0.5 0.5 0.5 0.282 0 0.437-0.02 0.655-0.188l3.532-3.156a0.78 0.78 0 0 0 0.218-0.375 0.745 0.745 0 0 0 0.032-0.281 0.723 0.723 0 0 0-0.032-0.156 0.78 0.78 0 0 0-0.218-0.375l-2.813-2.47 2.813-2.468a0.78 0.78 0 0 0 0.218-0.375 0.712 0.712 0 0 0 0-0.437 0.78 0.78 0 0 0-0.218-0.375l-3.532-3.157c-0.199-0.16-0.427-0.187-0.656-0.187zm0.5 2.063 2.094 1.875-2.094 1.812zm0 6.187 2.094 1.813-2.094 1.875z" color="#bebebe" fill="#f2f2f2" overflow="visible"/>
|
||||
<svg width="16" height="16" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<style id="current-color-scheme" type="text/css">.ColorScheme-Text { color:#dedede; } .ColorScheme-Highlight { color:#5294e2; }</style>
|
||||
</defs>
|
||||
<path d="m7.2949 1.0449a0.50005 0.50005 0 0 0-0.29492 0.45508v5.2852l-2.7344-1.709a0.5 0.5 0 0 0-0.68945 0.1582 0.5 0.5 0 0 0 0.1582 0.68945l3.2656 2.041v0.070313l-3.2656 2.041a0.5 0.5 0 0 0-0.1582 0.68945 0.5 0.5 0 0 0 0.68945 0.1582l2.7344-1.709v5.2852a0.50005 0.50005 0 0 0 0.83203 0.37305l4.5-4a0.50005 0.50005 0 0 0-0.06641-0.79688l-3.3223-2.0762 3.3223-2.0762a0.50005 0.50005 0 0 0 0.06641-0.79688l-4.5-4a0.50005 0.50005 0 0 0-0.53711-0.082031zm0.70508 1.5684 3.1699 2.8164-3.1699 1.9805zm0 5.9766 3.1699 1.9805-3.1699 2.8164z" class="ColorScheme-Text" fill="currentColor"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 777 B After Width: | Height: | Size: 822 B |
@@ -1,30 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
id="svg6"
|
||||
version="1.1"
|
||||
width="24"
|
||||
height="24">
|
||||
<metadata
|
||||
id="metadata12">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs10" />
|
||||
<path
|
||||
id="path2"
|
||||
d="M 6.3515625,4.7363281 5.4121094,5.6777344 17.601562,17.867188 18.541016,16.925781 13.267578,11.652344 15.6875,9.53125 c 0.106935,-0.1017801 0.18279,-0.2318176 0.21875,-0.375 0.04605,-0.1421978 0.04605,-0.2953022 0,-0.4375 C 15.87029,8.5755676 15.794435,8.4455301 15.6875,8.34375 L 12.15625,5.1875 C 11.95725,5.0275 11.729,5 11.5,5 11.223,5 11,5.223 11,5.5 V 9.3847656 Z M 12,7.0625 14.09375,8.9375 12.195312,10.580078 12,10.384766 Z M 10.494141,12.060547 8.3125,13.96875 c -0.310445,0.276247 -0.3384165,0.751761 -0.0625,1.0625 0.273,0.313 0.71825,0.36675 1.03125,0.09375 l 1.720703,-1.5 V 18.5 c 0,0.277 0.222,0.5 0.5,0.5 0.282,0 0.436297,-0.0195 0.654297,-0.1875 L 14.84375,16.410156 13.779297,15.345703 12,16.9375 v -3.371094 z"
|
||||
style="color:#bebebe;overflow:visible;fill:#f2f2f2;marker:none" />
|
||||
<svg width="16" height="16" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<style id="current-color-scheme" type="text/css">.ColorScheme-Text {
|
||||
color:#dedede;
|
||||
}</style>
|
||||
<rect class="ColorScheme-Text" transform="rotate(45)" x="1.3137" y="-.5" width="20" height="1" rx=".5" ry=".5" fill="currentColor" stroke-width="1.118"/>
|
||||
<path class="ColorScheme-Text" d="m7.2953 1.0449a0.50005 0.50005 0 0 0-0.29492 0.45508v5.2852l-2.7344-1.709a0.5 0.5 0 0 0-0.68945 0.1582 0.5 0.5 0 0 0 0.1582 0.68945l3.2656 2.041v0.07031l-3.2656 2.041a0.5 0.5 0 0 0-0.1582 0.68945 0.5 0.5 0 0 0 0.68945 0.1582l2.7344-1.709v5.2852a0.50005 0.50005 0 0 0 0.83203 0.37305l4.5-4a0.50005 0.50005 0 0 0-0.06641-0.79688l-3.3223-2.0762 3.3223-2.0762a0.50005 0.50005 0 0 0 0.06641-0.79688l-4.5-4a0.50005 0.50005 0 0 0-0.53711-0.08203zm0.70508 1.5684 3.1699 2.8164-3.1699 1.9805zm0 5.9766 3.1699 1.9805-3.1699 2.8164z" fill="currentColor" opacity=".35"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 943 B |
@@ -0,0 +1,7 @@
|
||||
<svg width="16" height="16" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<style id="current-color-scheme" type="text/css">.ColorScheme-Text { color:#dedede; } .ColorScheme-Highlight { color:#5294e2; }</style>
|
||||
</defs>
|
||||
<path d="m7.4805 1.002a0.50005 0.50005 0 0 0-0.18555 0.042969 0.50005 0.50005 0 0 0-0.29492 0.45508v5.2852l-2.7344-1.709a0.5 0.5 0 0 0-0.68945 0.1582 0.5 0.5 0 0 0 0.1582 0.68945l3.2656 2.041v0.070312l-3.2656 2.041a0.5 0.5 0 0 0-0.1582 0.68945 0.5 0.5 0 0 0 0.68945 0.1582l2.7344-1.709v5.2852a0.50005 0.50005 0 0 0 0.83203 0.37305l1.168-1.0391v-0.83398c0-0.21977 0.042063-0.42278 0.11719-0.60742l-1.1172 0.99414v-4.7969l2.7383 1.7109c0.18696-0.29042 0.41455-0.54722 0.67969-0.75391l-2.4746-1.5469 3.3223-2.0762a0.50005 0.50005 0 0 0 0.066406-0.79688l-4.5-4a0.50005 0.50005 0 0 0-0.35156-0.125zm0.51953 1.6113 3.1699 2.8164-3.1699 1.9805v-4.7969z" class="ColorScheme-Text" fill="currentColor"/>
|
||||
<path d="m13 10c-1.108 0-2 0.892-2 2-0.554 0-1 0.446-1 1v2c0 0.554 0.446 1 1 1h4c0.554 0 1-0.446 1-1v-2c0-0.554-0.446-1-1-1 0-1.108-0.892-2-2-2zm0 1c0.554 0 1 0.446 1 1h-2c0-0.554 0.446-1 1-1z" class="ColorScheme-Text" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -1,6 +1,8 @@
|
||||
<svg width="24" height="24" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<style id="current-color-scheme" type="text/css">.ColorScheme-Text { color:#dedede; }</style>
|
||||
</defs>
|
||||
<path class="ColorScheme-Text" d="m12 7.0206c-2.6432 0-5.1397 1.1665-6.85 3.2009a0.63845 0.63845 0 0 0 0.49005 1.0491c0.18222 0 0.36305-0.0773 0.48971-0.22742 1.4657-1.7438 3.6057-2.7446 5.8703-2.7446 2.2646 0 4.4046 1.0012 5.8703 2.7446 0.12631 0.15116 0.30714 0.22777 0.48971 0.22777a0.63776 0.63776 0 0 0 0.4897-1.0495c-1.7097-2.0344-4.2068-3.2009-6.8504-3.2009zm0 2.707c-1.8877 0-3.6709 0.83378-4.8922 2.2863a0.63894 0.63894 0 1 0 0.97838 0.82204 5.1055 5.1055 0 0 1 3.9135-1.8301c1.5102 0 2.9369 0.66744 3.9142 1.8301 0.12631 0.15116 0.30784 0.22777 0.48971 0.22777a0.63845 0.63845 0 0 0 0.48833-1.0495c-1.2217-1.4529-3.0038-2.2867-4.8922-2.2867zm0 2.8126a3.8272 3.8272 0 0 0-2.9358 1.3704 0.63845 0.63845 0 0 0 0.079374 0.90038 0.63603 0.63603 0 0 0 0.90038-0.07765 2.5507 2.5507 0 0 1 1.9561-0.91453c0.75475 0 1.4677 0.33303 1.9568 0.91453a0.63707 0.63707 0 0 0 0.90004 0.07765 0.63845 0.63845 0 0 0 0.07937-0.90038 3.8272 3.8272 0 0 0-2.9362-1.3704zm-0.05452 2.4264c-0.49971 0.04832-0.98079 0.42172-0.99736 0.97838a1.0522 1.0522 0 0 0 2.1041 0c-0.08869-0.74716-0.6067-1.026-1.1068-0.97838z" fill="currentColor"/>
|
||||
<svg width="16" height="16" version="1.1" xmlns="http://www.w3.org/2000/svg">
|
||||
<g fill="#dedede">
|
||||
<path d="m8 3c-2.8405-3e-7 -5.4324 1.0071-7.4863 2.6582a0.5 0.5 0 0 0-0.095703 0.072266 0.5 0.5 0 0 0-0.1543 0.35938 0.5 0.5 0 0 0 0.5 0.5 0.5 0.5 0 0 0 0.31836-0.11328c1.891-1.5388 4.2898-2.4766 6.918-2.4766 2.6282-3e-7 5.0269 0.93776 6.918 2.4766a0.5 0.5 0 0 0 0.31836 0.11328 0.5 0.5 0 0 0 0.5-0.5 0.5 0.5 0 0 0-0.1543-0.35938 0.5 0.5 0 0 0-0.095703-0.072266c-2.0539-1.6511-4.6459-2.6582-7.4863-2.6582z" fill-rule="evenodd" style="paint-order:stroke fill markers"/>
|
||||
<path d="m8 9c-1.3525-2e-7 -2.5832 0.46891-3.5859 1.2246-0.11034 0.09497-0.16795 0.23918-0.16797 0.38477 0 0.27614 0.22386 0.5 0.5 0.5 0.12436-3.26e-4 0.24413-0.04698 0.33594-0.13086 0.82215-0.60306 1.8199-0.97852 2.918-0.97852 1.0981-2e-7 2.0958 0.37546 2.918 0.97852 0.091809 0.083879 0.21158 0.13053 0.33594 0.13086 0.27614 0 0.5-0.22386 0.5-0.5-1.6e-5 -0.14559-0.057625-0.2898-0.16797-0.38477-1.0027-0.7557-2.2334-1.2246-3.5859-1.2246z"/>
|
||||
<path d="m8 6c-2.0731 0-3.9681 0.71726-5.4883 1.8965a0.5 0.5 0 0 0-0.10156 0.074219 0.5 0.5 0 0 0-0.15625 0.36133 0.5 0.5 0 0 0 0.5 0.5 0.5 0.5 0 0 0 0.32031-0.11523c1.3588-1.0688 3.0627-1.7168 4.9258-1.7168 1.8631-2e-7 3.567 0.64796 4.9258 1.7168a0.5 0.5 0 0 0 0.32031 0.11523 0.5 0.5 0 0 0 0.5-0.5 0.5 0.5 0 0 0-0.15625-0.36133 0.5 0.5 0 0 0-0.10156-0.074219c-1.5202-1.1792-3.4152-1.8965-5.4883-1.8965z" fill-rule="evenodd" style="paint-order:stroke fill markers"/>
|
||||
<circle cx="8" cy="13" r="1" fill-rule="evenodd" style="paint-order:stroke fill markers"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
@@ -1,5 +1 @@
|
||||
<svg viewBox="-4 -4 32 32" width="24" height="24" stroke="#fff" stroke-width="2" fill="none"
|
||||
stroke-linecap="round" stroke-linejoin="round" class="css-i6dzq1">
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M7.452 2C4.446 2 2 4.439 2 7.435c0 2.997 2.446 5.436 5.452 5.436 1.31 0 2.514-.464 3.455-1.235l3.547 3.537.62-.619-3.536-3.525a5.403 5.403 0 0 0 1.365-3.594C12.903 4.44 10.457 2 7.452 2Zm0 .875a4.561 4.561 0 1 1 0 9.121 4.561 4.561 0 1 1 0-9.121z" style="fill:#dedede;fill-opacity:1"/></svg>
|
||||
|
Before Width: | Height: | Size: 261 B After Width: | Height: | Size: 363 B |
|
After Width: | Height: | Size: 11 KiB |
@@ -1,85 +1,98 @@
|
||||
{
|
||||
"launcher_config": {
|
||||
"em": {
|
||||
"icon": "emoji",
|
||||
"description": "Emoji - Search and copy emojis"
|
||||
},
|
||||
"clip": {
|
||||
"icon": "clipboard",
|
||||
"description": "Clipboard - Search and manage clipboard history"
|
||||
},
|
||||
"=": {
|
||||
"examples": ["= 10*5", "= 2^8", "= sin(30)"],
|
||||
"icon": "calculator",
|
||||
"description": "Quick Math - Fast mathematical expressions"
|
||||
},
|
||||
"app": {
|
||||
"icon": "apps",
|
||||
"description": "Applications - Launch installed applications"
|
||||
},
|
||||
"bin": {
|
||||
"icon": "terminal",
|
||||
"description": "Bins - Search and run executable binaries"
|
||||
},
|
||||
"power": {
|
||||
"icon": "shutdown",
|
||||
"description": "Power - System power management and session control"
|
||||
},
|
||||
"caffeine": {
|
||||
"examples": ["caffeine 30m", "caffeine 1h", "caffeine 2h", "caffeine on"],
|
||||
"icon": "coffee",
|
||||
"description": "Caffeine - Prevent system from going idle"
|
||||
},
|
||||
"sc": {
|
||||
"icon": "toolbox",
|
||||
"description": "Screencapture - Record screen and audio"
|
||||
},
|
||||
"wall": {
|
||||
"icon": "wallpapers",
|
||||
"description": "Wallpapers - Set wallpapers, and tons of features"
|
||||
},
|
||||
"?": {
|
||||
"icon": "world",
|
||||
"description": "Quick Web Search - Fast web search with question mark",
|
||||
"examples": ["? cats", "? google cats", "? youtube music"]
|
||||
},
|
||||
"remind": {
|
||||
"icon": "timer_on",
|
||||
"description": "Reminders - Set time-based reminders with notifications"
|
||||
},
|
||||
"otp": {
|
||||
"icon": "auth",
|
||||
"description": "Manage TOTP codes and 2FA authentication"
|
||||
},
|
||||
"pass": {
|
||||
"icon": "key",
|
||||
"description": "Password Manager - Search and manage passwords"
|
||||
},
|
||||
"bm": {
|
||||
"icon": "bookmark",
|
||||
"description": "Bookmarks - Search and manage bookmarks"
|
||||
},
|
||||
"script": {
|
||||
"icon": "terminal",
|
||||
"description": "Bash Scripts - Manage and execute bash scripts"
|
||||
},
|
||||
"bash": {
|
||||
"icon": "terminal",
|
||||
"description": "Bash Scripts - Manage and execute bash scripts"
|
||||
},
|
||||
"sh": {
|
||||
"icon": "terminal",
|
||||
"description": "Bash Scripts - Manage and execute bash scripts"
|
||||
},
|
||||
"tmux": {
|
||||
"icon": "terminal",
|
||||
"description": "Tmux Manager - Create, attach, rename, and kill tmux sessions"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"max_examples_shown": 2,
|
||||
"default_icon": "apps",
|
||||
"fallback_example_template": "{trigger} <search>",
|
||||
"config_version": "1.0"
|
||||
}
|
||||
"launcher_config": {
|
||||
"em": {
|
||||
"icon": "emoji-people-symbolic",
|
||||
"description": "Emoji - Search and copy emojis"
|
||||
},
|
||||
"clip": {
|
||||
"icon": "app.getclipboard.Clipboard",
|
||||
"description": "Clipboard - Search and manage clipboard history"
|
||||
},
|
||||
"=": {
|
||||
"examples": [
|
||||
"= 10*5",
|
||||
"= 2^8",
|
||||
"= sin(30)"
|
||||
],
|
||||
"icon": "calculator",
|
||||
"description": "Quick Math - Fast mathematical expressions"
|
||||
},
|
||||
"app": {
|
||||
"icon": "apps",
|
||||
"description": "Applications - Launch installed applications"
|
||||
},
|
||||
"bin": {
|
||||
"icon": "terminal",
|
||||
"description": "Bins - Search and run executable binaries"
|
||||
},
|
||||
"power": {
|
||||
"icon": "shutdown",
|
||||
"description": "Power - System power management and session control"
|
||||
},
|
||||
"caffeine": {
|
||||
"examples": [
|
||||
"caffeine 30m",
|
||||
"caffeine 1h",
|
||||
"caffeine 2h",
|
||||
"caffeine on"
|
||||
],
|
||||
"icon": "caffeine",
|
||||
"description": "Caffeine - Prevent system from going idle"
|
||||
},
|
||||
"sc": {
|
||||
"icon": "cs-screen",
|
||||
"description": "Screencapture - Record screen and audio"
|
||||
},
|
||||
"wall": {
|
||||
"icon": "daily-wallpaper",
|
||||
"description": "Wallpapers - Set wallpapers, and tons of features"
|
||||
},
|
||||
"?": {
|
||||
"icon": "search-symbolic",
|
||||
"description": "Quick Web Search - Fast web search with question mark",
|
||||
"examples": [
|
||||
"? cats",
|
||||
"? google cats",
|
||||
"? youtube music"
|
||||
]
|
||||
},
|
||||
"remind": {
|
||||
"icon": "alarm-timer",
|
||||
"description": "Reminders - Set time-based reminders with notifications"
|
||||
},
|
||||
"otp": {
|
||||
"icon": "auth-otp-symbolic",
|
||||
"description": "Manage TOTP codes and 2FA authentication"
|
||||
},
|
||||
"pass": {
|
||||
"icon": "nextcloud-password-client",
|
||||
"description": "Password Manager - Search and manage passwords"
|
||||
},
|
||||
"bm": {
|
||||
"icon": "bookmark-add-symbolic",
|
||||
"description": "Bookmarks - Search and manage bookmarks"
|
||||
},
|
||||
"script": {
|
||||
"icon": "terminal",
|
||||
"description": "Bash Scripts - Manage and execute bash scripts"
|
||||
},
|
||||
"bash": {
|
||||
"icon": "terminal",
|
||||
"description": "Bash Scripts - Manage and execute bash scripts"
|
||||
},
|
||||
"sh": {
|
||||
"icon": "terminal",
|
||||
"description": "Bash Scripts - Manage and execute bash scripts"
|
||||
},
|
||||
"tmux": {
|
||||
"icon": "terminal",
|
||||
"description": "Tmux Manager - Create, attach, rename, and kill tmux sessions"
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"max_examples_shown": 2,
|
||||
"default_icon": "apps",
|
||||
"fallback_example_template": "{trigger} <search>",
|
||||
"config_version": "1.0"
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ tooltip {
|
||||
border-radius: 16px;
|
||||
border-color: var(--surface);
|
||||
background-color: var(--shadow);
|
||||
animation: tooltipShow 0.25s cubic-bezier(0.5, 0.25, 0, 1);
|
||||
/* animation: tooltipShow 0.25s cubic-bezier(0.5, 0.25, 0, 1); */
|
||||
}
|
||||
|
||||
@keyframes tooltipShow {
|
||||
@@ -71,3 +71,96 @@ menu > menuitem:hover {
|
||||
menu > menuitem:hover > label {
|
||||
color: var(--shadow);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#weather-widget label{
|
||||
font-family: "SF Pro Rounded";
|
||||
/* font-size: 20px; */
|
||||
font-weight: 600;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
#city {
|
||||
margin-top: 10px;
|
||||
font-size: 20px;
|
||||
|
||||
}
|
||||
#temperature {
|
||||
font-size: 50px;
|
||||
}
|
||||
|
||||
#condition-emoji{
|
||||
margin-top: 10px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
#condition {
|
||||
font-size: 15px;
|
||||
}
|
||||
#feels-like {
|
||||
|
||||
font-size: 15px;
|
||||
}
|
||||
/* # */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#date-widget label {
|
||||
font-family: "SF Pro Rounded";
|
||||
margin-top: 10px;
|
||||
}
|
||||
#day label,
|
||||
#month label{
|
||||
color: var(--foreground);
|
||||
padding: 5px;
|
||||
margin-top: 10px;
|
||||
font-size: 30px;
|
||||
color: alpha(#fff, 0.7);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
#date label{
|
||||
color: alpha(#fff, 0.7);
|
||||
margin-top: -10px;
|
||||
font-size: 90px;
|
||||
font-weight: 600;
|
||||
}
|
||||
#day label{
|
||||
color: alpha(#FF0000, 0.7);
|
||||
}
|
||||
#box-widget{
|
||||
background-color: alpha(#232323, 1);
|
||||
margin: 20px 20px 20px 20px;
|
||||
min-width: 170px;
|
||||
min-height: 170px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
#box-widget-2{
|
||||
background-color: alpha(#425D8A, 1);
|
||||
min-width: 170px;
|
||||
min-height: 170px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
from fabric.core import widgets
|
||||
import setproctitle
|
||||
from loguru import logger
|
||||
|
||||
@@ -7,6 +8,7 @@ from fabric.utils import get_relative_path, monitor_file
|
||||
from modules.dock import Dock
|
||||
from modules.launcher.main import Launcher
|
||||
from modules.notification.notification import ModusNoti
|
||||
from modules.widget import Deskwidgets
|
||||
from modules.osd import OSD
|
||||
|
||||
# from modules.corners import Corners
|
||||
@@ -41,6 +43,7 @@ if __name__ == "__main__":
|
||||
panel.launcher = launcher
|
||||
osd = OSD()
|
||||
|
||||
widgets = Deskwidgets()
|
||||
# Set corners visibility based on config
|
||||
# corners_visible = config.get("corners_visible", True)
|
||||
# corners.set_visible(corners_visible)
|
||||
@@ -50,7 +53,9 @@ if __name__ == "__main__":
|
||||
_ = css_file.connect("changed", lambda *_: set_css())
|
||||
|
||||
# Make sure corners is added to the app
|
||||
app = Application(f"{APP_NAME}", panel, dock, switcher, launcher, modusnoti, osd)
|
||||
app = Application(
|
||||
f"{APP_NAME}", panel, dock, switcher, launcher, modusnoti, osd, widgets
|
||||
)
|
||||
|
||||
def set_css():
|
||||
app.set_stylesheet_from_file(
|
||||
|
||||
@@ -88,7 +88,6 @@ class Player(Box):
|
||||
f"background-image:url('{data.HOME_DIR}/.current.wall')"
|
||||
)
|
||||
|
||||
|
||||
self.children = [
|
||||
self.album_cover,
|
||||
Box(
|
||||
@@ -201,9 +200,6 @@ class Player(Box):
|
||||
return
|
||||
self.duration = dur
|
||||
|
||||
|
||||
|
||||
|
||||
def on_seek(self, sender, ratio):
|
||||
pos = ratio * self.duration # duration in seconds
|
||||
self._player.set_position(int(pos))
|
||||
@@ -269,7 +265,7 @@ class Player(Box):
|
||||
|
||||
self._is_updating = True
|
||||
update_func, args, kwargs = self._update_queue.pop(0)
|
||||
|
||||
|
||||
try:
|
||||
update_func(*args, **kwargs)
|
||||
except Exception as e:
|
||||
@@ -284,13 +280,14 @@ class Player(Box):
|
||||
"""Finish current update and process next item in queue."""
|
||||
self._is_updating = False
|
||||
self._update_timeout_id = None
|
||||
|
||||
|
||||
if self._update_queue:
|
||||
self._process_queue()
|
||||
return False
|
||||
|
||||
def _handle_thumbnail(self, metadata, player):
|
||||
"""Queue thumbnail update to prevent overlapping operations."""
|
||||
|
||||
def _update_thumbnail():
|
||||
keys = metadata.keys()
|
||||
player_name = player.props.player_name
|
||||
@@ -305,16 +302,24 @@ class Player(Box):
|
||||
logger.debug(f"Found MPRIS art for {player_name}: {art_url}")
|
||||
|
||||
# Check for browser players and YouTube URLs
|
||||
elif player_name in ["firefox", "chromium", "vivaldi", "brave"] and "xesam:url" in keys:
|
||||
elif (
|
||||
player_name in ["firefox", "chromium", "vivaldi", "brave"]
|
||||
and "xesam:url" in keys
|
||||
):
|
||||
webpage_url = metadata["xesam:url"]
|
||||
if webpage_url and "youtube.com" in webpage_url:
|
||||
# Simple YouTube thumbnail extraction
|
||||
import re
|
||||
|
||||
match = re.search(r"(?:v=|/)([a-zA-Z0-9_-]{11})", webpage_url)
|
||||
if match:
|
||||
video_id = match.group(1)
|
||||
new_thumbnail = f"https://img.youtube.com/vi/{video_id}/hqdefault.jpg"
|
||||
logger.debug(f"Found YouTube thumbnail for {player_name}: {new_thumbnail}")
|
||||
new_thumbnail = (
|
||||
f"https://img.youtube.com/vi/{video_id}/hqdefault.jpg"
|
||||
)
|
||||
logger.debug(
|
||||
f"Found YouTube thumbnail for {player_name}: {new_thumbnail}"
|
||||
)
|
||||
|
||||
# Only update if thumbnail has changed
|
||||
if new_thumbnail and new_thumbnail != self._current_thumbnail:
|
||||
@@ -324,20 +329,20 @@ class Player(Box):
|
||||
elif not new_thumbnail and self._current_thumbnail:
|
||||
# Reset to default if no thumbnail found
|
||||
self._current_thumbnail = None
|
||||
self.album_cover.set_style(f"background-image:url('{data.HOME_DIR}/.current.wall')")
|
||||
self.album_cover.set_style(
|
||||
f"background-image:url('{data.HOME_DIR}/.current.wall')"
|
||||
)
|
||||
logger.debug(f"Reset to default wallpaper for {player_name}")
|
||||
|
||||
self._queue_update(_update_thumbnail)
|
||||
|
||||
|
||||
|
||||
def handle_next(self, player):
|
||||
def _do_next():
|
||||
try:
|
||||
self._player._player.next()
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to go to next track: {e}")
|
||||
|
||||
|
||||
self._queue_update(_do_next)
|
||||
|
||||
def handle_prev(self, player):
|
||||
@@ -346,11 +351,9 @@ class Player(Box):
|
||||
self._player._player.previous()
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to go to previous track: {e}")
|
||||
|
||||
|
||||
self._queue_update(_do_prev)
|
||||
|
||||
|
||||
|
||||
def handle_play_pause(self, player):
|
||||
def _do_play_pause():
|
||||
is_playing = (
|
||||
@@ -431,9 +434,7 @@ class Placeholder(Box):
|
||||
children=[
|
||||
CenterBox(
|
||||
name="details",
|
||||
center_children=Label(
|
||||
label="Nothing Playing", style="color:black;"
|
||||
),
|
||||
center_children=Label(label="Nothing Playing"),
|
||||
)
|
||||
],
|
||||
),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import json
|
||||
import re
|
||||
from typing import List
|
||||
import subprocess
|
||||
|
||||
from fabric.utils import DesktopApp
|
||||
from fabric.utils.helpers import get_desktop_applications, get_relative_path
|
||||
@@ -155,8 +156,21 @@ class ApplicationsPlugin(PluginBase):
|
||||
pattern = ".*".join(re.escape(char) for char in query)
|
||||
return bool(re.search(pattern, text, re.IGNORECASE))
|
||||
|
||||
import subprocess
|
||||
|
||||
def _launch_application(self, app: DesktopApp):
|
||||
app.launch()
|
||||
print("Original:", app.command_line)
|
||||
|
||||
# Remove ALL % codes (e.g., %u, %U, %f, %F, %i, %c, etc.)
|
||||
cleaned_command = re.sub(r"%\w+", "", app.command_line).strip()
|
||||
|
||||
print("Cleaned:", cleaned_command)
|
||||
|
||||
# Final command with hyprctl dispatch
|
||||
final_command = f"hyprctl dispatch exec 'uwsm app -- {cleaned_command}'"
|
||||
subprocess.Popen(final_command, shell=True)
|
||||
|
||||
# app.launch()
|
||||
|
||||
def _get_all_applications(self) -> List[Result]:
|
||||
"""Get a list of all available applications."""
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import os
|
||||
|
||||
from fabric.utils import get_relative_path
|
||||
from gi.repository import Gdk, GdkPixbuf, GLib, Gtk # type: ignore
|
||||
from loguru import logger
|
||||
|
||||
@@ -103,7 +104,7 @@ class NotificationWidget(Box):
|
||||
if "file://" in notification.app_icon
|
||||
else Image(
|
||||
name="notification-icon",
|
||||
icon_name="dialog-information-symbolic" or notification.app_icon,
|
||||
icon_name="notifications" or notification.app_icon,
|
||||
icon_size=24,
|
||||
)
|
||||
)
|
||||
@@ -175,31 +176,24 @@ class NotificationWidget(Box):
|
||||
ellipsization="end",
|
||||
)
|
||||
if notification.body
|
||||
else Box()
|
||||
else Label(
|
||||
markup="",
|
||||
h_align="start",
|
||||
ellipsization="end",
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
Box(h_expand=True),
|
||||
Box(
|
||||
orientation="v",
|
||||
children=[
|
||||
self.create_close_button() if self.show_close_button else Box(),
|
||||
Box(v_expand=True),
|
||||
],
|
||||
Button(
|
||||
name="notification-close-button",
|
||||
image=CustomImage(icon_name="close-symbolic", icon_size=18),
|
||||
visible=True, # Initially hidden
|
||||
on_clicked=lambda *_: self.notification.close("dismissed-by-user"),
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
def create_close_button(self):
|
||||
if self.close_button is None:
|
||||
self.close_button = Button(
|
||||
name="notification-close-button",
|
||||
visible=False, # Initially hidden
|
||||
on_clicked=lambda *_: self.notification.close("dismissed-by-user"),
|
||||
child=Label(label="×", name="close-button-label"),
|
||||
)
|
||||
return self.close_button
|
||||
|
||||
def _on_enter_notify(self, widget, event):
|
||||
self._is_hovered = True
|
||||
if self.close_button:
|
||||
@@ -215,12 +209,12 @@ class NotificationWidget(Box):
|
||||
return False
|
||||
|
||||
def get_pixbuf(self, icon_path, width, height):
|
||||
if icon_path.startswith("file://"):
|
||||
if icon_path.startswith("file://") and os.path.exists(icon_path):
|
||||
icon_path = icon_path[7:]
|
||||
|
||||
if not os.path.exists(icon_path):
|
||||
logger.warning(f"Icon path does not exist: {icon_path}")
|
||||
return None
|
||||
icon_path = get_relative_path("../../config/assets/icons/notification.png")
|
||||
|
||||
try:
|
||||
pixbuf = GdkPixbuf.Pixbuf.new_from_file(icon_path)
|
||||
@@ -297,18 +291,20 @@ class NotificationRevealer(SlideRevealer):
|
||||
self._is_closing = False
|
||||
|
||||
# Enhanced swipe detection variables for Android-style animation
|
||||
self._drag_start_x = 0
|
||||
self._drag_start_y = 0
|
||||
self._drag_start_x = 0
|
||||
self._is_dragging = False
|
||||
self._swipe_threshold = 80 # Distance to trigger auto-dismiss
|
||||
self._swipe_velocity_threshold = 150 # Velocity to trigger dismiss even on shorter swipes
|
||||
self._swipe_velocity_threshold = (
|
||||
150 # Velocity to trigger dismiss even on shorter swipes
|
||||
)
|
||||
self._swipe_in_progress = False
|
||||
self._current_offset = 0
|
||||
self._last_drag_time = 0
|
||||
self._drag_velocity = 0
|
||||
self._spring_back_duration = 200 # Duration for spring-back animation
|
||||
self._dismiss_threshold = 0.3 # Dismiss if swiped 30% of width
|
||||
|
||||
|
||||
# Animation state
|
||||
self._animation_in_progress = False
|
||||
self._spring_timer_id = None
|
||||
@@ -324,15 +320,10 @@ class NotificationRevealer(SlideRevealer):
|
||||
child=self.notif_box,
|
||||
)
|
||||
|
||||
self.event_box.connect("button-press-event", self._on_button_press)
|
||||
self.event_box.connect("button-release-event", self._on_button_release)
|
||||
self.event_box.connect("motion-notify-event", self._on_motion)
|
||||
self.event_box.connect("realize", self._on_realize)
|
||||
|
||||
super().__init__(
|
||||
child=self.event_box,
|
||||
direction="right",
|
||||
duration=350, # Optimized duration for macOS-like feel
|
||||
duration=600, # Optimized duration for macOS-like feel
|
||||
)
|
||||
|
||||
smooth_revealer_animation(self)
|
||||
@@ -340,90 +331,7 @@ class NotificationRevealer(SlideRevealer):
|
||||
# Connect our own handler that manages the slide animation
|
||||
self.notification.connect("closed", self.on_resolved)
|
||||
|
||||
def _on_realize(self, widget):
|
||||
"""Setup CSS provider when widget is realized"""
|
||||
try:
|
||||
self._css_provider = Gtk.CssProvider()
|
||||
context = widget.get_style_context()
|
||||
context.add_provider(self._css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not setup CSS provider for notification animation: {e}")
|
||||
|
||||
def _apply_transform(self, offset_x, opacity=1.0, scale=1.0):
|
||||
"""Apply transform to the notification widget for smooth animation"""
|
||||
if not self.event_box or not self.event_box.get_realized():
|
||||
return
|
||||
|
||||
# Clamp values
|
||||
offset_x = max(0, offset_x) # Only allow rightward movement
|
||||
opacity = max(0.1, min(1.0, opacity)) # Keep some visibility
|
||||
scale = max(0.8, min(1.0, scale)) # Subtle scale effect
|
||||
|
||||
try:
|
||||
if self._css_provider:
|
||||
# Apply CSS transform using the provider
|
||||
css_data = f"""
|
||||
* {{
|
||||
margin-left: {int(offset_x)}px;
|
||||
opacity: {opacity};
|
||||
transform: scale({scale});
|
||||
}}
|
||||
"""
|
||||
self._css_provider.load_from_data(css_data.encode('utf-8'))
|
||||
else:
|
||||
# Fallback: use widget margins for offset
|
||||
current_margin = self.event_box.get_margin_start()
|
||||
if current_margin != int(offset_x):
|
||||
self.event_box.set_margin_start(int(offset_x))
|
||||
|
||||
# Set opacity directly on the widget
|
||||
self.event_box.set_opacity(opacity)
|
||||
|
||||
except Exception as e:
|
||||
# Final fallback: just use margin
|
||||
try:
|
||||
self.event_box.set_margin_start(int(offset_x))
|
||||
self.event_box.set_opacity(opacity)
|
||||
except:
|
||||
pass
|
||||
|
||||
def _animate_spring_back(self, start_offset, target_offset=0, duration=None):
|
||||
"""Animate the notification springing back to its original position"""
|
||||
if duration is None:
|
||||
duration = self._spring_back_duration
|
||||
|
||||
if self._spring_timer_id:
|
||||
GLib.source_remove(self._spring_timer_id)
|
||||
|
||||
start_time = GLib.get_monotonic_time() / 1000
|
||||
offset_diff = target_offset - start_offset
|
||||
|
||||
def animate_step():
|
||||
current_time = GLib.get_monotonic_time() / 1000
|
||||
elapsed = current_time - start_time
|
||||
progress = min(1.0, elapsed / duration)
|
||||
|
||||
# Use easing function for spring-like animation
|
||||
eased_progress = self._ease_out_back(progress)
|
||||
current_offset = start_offset + (offset_diff * eased_progress)
|
||||
|
||||
# Calculate opacity and scale based on offset
|
||||
max_offset = NOTIFICATION_WIDTH * 0.8
|
||||
offset_ratio = abs(current_offset) / max_offset
|
||||
opacity = 1.0 - (offset_ratio * 0.3) # Fade slightly
|
||||
scale = 1.0 - (offset_ratio * 0.05) # Slight scale down
|
||||
|
||||
self._apply_transform(current_offset, opacity, scale)
|
||||
|
||||
if progress >= 1.0:
|
||||
self._animation_in_progress = False
|
||||
self._current_offset = target_offset
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
self._animation_in_progress = True
|
||||
self._spring_timer_id = GLib.timeout_add(16, animate_step) # ~60fps
|
||||
|
||||
def _ease_out_back(self, t):
|
||||
"""Easing function for spring-back animation"""
|
||||
@@ -435,28 +343,30 @@ class NotificationRevealer(SlideRevealer):
|
||||
"""Animate the notification sliding out completely - now much faster"""
|
||||
target_offset = NOTIFICATION_WIDTH + 50 # Slide completely out of view
|
||||
duration = 120 # Much faster dismiss animation (was 250)
|
||||
|
||||
|
||||
if self._spring_timer_id:
|
||||
GLib.source_remove(self._spring_timer_id)
|
||||
|
||||
|
||||
start_time = GLib.get_monotonic_time() / 1000
|
||||
offset_diff = target_offset - start_offset
|
||||
|
||||
|
||||
def animate_step():
|
||||
current_time = GLib.get_monotonic_time() / 1000
|
||||
elapsed = current_time - start_time
|
||||
progress = min(1.0, elapsed / duration)
|
||||
|
||||
|
||||
# Use faster easing for snappier exit
|
||||
eased_progress = progress * progress # Quadratic ease out - faster than cubic
|
||||
eased_progress = (
|
||||
progress * progress
|
||||
) # Quadratic ease out - faster than cubic
|
||||
current_offset = start_offset + (offset_diff * eased_progress)
|
||||
|
||||
|
||||
# Fade out and scale down during dismiss
|
||||
opacity = 1.0 - (progress * 1.2) # Faster fade out
|
||||
scale = 1.0 - (progress * 0.15) # Slightly more scale change
|
||||
|
||||
scale = 1.0 - (progress * 0.15) # Slightly more scale change
|
||||
|
||||
self._apply_transform(current_offset, opacity, scale)
|
||||
|
||||
|
||||
if progress >= 1.0:
|
||||
# Trigger the actual notification dismissal immediately
|
||||
try:
|
||||
@@ -464,22 +374,24 @@ class NotificationRevealer(SlideRevealer):
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
|
||||
|
||||
return True
|
||||
|
||||
|
||||
self._animation_in_progress = True
|
||||
self._spring_timer_id = GLib.timeout_add(12, animate_step) # Higher fps for smoother animation
|
||||
self._spring_timer_id = GLib.timeout_add(
|
||||
12, animate_step
|
||||
) # Higher fps for smoother animation
|
||||
|
||||
def _calculate_drag_velocity(self, current_x):
|
||||
"""Calculate the velocity of the drag gesture"""
|
||||
current_time = GLib.get_monotonic_time() / 1000
|
||||
|
||||
|
||||
if self._last_drag_time > 0:
|
||||
time_diff = current_time - self._last_drag_time
|
||||
if time_diff > 0:
|
||||
distance_diff = current_x - self._drag_start_x - self._current_offset
|
||||
self._drag_velocity = abs(distance_diff / time_diff)
|
||||
|
||||
|
||||
self._last_drag_time = current_time
|
||||
|
||||
def _on_animation_complete(self, is_hiding=False):
|
||||
@@ -523,109 +435,6 @@ class NotificationRevealer(SlideRevealer):
|
||||
timeout_duration = 80 if self._swipe_in_progress else (self.duration + 30)
|
||||
GLib.timeout_add(timeout_duration, lambda: self._on_animation_complete(True))
|
||||
|
||||
def _on_button_press(self, _widget, event):
|
||||
if event.button == 1:
|
||||
self._drag_start_x = event.x
|
||||
self._drag_start_y = event.y
|
||||
self._is_dragging = True
|
||||
self._swipe_in_progress = False
|
||||
self._current_offset = 0
|
||||
self._last_drag_time = GLib.get_monotonic_time() / 1000
|
||||
self._drag_velocity = 0
|
||||
|
||||
# Stop any ongoing animations
|
||||
if self._spring_timer_id:
|
||||
GLib.source_remove(self._spring_timer_id)
|
||||
self._animation_in_progress = False
|
||||
return False
|
||||
|
||||
def _on_button_release(self, _widget, event):
|
||||
if event.button == 3: # Right click
|
||||
try:
|
||||
if self.parent_window:
|
||||
self.parent_window.clear_notification_queue()
|
||||
# Also dismiss current notification
|
||||
self.notification.close("dismissed-by-user")
|
||||
except:
|
||||
pass # Ignore errors
|
||||
return True
|
||||
|
||||
elif self._is_dragging and event.button == 1:
|
||||
self._is_dragging = False
|
||||
|
||||
# Calculate final swipe metrics
|
||||
dx = event.x - self._drag_start_x
|
||||
dy = abs(event.y - self._drag_start_y)
|
||||
|
||||
# Calculate dismiss threshold based on notification width
|
||||
dismiss_distance = NOTIFICATION_WIDTH * self._dismiss_threshold
|
||||
|
||||
# Determine if we should dismiss or spring back
|
||||
should_dismiss = (
|
||||
(dx > dismiss_distance and dy < 60) or # Dragged far enough
|
||||
(dx > 30 and self._drag_velocity > self._swipe_velocity_threshold and dy < 60) # Fast swipe
|
||||
)
|
||||
|
||||
if should_dismiss:
|
||||
try:
|
||||
self._swipe_in_progress = True
|
||||
# For fast dismissal, if already dragged far enough, close immediately
|
||||
if dx > NOTIFICATION_WIDTH * 0.6:
|
||||
# Skip animation and close immediately for very large swipes
|
||||
self.notification.close("dismissed-by-user")
|
||||
else:
|
||||
# Animate the dismiss for smaller swipes
|
||||
self._animate_dismiss(self._current_offset)
|
||||
logger.debug(f"Notification dismissed by swipe: dx={dx}, velocity={self._drag_velocity}")
|
||||
except Exception as e:
|
||||
logger.error(f"Error dismissing notification by swipe: {e}")
|
||||
else:
|
||||
# Spring back to original position
|
||||
if abs(self._current_offset) > 5: # Only animate if there's noticeable displacement
|
||||
self._animate_spring_back(self._current_offset, 0)
|
||||
else:
|
||||
self._apply_transform(0, 1.0, 1.0)
|
||||
self._current_offset = 0
|
||||
return False
|
||||
|
||||
def _on_motion(self, _widget, event):
|
||||
if self._is_dragging and not self._animation_in_progress:
|
||||
# Calculate current swipe distance
|
||||
dx = event.x - self._drag_start_x
|
||||
dy = abs(event.y - self._drag_start_y)
|
||||
|
||||
# Only respond to primarily horizontal gestures
|
||||
if dy < 60: # Vertical tolerance
|
||||
# Calculate velocity for smooth interaction
|
||||
self._calculate_drag_velocity(event.x)
|
||||
|
||||
# Apply real-time transform - only allow rightward movement
|
||||
if dx > 0:
|
||||
self._current_offset = dx
|
||||
|
||||
# Calculate visual feedback based on drag distance
|
||||
max_offset = NOTIFICATION_WIDTH * 0.8
|
||||
offset_ratio = min(1.0, dx / max_offset)
|
||||
|
||||
# Apply diminishing returns for large swipes
|
||||
adjusted_offset = dx * (1.0 - offset_ratio * 0.3)
|
||||
|
||||
# Calculate opacity and scale
|
||||
opacity = 1.0 - (offset_ratio * 0.4) # Fade as it's dragged
|
||||
scale = 1.0 - (offset_ratio * 0.1) # Slight scale down
|
||||
|
||||
self._apply_transform(adjusted_offset, opacity, scale)
|
||||
|
||||
# Visual indication of dismiss threshold
|
||||
if dx > NOTIFICATION_WIDTH * self._dismiss_threshold:
|
||||
# Could add visual cue here (like changing color)
|
||||
pass
|
||||
else:
|
||||
# Reset to original position if dragging left
|
||||
self._current_offset = 0
|
||||
self._apply_transform(0, 1.0, 1.0)
|
||||
return False
|
||||
|
||||
def destroy(self):
|
||||
# Clean up CSS provider and timers
|
||||
if self._spring_timer_id:
|
||||
|
||||
@@ -15,7 +15,7 @@ class BluetoothIndicator(Box):
|
||||
self.bluetooth = BluetoothClient()
|
||||
self.bt_icon = Svg(
|
||||
name="bt-icon",
|
||||
size=23,
|
||||
size=18,
|
||||
svg_file=get_relative_path(
|
||||
"../../../config/assets/icons/applets/bluetooth-clear.svg"
|
||||
),
|
||||
@@ -49,7 +49,12 @@ class BluetoothIndicator(Box):
|
||||
"../../../config/assets/icons/applets/bluetooth-clear.svg"
|
||||
)
|
||||
)
|
||||
if len(connected_devices) == 1:
|
||||
if len(connected_devices) >= 1:
|
||||
self.bt_icon.set_from_file(
|
||||
get_relative_path(
|
||||
"../../../config/assets/icons/applets/bluetooth-paired.svg"
|
||||
)
|
||||
)
|
||||
device = connected_devices[0]
|
||||
tooltip = f"Connected to {device.alias}"
|
||||
if device.battery_percentage > 0:
|
||||
@@ -111,7 +116,7 @@ class NetworkIndicator(Box):
|
||||
|
||||
self.network_icon = Svg(
|
||||
name="network-icon",
|
||||
size=25,
|
||||
size=18,
|
||||
svg_file=get_relative_path(
|
||||
"../../../config/assets/icons/applets/wifi-clear.svg"
|
||||
),
|
||||
@@ -236,7 +241,7 @@ class BatteryIndicator(Box):
|
||||
|
||||
self.battery_icon = Svg(
|
||||
name="battery-icon",
|
||||
size=22,
|
||||
size=23,
|
||||
svg_file=get_relative_path(
|
||||
"../../../config/assets/icons/battery/battery-100.svg"
|
||||
),
|
||||
|
||||
@@ -41,7 +41,7 @@ class Panel(Window):
|
||||
self.imac = Button(
|
||||
name="panel-button",
|
||||
child=Svg(
|
||||
size=23,
|
||||
size=21,
|
||||
svg_file=get_relative_path("../../config/assets/icons/misc/logo.svg"),
|
||||
),
|
||||
on_clicked=lambda *_: self.menubar.show_system_dropdown(self.imac),
|
||||
@@ -98,7 +98,7 @@ class Panel(Window):
|
||||
name="panel-button",
|
||||
on_clicked=lambda *_: self.search_apps(),
|
||||
child=Svg(
|
||||
size=22,
|
||||
size=18,
|
||||
svg_file=get_relative_path("../../config/assets/icons/misc/search.svg"),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -0,0 +1,542 @@
|
||||
import psutil
|
||||
from fabric.widgets.box import Box
|
||||
from fabric.widgets.label import Label
|
||||
from fabric.widgets.overlay import Overlay
|
||||
from fabric.widgets.datetime import DateTime
|
||||
from fabric.widgets.circularprogressbar import CircularProgressBar
|
||||
from widgets.wayland import WaylandWindow as Window
|
||||
from fabric.utils import invoke_repeater
|
||||
import requests
|
||||
import urllib.parse
|
||||
import datetime
|
||||
from gi.repository import GLib
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
import time
|
||||
import config.data as data
|
||||
from config.data import load_config
|
||||
import subprocess
|
||||
|
||||
executor = ThreadPoolExecutor(max_workers=4)
|
||||
|
||||
|
||||
config = load_config()
|
||||
|
||||
|
||||
def margin():
|
||||
return (
|
||||
data.DOCK_ICON_SIZE + 10
|
||||
if not data.DOCK_ALWAYS_OCCLUDED and data.DOCK_ENABLED
|
||||
else 0
|
||||
)
|
||||
|
||||
|
||||
def get_location():
|
||||
"""Fetch location from config file or IP API asynchronously."""
|
||||
for attempt in range(5):
|
||||
try:
|
||||
response = requests.get("https://ipinfo.io/json", timeout=3)
|
||||
if response.status_code == 200:
|
||||
return response.json().get("city", "").replace(" ", "")
|
||||
except requests.RequestException as e:
|
||||
print(f"Error getting location: {e}")
|
||||
if attempt < 4:
|
||||
time.sleep(10)
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
def get_location_async(callback):
|
||||
"""Fetch location asynchronously to prevent UI freeze."""
|
||||
executor.submit(lambda: GLib.idle_add(callback, get_location()))
|
||||
|
||||
|
||||
def get_weather(callback):
|
||||
"""Fetch weather data asynchronously and update UI."""
|
||||
|
||||
def fetch_weather():
|
||||
location = get_location()
|
||||
if not location:
|
||||
return GLib.idle_add(callback, None)
|
||||
|
||||
encoded_location = urllib.parse.quote(location)
|
||||
url = f"https://wttr.in/{encoded_location}?format=j1"
|
||||
urlemoji = f"https://wttr.in/{encoded_location}?format=%c"
|
||||
|
||||
for attempt in range(5):
|
||||
try:
|
||||
response = requests.get(urlemoji, timeout=3)
|
||||
responseinfo = requests.get(url, timeout=3).json()
|
||||
|
||||
if response.status_code == 200:
|
||||
temp_unit = "C"
|
||||
temp = (
|
||||
responseinfo["current_condition"][0][f"temp_{temp_unit}"] + "°"
|
||||
)
|
||||
condition = responseinfo["current_condition"][0]["weatherDesc"][0][
|
||||
"value"
|
||||
]
|
||||
maxtemp = responseinfo["weather"][0]["maxtempC"] + "°"
|
||||
mintemp = responseinfo["weather"][0]["mintempC"] + "°"
|
||||
location = responseinfo["nearest_area"][0]["areaName"][0]["value"]
|
||||
emoji = response.text.strip()
|
||||
|
||||
GLib.idle_add(
|
||||
callback,
|
||||
[
|
||||
emoji,
|
||||
temp,
|
||||
condition,
|
||||
location,
|
||||
maxtemp,
|
||||
mintemp,
|
||||
],
|
||||
)
|
||||
return
|
||||
except requests.RequestException as e:
|
||||
print(f"Error fetching weather (attempt {attempt + 1}): {e}")
|
||||
if attempt < 4:
|
||||
time.sleep(10)
|
||||
else:
|
||||
GLib.idle_add(callback, None)
|
||||
return
|
||||
|
||||
executor.submit(fetch_weather)
|
||||
|
||||
|
||||
def update_weather(widget):
|
||||
def fetch_and_update():
|
||||
get_weather(lambda weather_info: update_widget(widget, weather_info))
|
||||
return True
|
||||
|
||||
GLib.timeout_add_seconds(600, fetch_and_update)
|
||||
fetch_and_update()
|
||||
|
||||
|
||||
def update_widget(widget, weather_info):
|
||||
if weather_info:
|
||||
widget.weatherinfo = weather_info
|
||||
widget.update_labels(weather_info)
|
||||
|
||||
|
||||
class Sysinfo(Box):
|
||||
@staticmethod
|
||||
def bake_progress_bar(name: str = "progress-bar", size: int = 45, **kwargs):
|
||||
return CircularProgressBar(
|
||||
name=name,
|
||||
start_angle=180,
|
||||
end_angle=540,
|
||||
min_value=0,
|
||||
max_value=100,
|
||||
size=size,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def bake_progress_icon(**kwargs):
|
||||
return Label(**kwargs).build().add_style_class("progress-icon").unwrap()
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(
|
||||
layer="bottom",
|
||||
title="sysinfo",
|
||||
name="sysinfo",
|
||||
visible=False,
|
||||
all_visible=False,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
self.cpu_progress = self.bake_progress_bar()
|
||||
self.ram_progress = self.bake_progress_bar()
|
||||
self.bat_circular = self.bake_progress_bar().build().set_value(42).unwrap()
|
||||
|
||||
self.progress_container = Box(
|
||||
name="progress-bar-container",
|
||||
spacing=12,
|
||||
children=[
|
||||
Box(
|
||||
children=[
|
||||
Overlay(
|
||||
child=self.cpu_progress,
|
||||
tooltip_text="",
|
||||
overlays=[
|
||||
self.bake_progress_icon(
|
||||
label="",
|
||||
name="progress-icon-cpu",
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
Box(
|
||||
children=[
|
||||
Overlay(
|
||||
child=self.ram_progress,
|
||||
tooltip_text="",
|
||||
overlays=[
|
||||
self.bake_progress_icon(
|
||||
name="progress-icon-ram",
|
||||
label="",
|
||||
)
|
||||
],
|
||||
)
|
||||
]
|
||||
),
|
||||
Box(
|
||||
children=[
|
||||
Overlay(
|
||||
child=self.bat_circular,
|
||||
tooltip_text="",
|
||||
overlays=[
|
||||
self.bake_progress_icon(
|
||||
label="",
|
||||
name="progress-icon-bat",
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
self.update_status()
|
||||
invoke_repeater(1000, self.update_status)
|
||||
|
||||
self.add(
|
||||
Box(
|
||||
name="progress-bar-container-main",
|
||||
orientation="v",
|
||||
spacing=24,
|
||||
children=[self.progress_container],
|
||||
),
|
||||
)
|
||||
self.show_all()
|
||||
|
||||
def update_status(self):
|
||||
"""Update system info asynchronously to prevent UI lag."""
|
||||
|
||||
def update():
|
||||
cpu = psutil.cpu_percent()
|
||||
ram = psutil.virtual_memory().percent
|
||||
battery = (
|
||||
psutil.sensors_battery().percent if psutil.sensors_battery() else 80
|
||||
)
|
||||
GLib.idle_add(self.cpu_progress.set_value, cpu)
|
||||
GLib.idle_add(self.ram_progress.set_value, ram)
|
||||
GLib.idle_add(self.bat_circular.set_value, battery)
|
||||
|
||||
GLib.idle_add(self.cpu_progress.set_tooltip_text, f"{str(round(cpu))}%")
|
||||
GLib.idle_add(self.ram_progress.set_tooltip_text, f"{str(round(ram))}%")
|
||||
GLib.idle_add(self.bat_circular.set_tooltip_text, f"{str(round(battery))}%")
|
||||
|
||||
executor.submit(update)
|
||||
return True
|
||||
|
||||
|
||||
def fetch_quote(callback):
|
||||
"""Fetch quotes asynchronously."""
|
||||
|
||||
def fetch():
|
||||
quotes_type = "stoic"
|
||||
|
||||
url = (
|
||||
"https://stoic-quotes.com/api/quote"
|
||||
if quotes_type == "stoic"
|
||||
else "https://zenquotes.io/api/random"
|
||||
)
|
||||
|
||||
for attempt in range(5):
|
||||
try:
|
||||
response = requests.get(url, timeout=3)
|
||||
response.raise_for_status()
|
||||
respdata = response.json()
|
||||
quote = (
|
||||
f"{respdata[0]['q']} - {respdata[0]['a']}"
|
||||
if quotes_type == "zen"
|
||||
else f"{respdata['text']} - {respdata['author']}"
|
||||
)
|
||||
break
|
||||
except requests.RequestException as e:
|
||||
print(f"Error fetching quote: {e}")
|
||||
if attempt < 4:
|
||||
time.sleep(10)
|
||||
else:
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["hyprctl", "splash"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
quote = result.stdout.strip() + " - Team Hyprland"
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f"Error fetching quote from hyprctl: {e}")
|
||||
quote = "I learn from the mistakes of people who take my advice - Trix"
|
||||
|
||||
GLib.idle_add(callback, quote)
|
||||
|
||||
executor.submit(fetch)
|
||||
|
||||
|
||||
def fetch_quote_async(callback):
|
||||
GLib.idle_add(lambda: fetch_quote(callback))
|
||||
|
||||
|
||||
class QuoteWidget(Label):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(
|
||||
name="quote",
|
||||
label="",
|
||||
anchor="center",
|
||||
h_align="center",
|
||||
v_align="center",
|
||||
h_expand=True,
|
||||
justification="center",
|
||||
v_expand=True,
|
||||
visible=False,
|
||||
)
|
||||
fetch_quote_async(self.update_label)
|
||||
|
||||
def update_label(self, quote):
|
||||
"""Update quote asynchronously."""
|
||||
max_width = 150 # Set the maximum width for the quote
|
||||
if len(quote) > max_width:
|
||||
words = quote.split()
|
||||
line1, line2 = "", ""
|
||||
for word in words:
|
||||
if len(line1) + len(word) + 1 <= max_width:
|
||||
line1 += word + " "
|
||||
else:
|
||||
line2 += word + " "
|
||||
quote = line1.strip() + "\n" + line2.strip()
|
||||
self.set_label(quote)
|
||||
self.set_visible(True)
|
||||
|
||||
|
||||
class ActivationMainText(Label):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(
|
||||
name="activation-main-text",
|
||||
label="",
|
||||
anchor="bottom right",
|
||||
justification="left",
|
||||
v_align="start",
|
||||
h_align="start",
|
||||
h_expand=True,
|
||||
v_expand=True,
|
||||
visible=False,
|
||||
)
|
||||
self.set_label("Activate Linux")
|
||||
|
||||
|
||||
class ActivationSubText(Label):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(
|
||||
name="activation-sub-text",
|
||||
label="",
|
||||
anchor="bottom right",
|
||||
justification="left",
|
||||
v_align="start",
|
||||
h_align="start",
|
||||
h_expand=True,
|
||||
v_expand=True,
|
||||
visible=False,
|
||||
)
|
||||
self.set_label("Go to Settings to activate Linux")
|
||||
|
||||
|
||||
def create_widgets(config):
|
||||
widgets = []
|
||||
if config.get("widgets_displaytype_visible", True):
|
||||
if config.get("widgets_clock_visible", True):
|
||||
widgets.append(
|
||||
DateTime(formatters=["%A, %d %B"], interval=10000, name="date")
|
||||
)
|
||||
if config.get("widgets_date_visible", True):
|
||||
widgets.append(DateTime(formatters=["%I:%M %p"], name="clock"))
|
||||
if config.get("widgets_quote_visible", True):
|
||||
widgets.append(QuoteWidget())
|
||||
if config.get("widgets_weatherwid_visible", True):
|
||||
widgets.append(WeatherWidget())
|
||||
else:
|
||||
widgets.append(DateTime(formatters=["%I:%M %p"], name="clock"))
|
||||
widgets.append(DateTime(formatters=["%A. %d %B"], interval=10000, name="date"))
|
||||
return widgets
|
||||
|
||||
|
||||
class Weather(Box):
|
||||
def __init__(self, parent, **kwargs):
|
||||
super().__init__(
|
||||
name="weather-widget",
|
||||
h_expand=True,
|
||||
v_expand=True,
|
||||
justification="right",
|
||||
orientation="v",
|
||||
all_visible=False,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
self.parent = parent
|
||||
self.city = Label(
|
||||
name="city", label="New York", justification="right", h_align="start"
|
||||
)
|
||||
self.temperature = Label(name="temperature", label="36", h_align="start")
|
||||
self.condition_em = Label(name="condition-emoji", label="☁️", h_align="start")
|
||||
self.condition = Label(name="condition", label="Cloudy", h_align="start")
|
||||
self.feels_like = Label(name="feels-like", label="H:28 L:20", h_align="start")
|
||||
self.add(self.city)
|
||||
self.add(self.temperature)
|
||||
self.add(self.condition_em)
|
||||
self.add(self.condition)
|
||||
self.add(self.feels_like)
|
||||
update_weather(self)
|
||||
|
||||
def update_labels(self, weather_info):
|
||||
if not self.weatherinfo:
|
||||
return
|
||||
|
||||
# Unpack weather info into variables for better readability
|
||||
emoji, temp, condition, location, maxtemp, mintemp = self.weatherinfo
|
||||
maxmin = f"H:{maxtemp} L:{mintemp}"
|
||||
|
||||
# Store references to deeply nested children to avoid repeated lookups
|
||||
|
||||
self.city.set_label(location)
|
||||
self.feels_like.set_label(maxmin)
|
||||
self.condition.set_label(condition)
|
||||
self.temperature.set_label(temp)
|
||||
self.condition_em.set_label(emoji)
|
||||
self.parent.set_visible(True)
|
||||
|
||||
|
||||
class WeatherContainer(Box):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(
|
||||
orientation="v",
|
||||
name="box-widget-2",
|
||||
v_expand=True,
|
||||
v_align="center",
|
||||
visible=True,
|
||||
h_align="center",
|
||||
children=[
|
||||
Weather(self),
|
||||
],
|
||||
**kwargs,
|
||||
)
|
||||
# self.set_style("background-color: rgba(0, 0, 0, 0.5);")
|
||||
# self.add(Weather())
|
||||
|
||||
|
||||
class Date(Box):
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(
|
||||
name="date-widget",
|
||||
h_expand=True,
|
||||
v_expand=True,
|
||||
justification="center",
|
||||
h_align="center",
|
||||
v_align="start",
|
||||
orientation="v",
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
self.top = Box(orientation="h", name="date-top", h_expand=True)
|
||||
self.dateone = DateTime(formatters=["%a"], interval=10000, name="day")
|
||||
self.datetwo = DateTime(formatters=["%b"], interval=10000, name="month")
|
||||
self.datethree = DateTime(formatters=["%-d"], interval=10000, name="date")
|
||||
self.top.add(self.dateone)
|
||||
self.top.add(self.datetwo)
|
||||
self.add(self.top)
|
||||
self.add(self.datethree)
|
||||
|
||||
|
||||
class Deskwidgets(Window):
|
||||
def __init__(self, **kwargs):
|
||||
config = load_config()
|
||||
super().__init__(
|
||||
name="desktop",
|
||||
layer="bottom",
|
||||
title="desktop-widgets",
|
||||
orientation="v",
|
||||
exclusivity="none",
|
||||
child=[
|
||||
Window(
|
||||
anchor="top left",
|
||||
orientation="h",
|
||||
layer="bottom",
|
||||
child=[
|
||||
Box(
|
||||
name="desktop-widgets-container",
|
||||
children=[
|
||||
Box(
|
||||
orientation="v",
|
||||
name="box-widget",
|
||||
v_expand=True,
|
||||
v_align="center",
|
||||
h_align="center",
|
||||
children=[Date()],
|
||||
),
|
||||
WeatherContainer(),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
Window(
|
||||
anchor="bottom left",
|
||||
orientation="h",
|
||||
layer="bottom",
|
||||
child=[],
|
||||
),
|
||||
],
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
|
||||
# else:
|
||||
#
|
||||
# class Deskwidgets(Window):
|
||||
# def __init__(self, **kwargs):
|
||||
# config = load_config()
|
||||
# super().__init__(name="desktop", **kwargs)
|
||||
# desktop_widget = Window(
|
||||
# layer="bottom",
|
||||
# anchor="bottom left",
|
||||
# v_align="start",
|
||||
# h_align="start",
|
||||
# h_expand=True,
|
||||
# v_expand=True,
|
||||
# justification="left",
|
||||
# exclusivity="none",
|
||||
# child=Box(
|
||||
# orientation="v",
|
||||
# children=create_widgets(config),
|
||||
# ),
|
||||
# all_visible=True,
|
||||
# )
|
||||
# if config.get("widgets_activation_visible", True):
|
||||
# activationnag = Window(
|
||||
# name="activation",
|
||||
# anchor="bottom right",
|
||||
# layer="top",
|
||||
# justification="left",
|
||||
# v_align="start",
|
||||
# h_align="start",
|
||||
# h_expand=True,
|
||||
# v_expand=True,
|
||||
# child=Box(
|
||||
# orientation="v",
|
||||
# children=[
|
||||
# ActivationMainText(),
|
||||
# ActivationSubText(),
|
||||
# ],
|
||||
# ),
|
||||
# all_visible=True,
|
||||
# )
|
||||
# else:
|
||||
# activationnag = None
|
||||
#
|
||||
# else:
|
||||
#
|
||||
# class Deskwidgets(Window):
|
||||
# def __init__(self, **kwargs):
|
||||
# super().__init__(name="desktop", **kwargs)
|
||||
# self.set_visible(False)
|
||||
@@ -5,14 +5,16 @@
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
|
||||
#control-center-widgets {
|
||||
background-color: alpha(#000, 0.3);
|
||||
background-color: alpha(#fff, 0.09);
|
||||
box-shadow: inset 0 0 0 1px alpha(#aaa, 0.4);
|
||||
border: 1px solid alpha(#111, 0.4);
|
||||
border-radius: 1.25rem;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
|
||||
/* Widgets */
|
||||
#wifi-widget,
|
||||
#bluetooth-widget {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
#launcher {
|
||||
background-color: alpha(#999, 0.3);
|
||||
color: #000;
|
||||
background-color: alpha(#000, 0.3);
|
||||
padding: 0;
|
||||
border-radius: 12px;
|
||||
min-width: 640px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0 25px 50px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
#launcher-search{
|
||||
color: #000;
|
||||
}
|
||||
#header_box {
|
||||
padding: 0px 20px 0 20px;
|
||||
color: #000;
|
||||
|
||||
@@ -14,8 +14,11 @@
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#noti-clear-button {
|
||||
background-color: alpha(#888, 0.09);
|
||||
}
|
||||
#noti-clear-button:hover {
|
||||
background-color: var(--surface-bright);
|
||||
background-color: alpha(#fff, 0.09);
|
||||
}
|
||||
|
||||
/* Stacked notification group styles */
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#notification {
|
||||
padding: 16px;
|
||||
background-color: alpha(#000, 0.3);
|
||||
background-color: alpha(#fff, 0.03);
|
||||
/* border: 0.5px solid alpha(#111, 0.4); */
|
||||
/* box-shadow: */
|
||||
/* inset 0 0 200px 0 alpha(#111, 0.5), */
|
||||
@@ -15,14 +15,14 @@
|
||||
}
|
||||
|
||||
#notification-action-buttons button {
|
||||
background-color: alpha(#000, 0.3);
|
||||
background-color: alpha(#999, 0.3);
|
||||
padding: 8px;
|
||||
border-radius: 20px;
|
||||
transition: background-color 0.1s ease;
|
||||
}
|
||||
|
||||
#notification-action-buttons button:hover {
|
||||
background-color: var(--surface-bright);
|
||||
background-color: alpha(#000, 0.3);
|
||||
}
|
||||
|
||||
#notification-action-buttons button:active {
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
}
|
||||
|
||||
#panel-button {
|
||||
border-radius: 8px;
|
||||
margin: 0 4px 0 4px;
|
||||
min-width: 20px;
|
||||
min-height: 20px;
|
||||
|
||||