26 Commits
0.5 ... 0.7

Author SHA1 Message Date
Stephen Horvath
a97148f7a5 Release 0.7 2025-12-25 20:48:18 +10:00
Stephen Horvath
8b10c49ed4 Update Python on Windows CI 2025-12-25 20:15:14 +10:00
Stephen Horvath
f31054c18b Update Gvsbuild on CI 2025-12-25 20:10:22 +10:00
Stephen Horvath
2b50f0816f Implement fan set points 2025-12-25 19:35:33 +10:00
Stephen Horvath
1fc4b94237 Streamline udev rules 2025-11-06 13:58:51 +10:00
Stephen Horvath
90a994a685 Disable blue power button colour
Early Intel models report that their power button LED supports blue
colour, but in reality it just turns it off.
2025-10-20 15:07:37 +10:00
Stephen Horvath
7b123da001 Handle custom power LED brightness levels 2025-10-16 22:45:23 +10:00
Stephen Horvath
c8fd626446 Update about text to match README 2025-10-04 10:32:09 +10:00
Stephen Horvath
0f6fee40ae Add disclaimer to README 2025-10-04 10:29:23 +10:00
Stephen Horvath
4a788c2395 Merge pull request #11 from OrioleNix/patch-1
Change title from gtk3 to gtk4
2025-10-04 07:05:08 +10:00
OrioleNix
d2f34c9b5a Update main.py 2025-10-03 11:22:34 -04:00
Stephen Horvath
4958722edd Update README.md 2025-09-28 23:26:39 +10:00
Stephen Horvath
23b54ee397 Add FUNDING.yml 2025-09-28 23:24:51 +10:00
Stephen Horvath
73bb7abf6e Release 0.6 2025-09-28 13:35:13 +10:00
Stephen Horvath
9f0fabb91f Fix metainfo screenshots 2025-09-28 12:55:28 +10:00
Stephen Horvath
a31f851707 More changes for Flathub submission 2025-09-27 18:21:11 +10:00
Stephen Horvath
70d3788dd6 Add sensors to Hardware/Sensors page 2025-09-23 12:54:51 +10:00
Stephen Horvath
f3b74ccc02 Rework advanced LEDs 2025-09-21 22:30:24 +10:00
Stephen Horvath
335a0881b3 Preparation for Flathub publishing 2025-09-21 13:37:35 +10:00
Stephen Horvath
c39a88b7db Remove hard edges from logo 2025-09-19 13:36:33 +10:00
Stephen Horvath
ba78392b24 Add Windows ZIP build 2025-09-18 15:15:55 +10:00
Stephen Horvath
c1205c7661 Add board model to debug info 2025-09-17 21:48:34 +10:00
Stephen Horvath
973ff26fa7 Fix stutter when opening About dialogue 2025-09-17 21:48:06 +10:00
Stephen Horvath
4a8dfbad47 Update flatpak versioning 2025-09-17 17:19:58 +10:00
Stephen Horvath
2a0675d3be Fix pyinstaller for CI and Windows 2025-09-17 16:51:33 +10:00
Stephen Horvath
55580535db Add new logo and splash screen 2025-09-17 16:26:21 +10:00
39 changed files with 1140 additions and 312 deletions

2
.github/FUNDING.yml vendored Normal file
View File

@@ -0,0 +1,2 @@
github: [Steve-Tech]
thanks_dev: u/gh/steve-tech

View File

@@ -8,7 +8,7 @@ jobs:
flatpak:
runs-on: ubuntu-latest
container:
image: ghcr.io/flathub-infra/flatpak-github-actions:gnome-48
image: ghcr.io/flathub-infra/flatpak-github-actions:gnome-49
options: --privileged
steps:
- uses: actions/checkout@v4

View File

@@ -13,7 +13,7 @@ jobs:
- name: Install System Dependencies
uses: awalsh128/cache-apt-pkgs-action@latest
with:
packages: python3-gi gir1.2-gtk-4.0 gir1.2-adw-1 libglib2.0-bin
packages: python3-gi gir1.2-gtk-4.0 gir1.2-adw-1 libglib2.0-bin python3-tk
version: 0
- name: Install PyInstaller
run: pip install pyinstaller
@@ -21,7 +21,7 @@ jobs:
run: pip install .
- name: Build with PyInstaller
# pyinstaller doesn't find the GTK libraries after caching?
run: LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu pyinstaller yafi.spec
run: LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu pyinstaller yafi.spec -- --onefile
working-directory: pyinstaller
- name: Upload PyInstaller Artifact
uses: actions/upload-artifact@v4

View File

@@ -11,7 +11,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.13'
python-version: '3.14'
cache: 'pip'
- name: Cache GTK4
@@ -19,11 +19,11 @@ jobs:
uses: actions/cache@v4
with:
path: C:\gtk
key: Gvsbuild_2025.8.0
key: Gvsbuild_2025.11.1
- name: Download GTK4 Gvsbuild zip
if: steps.cache-gtk4.outputs.cache-hit != 'true'
run: Start-BitsTransfer -Source https://github.com/wingtk/gvsbuild/releases/download/2025.8.0/GTK4_Gvsbuild_2025.8.0_x64.zip -Destination Gvsbuild.zip
run: Start-BitsTransfer -Source https://github.com/wingtk/gvsbuild/releases/download/2025.11.1/GTK4_Gvsbuild_2025.11.1_x64.zip -Destination Gvsbuild.zip
- name: Extract Gvsbuild zip
if: steps.cache-gtk4.outputs.cache-hit != 'true'
@@ -41,12 +41,22 @@ jobs:
- name: Build YAFI via Pip
run: pip install .
- name: Build with PyInstaller
- name: Build with PyInstaller (ZIP)
run: python -m PyInstaller yafi.spec
working-directory: pyinstaller
- name: Upload PyInstaller Artifact
- name: Upload PyInstaller Artifact (ZIP)
uses: actions/upload-artifact@v4
with:
path: pyinstaller/dist/YAFI/*
name: yafi-windows-${{ github.sha }}
- name: Build with PyInstaller (Standalone)
run: python -m PyInstaller yafi.spec --noconfirm -- --onefile
working-directory: pyinstaller
- name: Upload PyInstaller Artifact (Standalone)
uses: actions/upload-artifact@v4
with:
path: pyinstaller/dist/YAFI.exe
name: yafi-windows-${{ github.sha }}
name: yafi-windows-standalone-${{ github.sha }}

View File

@@ -1,7 +0,0 @@
# CrOS_EC_Python udev rules
# LPC Access
KERNEL=="port", TAG+="uaccess"
# /dev/cros_ec Access
KERNEL=="cros_ec", TAG+="uaccess"

View File

@@ -11,9 +11,13 @@ You can download the latest release from the [Releases page](https://github.com/
There are builds for Flatpak, and PyInstaller for portable execution on Linux or Windows.
YAFI is also available on [Flathub](https://flathub.org/en/apps/au.stevetech.yafi): `flatpak install flathub au.stevetech.yafi`.
### Linux
To allow YAFI to communicate with the EC, you need to copy the [`60-cros_ec_python.rules`](60-cros_ec_python.rules) file to `/etc/udev/rules.d/` and reload the rules with `sudo udevadm control --reload-rules && sudo udevadm trigger`.
To allow YAFI to communicate with the EC, you will need to enable user access to the `/dev/cros_ec` device. You can do this by running `echo KERNEL=="cros_ec", TAG+="uaccess" | sudo tee /etc/udev/rules.d/60-yafi.rules`, and then reload the rules with `sudo udevadm control --reload-rules && sudo udevadm trigger`.
You can also do this by running `curl -Lfs yafi.stevetech.au/udev.sh | sudo sh` which will run the [`add-udev-rules.sh`](add-udev-rules.sh) script.
### Windows
@@ -21,6 +25,8 @@ If your Laptop's BIOS supports Framework's EC driver, there is no need to instal
Otherwise, YAFI supports the [PawnIO](https://pawnio.eu/) driver, and will be automatically used if installed and there is no Framework driver available. YAFI will need to be run as administrator to communicate with the driver.
Currently the PawnIO driver does not support Framework 13 mainboards with 11th, 12th, or 13th Gen Intel CPUs.
## Building
### Flatpak
@@ -80,7 +86,7 @@ It is possible to run YAFI on Windows using [gvsbuild](https://github.com/wingtk
### `[Errno 13] Permission denied: '/dev/cros_ec'`
This error occurs when the udev rules are not installed or not working. Make sure you have copied the `60-cros_ec_python.rules` file to `/etc/udev/rules.d/` and reloaded the rules with `sudo udevadm control --reload-rules && sudo udevadm trigger`.
This error occurs when the udev rules are not installed or not working. Make sure you have installed the udev rules as described in the [Linux Installation](#linux) section.
### `Could not auto detect device, check you have the required permissions, or specify manually.`
@@ -88,3 +94,9 @@ This error occurs when `/dev/cros_ec` is not found, and the `CrOS_EC_Python` lib
You can either update your kernel to have a working `cros_ec_dev` driver, or run YAFI as root.
It can also occur if you do not have a CrOS EC, like on non Framework laptops.
## Disclaimer
YAFI is not affiliated with Framework Computer Inc. in any way.
YAFI is licensed under the [GPL-2.0-or-later license](COPYING), and comes with no warranty or guarantee of any kind. Use at your own risk.

7
add-udev-rules.sh Executable file
View File

@@ -0,0 +1,7 @@
#!/bin/bash
set -e
echo Installing udev rules for YAFI to /etc/udev/rules.d/60-yafi.rules
echo KERNEL=="cros_ec", TAG+="uaccess" > /etc/udev/rules.d/60-yafi.rules
udevadm control --reload-rules
udevadm trigger
echo udev rules installed successfully.

View File

@@ -1,13 +1,14 @@
{
"id" : "au.stevetech.yafi",
"runtime" : "org.gnome.Platform",
"runtime-version" : "48",
"runtime-version" : "49",
"sdk" : "org.gnome.Sdk",
"command" : "yafi",
"finish-args" : [
"--device=all",
"--socket=fallback-x11",
"--socket=wayland"
"--socket=wayland",
"--share=ipc"
],
"cleanup" : [
"/include",
@@ -21,6 +22,7 @@
"*.a"
],
"modules" : [
"python3-cros_ec_python.json",
{
"name" : "yafi",
"builddir" : true,
@@ -31,18 +33,6 @@
"path" : "."
}
]
},
{
"name": "cros_ec_python",
"buildsystem": "simple",
"build-options": {
"build-args": [
"--share=network"
]
},
"build-commands": [
"pip3 install --prefix=${FLATPAK_DEST} --no-cache-dir \"cros_ec_python>=0.2.0\""
]
}
]
}

View File

@@ -6,6 +6,6 @@ Comment=Yet Another Framework Interface
Terminal=false
Type=Application
Categories=Utility;
Keywords=GTK;
Keywords=Framework;EC;Embedded Controller;
StartupNotify=true
DBusActivatable=true

View File

@@ -7,11 +7,25 @@
<name>Yet Another Framework Interface</name>
<summary>YAFI is another GUI for the Framework Laptop Embedded Controller</summary>
<description>
<p>It is written in Python with a GTK3 theme, and uses the `CrOS_EC_Python` library to communicate with the EC.</p>
<p>It is written in Python with a GTK4 Adwaita theme, and uses the CrOS_EC_Python library to communicate with the EC.</p>
<p>YAFI has the capability for the following features:</p>
<ul>
<li>Fan control and temperature monitoring</li>
<li>LED control</li>
<li>Battery statistics</li>
<li>Battery limiting</li>
<li>Hardware information</li>
</ul>
<p>You will need to install the udev rules to allow non-root access to the EC device. See the README for more information.</p>
<p>Alternatively, you can run <code>curl -Lfs yafi.stevetech.au/udev.sh | sudo sh</code> to install the udev rules.</p>
<p>YAFI is not affiliated with Framework Computer Inc. in any way.</p>
</description>
<developer id="au.stevetech">
<name>Stephen Horvath</name>
<name translate="no">Stephen Horvath</name>
</developer>
<!-- Required: Should be a link to the upstream homepage for the component -->
@@ -27,7 +41,7 @@
<!-- Should provide a web link to an online user's reference, a software manual or help page -->
<!-- <url type="help">https://example.org/help</url> -->
<!-- URLs of this type should point to a webpage showing information on how to donate to the described software project -->
<!-- <url type="donation">https://example.org/donate</url> -->
<url type="donation">https://github.com/sponsors/Steve-Tech</url>
<!-- This could for example be an HTTPS URL to an online form or a page describing how to contact the developer -->
<!-- <url type="contact">https://example.org/contact</url> -->
<!-- URLs of this type should point to a webpage showing information on how to contribute to the described software project -->
@@ -42,42 +56,92 @@
<!-- Applications should set a brand color in both light and dark variants like so -->
<branding>
<color type="primary" scheme_preference="light">#ff00ff</color>
<color type="primary" scheme_preference="dark">#993d3d</color>
<color type="primary" scheme_preference="light">#7c916f</color>
<color type="primary" scheme_preference="dark">#282000</color>
</branding>
<screenshots>
<screenshot type="default">
<image>https://github.com/Steve-Tech/YAFI/blob/main/docs/1-thermal.png</image>
<caption>The Thermal page</caption>
<image>https://raw.githubusercontent.com/Steve-Tech/YAFI/refs/heads/main/docs/1-thermals.png</image>
<caption>Fan Control and Temperature Monitoring</caption>
</screenshot>
<screenshot>
<image>https://github.com/Steve-Tech/YAFI/blob/main/docs/2-leds.png</image>
<caption>The LED page</caption>
<image>https://raw.githubusercontent.com/Steve-Tech/YAFI/refs/heads/main/docs/2-leds.png</image>
<caption>LED Control</caption>
</screenshot>
<screenshot>
<image>https://github.com/Steve-Tech/YAFI/blob/main/docs/3-battery.png</image>
<caption>The Battery page</caption>
<image>https://raw.githubusercontent.com/Steve-Tech/YAFI/refs/heads/main/docs/3-battery.png</image>
<caption>Battery Statistics</caption>
</screenshot>
<screenshot>
<image>https://github.com/Steve-Tech/YAFI/blob/main/docs/4-hardware.png</image>
<caption>The Hardware page</caption>
<image>https://raw.githubusercontent.com/Steve-Tech/YAFI/refs/heads/main/docs/4-battery-limit.png</image>
<caption>Battery Limiting</caption>
</screenshot>
<screenshot>
<image>https://raw.githubusercontent.com/Steve-Tech/YAFI/refs/heads/main/docs/4a-battery-ext.png</image>
<caption>Battery Extender</caption>
</screenshot>
<screenshot>
<image>https://raw.githubusercontent.com/Steve-Tech/YAFI/refs/heads/main/docs/5-hardware.png</image>
<caption>Hardware Info</caption>
</screenshot>
</screenshots>
<releases>
<release version="0.1.0" date="2025-03-20">
<url type="details">https://github.com/Steve-Tech/YAFI/releases/tag/0.1.0</url>
<description translate="no">
<p>First release</p>
<release version="0.7" date="2025-12-25">
<url type="details">https://github.com/Steve-Tech/YAFI/releases/tag/0.7</url>
<description>
<p>YAFI now supports modifying fan set points.</p>
</description>
</release>
<release version="0.6" date="2025-09-28">
<url type="details">https://github.com/Steve-Tech/YAFI/releases/tag/0.6</url>
<description>
<p>The following changes have been made:</p>
<ul>
<li>Added Thermal page</li>
<li>Added LED page</li>
<li>Added Battery page</li>
<li>Added Hardware page</li>
<li>YAFI now has a new logo!</li>
<li>The advanced LED settings now detect available LEDs.</li>
<li>The ALS and lid open sensor has been added.</li>
<li>Stuttering has been reduced when opening the about dialog.</li>
</ul>
</description>
</release>
<release version="0.5" date="2025-09-16">
<url type="details">https://github.com/Steve-Tech/YAFI/releases/tag/0.5</url>
<description>
<p>The following bugs have been fixed:</p>
<ul>
<li>LED page crashing when encountering unsupported LEDs.</li>
<li>Scaling issues on Windows.</li>
<li>Minimum charge limiter doesn't work.</li>
</ul>
<p>This release also adds a traceback to the debug info, when there is an error during initialisation.</p>
</description>
</release>
<release version="0.4" date="2025-09-06">
<url type="details">https://github.com/Steve-Tech/YAFI/releases/tag/0.4</url>
<description>
<p>This release also adds a new battery statistics page.</p>
</description>
</release>
<release version="0.3" date="2025-04-13">
<url type="details">https://github.com/Steve-Tech/YAFI/releases/tag/0.3</url>
<description>
<p>YAFI now shows the battery limiter value.</p>
</description>
</release>
<release version="0.2" date="2025-03-24">
<url type="details">https://github.com/Steve-Tech/YAFI/releases/tag/0.2</url>
<description>
<p>This fixes an LED list bug.</p>
</description>
</release>
<release version="0.1" date="2025-03-20">
<url type="details">https://github.com/Steve-Tech/YAFI/releases/tag/0.1</url>
<description>
<p>This is the first release of YAFI.</p>
</description>
</release>
</releases>
</component>

View File

@@ -1,36 +1,92 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M1755 4883 c-120 -22 -418 -180 -609 -322 -118 -89 -148 -118 -177
-177 -23 -48 -24 -57 -27 -314 -1 -146 -7 -294 -13 -330 -18 -110 -78 -222
-163 -303 -46 -43 -69 -58 -305 -197 -191 -112 -222 -139 -257 -214 -33 -73
-39 -145 -39 -461 0 -297 5 -360 33 -440 25 -73 77 -118 257 -224 88 -51 187
-111 220 -132 123 -79 192 -169 245 -319 8 -21 14 -110 16 -230 8 -403 9 -423
29 -460 32 -61 56 -88 134 -148 234 -181 550 -347 676 -356 75 -5 116 12 306
125 181 108 307 175 330 176 8 1 35 5 61 11 70 14 174 5 251 -23 38 -13 164
-81 281 -149 286 -168 312 -173 494 -90 41 19 75 34 77 34 4 0 201 110 243
135 45 27 147 100 213 153 51 40 83 75 104 113 l30 54 6 310 c5 280 8 315 27
365 58 156 137 245 295 336 246 140 346 205 376 243 35 44 60 106 67 166 2 22
8 74 13 115 9 78 7 442 -4 520 -12 97 -16 116 -27 150 -16 52 -61 113 -105
142 -21 14 -126 78 -233 141 -107 64 -209 128 -225 144 -51 48 -101 115 -128
173 -48 105 -56 163 -56 454 -1 262 -1 270 -25 321 -24 53 -65 100 -124 145
-19 14 -46 34 -60 46 -42 32 -190 128 -257 164 -250 138 -384 177 -476 140
-19 -8 -101 -54 -184 -104 -316 -188 -340 -198 -480 -199 -153 -1 -181 10
-501 201 -178 106 -237 128 -309 115z m984 -848 c149 -19 308 -68 441 -135 68
-34 181 -100 190 -110 3 -3 21 -17 40 -30 52 -37 75 -57 166 -148 150 -151
281 -364 345 -562 33 -102 32 -99 56 -210 18 -86 26 -378 13 -455 -5 -27 -12
-68 -15 -90 -16 -102 -64 -250 -117 -364 -211 -450 -634 -764 -1113 -828 -113
-15 -425 -7 -455 12 -4 2 -22 7 -41 10 -150 26 -404 140 -531 239 -14 12 -41
32 -59 46 -115 85 -291 299 -371 449 -78 148 -144 353 -165 516 -10 76 -10
311 0 390 15 117 58 274 106 390 116 278 347 543 608 696 158 92 386 169 549
184 38 4 70 8 72 9 5 6 229 -2 281 -9z"/>
</g>
</svg>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="128"
height="128"
viewBox="0 0 128 128"
version="1.1"
id="svg1"
xml:space="preserve"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs1" /><g
id="layer1"><g
id="icon"><g
id="chip"
style="stroke-width:0;stroke-dasharray:none"><path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 34,8 v 8"
id="pin1" /><path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="M 54,8.0000019 V 16.000002"
id="pin2" /><path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="M 74.000009,8.0000019 V 16.000002"
id="pin3" /><path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 93.999992,8 v 8"
id="pin4" /><path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 120,34.000001 h -8"
id="pin5" /><path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 120,54.000001 h -8"
id="pin6" /><path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 120,74.000009 h -8"
id="pin7" /><path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 120,93.999992 h -8"
id="pin8" /><path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 94.000009,112 v 8"
id="pin9" /><path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 74.000026,112 v 8"
id="pin10" /><path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 54.000017,112 v 8"
id="pin11" /><path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 34.000017,112 v 8"
id="pin12" /><path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="M 16.000002,93.999992 H 8.0000019"
id="pin13" /><path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="M 16.000002,74.000009 H 8.0000019"
id="pin14" /><path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="M 16.000002,54.000001 H 8.0000019"
id="pin15" /><path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="M 16.000002,34.000001 H 8.0000019"
id="pin16" /><rect
style="fill:#000000;fill-opacity:0.5;stroke-width:0;stroke-dasharray:none"
id="shadow"
width="104"
height="104"
x="16"
y="16"
rx="16.000002"
ry="16.000002" /><rect
style="fill:#808080;stroke-width:0;stroke-dasharray:none"
id="package"
width="104"
height="104"
x="12.000001"
y="12.000001"
rx="16.000002"
ry="16.000002" /><circle
style="fill:#b3b3b3;fill-opacity:1;stroke:none;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
id="dot"
cx="22"
cy="22"
r="2" /></g><path
id="cog"
style="display:inline;fill:#282000;fill-opacity:1;stroke:none;stroke-width:8.00001;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 47.818359,18 2.542969,2.630859 c -2.531091,-2.605911 -6.371601,-0.420022 -6.429687,-0.386718 l -0.0039,0.002 -7.783203,4.49414 c -0.03804,0.02206 -3.86275,2.252383 -2.888672,5.746094 l 0.002,0.01172 0.0059,0.01563 3.027344,10.585937 C 33.755122,44.16501 31.7167,47.65852 30.302828,51.455131 l -10.679687,2.671875 c -3.549895,0.894185 -3.550781,5.380768 -3.550781,5.380859 v 8.984376 c 0,9.1e-5 0.001,4.493173 3.558593,5.382812 l 10.671875,2.669922 c 1.413872,3.79649 3.452294,7.288523 5.988282,10.353516 l -3.027344,10.585937 -0.0059,0.01758 c -1.79e-4,6.36e-4 1.79e-4,0.0013 0,0.002 -0.9887,3.517182 2.886719,5.755862 2.886719,5.755862 l 7.783203,4.49414 c 0,0 0.0038,0.002 0.0039,0.002 0.02177,0.0125 3.888449,2.23357 6.43164,-0.38867 L 47.818359,110 l 2.548829,-2.63477 7.652343,-7.914058 c 1.945156,0.325909 3.942752,0.494141 5.980469,0.49414 2.037713,0 4.035313,-0.168231 5.980469,-0.49414 l 7.65039,7.912108 0.002,0.002 2.548829,2.63477 -2.544922,-2.63281 c 2.522803,2.60122 6.349136,0.43583 6.43164,0.38867 l 7.78711,-4.49609 c 0,0 3.87542,-2.23868 2.886719,-5.755864 v -0.002 l -0.0059,-0.01758 -3.027344,-10.585937 c 2.536165,-3.064993 4.574289,-6.557026 5.988282,-10.353516 L 108.37109,73.875 c 3.55762,-0.889648 3.55664,-5.382812 3.55664,-5.382812 v -8.984376 c 0,10e-7 9.8e-4,-4.493163 -3.55664,-5.382812 L 97.697266,51.455078 C 96.283273,47.658406 94.245149,44.164957 91.708984,41.099609 l 3.027344,-10.585937 0.0059,-0.01563 0.002,-0.01172 c 0.976385,-3.502191 -2.869106,-5.73477 -2.888672,-5.746094 l -7.783203,-4.49414 c -9.1e-5,-5.2e-5 -0.0038,-0.0019 -0.0039,-0.002 -0.05432,-0.03116 -3.897327,-2.220492 -6.429687,0.386718 L 80.181641,18 l -2.550782,2.636719 -7.65039,7.912109 C 68.035313,28.223033 66.037713,28.054688 64,28.054688 c -2.037717,0 -4.035313,0.168337 -5.980469,0.49414 l -7.65039,-7.912109 z M 64,36 A 27.999999,27.999997 0 0 1 92,64 27.999999,27.999997 0 0 1 64,92 27.999999,27.999997 0 0 1 36,64 27.999999,27.999997 0 0 1 64,36 Z" /><path
id="Y"
style="fill:none;stroke:#282000;stroke-width:8;stroke-linecap:round;stroke-dasharray:none"
d="M 64.000002,62.00022 50.000001,48.000008 m 13.999997,38.000215 4e-6,-24.000003 m -4e-6,0 14.000001,-14.000212 m -13.999997,38.000215 -4e-6,-24.000003" /></g></g></svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@@ -1,36 +1,21 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.11, written by Peter Selinger 2001-2013
</metadata>
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M1755 4883 c-120 -22 -418 -180 -609 -322 -118 -89 -148 -118 -177
-177 -23 -48 -24 -57 -27 -314 -1 -146 -7 -294 -13 -330 -18 -110 -78 -222
-163 -303 -46 -43 -69 -58 -305 -197 -191 -112 -222 -139 -257 -214 -33 -73
-39 -145 -39 -461 0 -297 5 -360 33 -440 25 -73 77 -118 257 -224 88 -51 187
-111 220 -132 123 -79 192 -169 245 -319 8 -21 14 -110 16 -230 8 -403 9 -423
29 -460 32 -61 56 -88 134 -148 234 -181 550 -347 676 -356 75 -5 116 12 306
125 181 108 307 175 330 176 8 1 35 5 61 11 70 14 174 5 251 -23 38 -13 164
-81 281 -149 286 -168 312 -173 494 -90 41 19 75 34 77 34 4 0 201 110 243
135 45 27 147 100 213 153 51 40 83 75 104 113 l30 54 6 310 c5 280 8 315 27
365 58 156 137 245 295 336 246 140 346 205 376 243 35 44 60 106 67 166 2 22
8 74 13 115 9 78 7 442 -4 520 -12 97 -16 116 -27 150 -16 52 -61 113 -105
142 -21 14 -126 78 -233 141 -107 64 -209 128 -225 144 -51 48 -101 115 -128
173 -48 105 -56 163 -56 454 -1 262 -1 270 -25 321 -24 53 -65 100 -124 145
-19 14 -46 34 -60 46 -42 32 -190 128 -257 164 -250 138 -384 177 -476 140
-19 -8 -101 -54 -184 -104 -316 -188 -340 -198 -480 -199 -153 -1 -181 10
-501 201 -178 106 -237 128 -309 115z m984 -848 c149 -19 308 -68 441 -135 68
-34 181 -100 190 -110 3 -3 21 -17 40 -30 52 -37 75 -57 166 -148 150 -151
281 -364 345 -562 33 -102 32 -99 56 -210 18 -86 26 -378 13 -455 -5 -27 -12
-68 -15 -90 -16 -102 -64 -250 -117 -364 -211 -450 -634 -764 -1113 -828 -113
-15 -425 -7 -455 12 -4 2 -22 7 -41 10 -150 26 -404 140 -531 239 -14 12 -41
32 -59 46 -115 85 -291 299 -371 449 -78 148 -144 353 -165 516 -10 76 -10
311 0 390 15 117 58 274 106 390 116 278 347 543 608 696 158 92 386 169 549
184 38 4 70 8 72 9 5 6 229 -2 281 -9z"/>
</g>
</svg>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="96"
height="96"
viewBox="0 0 96 96"
version="1.1"
id="svg1"
xml:space="preserve"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs1" /><g
id="layer1"><g
id="icon"><path
id="cog"
style="display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:8.00001;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 31.818314,2 2.542969,2.630859 C 31.830192,2.024948 27.989682,4.210837 27.931596,4.244141 l -0.0039,0.002 -7.783203,4.49414 c -0.03804,0.02206 -3.86275,2.252383 -2.888672,5.746094 l 0.002,0.01172 0.0059,0.01563 3.027344,10.585937 c -2.535988,3.065348 -4.57441,6.558858 -5.988282,10.355469 L 3.623096,38.127006 c -3.549895,0.894185 -3.550781,5.380768 -3.550781,5.380859 v 8.984376 c 0,9.1e-5 0.001,4.493173 3.558593,5.382812 l 10.671875,2.669922 c 1.413872,3.79649 3.452294,7.288523 5.988282,10.353516 l -3.027344,10.585937 -0.0059,0.01758 c -1.79e-4,6.36e-4 1.79e-4,0.0013 0,0.002 -0.9887,3.517182 2.886719,5.755862 2.886719,5.755862 l 7.783203,4.49414 c 0,0 0.0038,0.002 0.0039,0.002 0.02177,0.0125 3.888449,2.23357 6.43164,-0.38867 L 31.818314,94 l 2.548829,-2.63477 7.652343,-7.914058 c 1.945156,0.325909 3.942752,0.494141 5.980469,0.49414 2.037713,0 4.035313,-0.168231 5.980469,-0.49414 l 7.65039,7.912108 0.002,0.002 2.548829,2.63477 -2.544922,-2.63281 c 2.522803,2.60122 6.349136,0.43583 6.43164,0.38867 l 7.78711,-4.49609 c 0,0 3.87542,-2.23868 2.886719,-5.755864 v -0.002 l -0.0059,-0.01758 -3.027344,-10.585937 c 2.536165,-3.064993 4.574289,-6.557026 5.988282,-10.353516 L 92.371045,57.875 c 3.55762,-0.889648 3.55664,-5.382812 3.55664,-5.382812 v -8.984376 c 0,10e-7 9.8e-4,-4.493163 -3.55664,-5.382812 L 81.697221,35.455078 C 80.283228,31.658406 78.245104,28.164957 75.708939,25.099609 l 3.027344,-10.585937 0.0059,-0.01563 0.002,-0.01172 c 0.976385,-3.502191 -2.869106,-5.73477 -2.888672,-5.746094 l -7.783203,-4.49414 c -9.1e-5,-5.2e-5 -0.0038,-0.0019 -0.0039,-0.002 -0.05432,-0.03116 -3.897327,-2.220492 -6.429687,0.386718 L 64.181596,2 l -2.550782,2.636719 -7.65039,7.912109 c -1.945156,-0.325795 -3.942756,-0.49414 -5.980469,-0.49414 -2.037717,0 -4.035313,0.168337 -5.980469,0.49414 l -7.65039,-7.912109 z m 16.181641,18 a 27.999999,27.999997 0 0 1 28,28 27.999999,27.999997 0 0 1 -28,28 27.999999,27.999997 0 0 1 -28,-28 27.999999,27.999997 0 0 1 28,-28 z" /><path
id="Y"
style="fill:none;stroke:#000000;stroke-width:8;stroke-linecap:round;stroke-dasharray:none"
d="M 48.000002,46.00022 34.000001,32.000008 m 13.999997,38.000215 4e-6,-24.000003 m -4e-6,0 14.000001,-14.000212 m -13.999997,38.000215 -4e-6,-24.000003" /></g></g></svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 KiB

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 104 KiB

3
flathub.json Normal file
View File

@@ -0,0 +1,3 @@
{
"only-arches": ["x86_64"]
}

162
logo.svg Normal file
View File

@@ -0,0 +1,162 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="128"
height="128"
viewBox="0 0 128 128"
version="1.1"
id="svg1"
xml:space="preserve"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
sodipodi:docname="logo.svg"
inkscape:export-filename="logo-plain.svg"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
inkscape:zoom="5.6568542"
inkscape:cx="38.802485"
inkscape:cy="83.261824"
inkscape:window-width="1876"
inkscape:window-height="1025"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="icon" /><defs
id="defs1" /><g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"><g
id="icon"
inkscape:label="icon"
inkscape:export-filename="icon.png"
inkscape:export-xdpi="96"
inkscape:export-ydpi="96"><g
id="chip"
inkscape:label="chip"
style="stroke-width:0;stroke-dasharray:none"><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="m 34,8 v 8"
id="pin1"
sodipodi:nodetypes="cc"
inkscape:label="pin1" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="M 54,8.0000019 V 16.000002"
id="pin2"
sodipodi:nodetypes="cc"
inkscape:label="pin2" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="M 74.000009,8.0000019 V 16.000002"
id="pin3"
sodipodi:nodetypes="cc"
inkscape:label="pin3" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="m 93.999992,8 v 8"
id="pin4"
sodipodi:nodetypes="cc"
inkscape:label="pin4" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="m 120,34.000001 h -8"
id="pin5"
sodipodi:nodetypes="cc"
inkscape:label="pin5" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="m 120,54.000001 h -8"
id="pin6"
sodipodi:nodetypes="cc"
inkscape:label="pin6" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="m 120,74.000009 h -8"
id="pin7"
sodipodi:nodetypes="cc"
inkscape:label="pin7" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="m 120,93.999992 h -8"
id="pin8"
sodipodi:nodetypes="cc"
inkscape:label="pin8" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="m 94.000009,112 v 8"
id="pin9"
sodipodi:nodetypes="cc"
inkscape:label="pin9" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="m 74.000026,112 v 8"
id="pin10"
sodipodi:nodetypes="cc"
inkscape:label="pin10" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="m 54.000017,112 v 8"
id="pin11"
sodipodi:nodetypes="cc"
inkscape:label="pin11" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="m 34.000017,112 v 8"
id="pin12"
sodipodi:nodetypes="cc"
inkscape:label="pin12" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="M 16.000002,93.999992 H 8.0000019"
id="pin13"
sodipodi:nodetypes="cc"
inkscape:label="pin13" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="M 16.000002,74.000009 H 8.0000019"
id="pin14"
sodipodi:nodetypes="cc"
inkscape:label="pin14" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="M 16.000002,54.000001 H 8.0000019"
id="pin15"
sodipodi:nodetypes="cc"
inkscape:label="pin15" /><path
style="stroke-width:8;stroke-dasharray:none;fill:none;stroke:#ffcc00;stroke-linecap:round;paint-order:stroke fill markers"
d="M 16.000002,34.000001 H 8.0000019"
id="pin16"
sodipodi:nodetypes="cc"
inkscape:label="pin16" /><rect
style="fill:#000000;stroke-width:0;stroke-dasharray:none;fill-opacity:0.5"
id="shadow"
width="104"
height="104"
x="16"
y="16"
rx="16.000002"
ry="16.000002"
inkscape:label="shadow" /><rect
style="fill:#808080;stroke-width:0;stroke-dasharray:none"
id="package"
width="104"
height="104"
x="12.000001"
y="12.000001"
rx="16.000002"
ry="16.000002"
inkscape:label="package" /><circle
style="fill:#b3b3b3;fill-opacity:1;stroke:none;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
id="dot"
cx="22"
cy="22"
r="2"
inkscape:label="dot" /></g><path
id="cog"
style="display:inline;fill:#282000;fill-opacity:1;stroke:none;stroke-width:8.00001;stroke-dasharray:none;paint-order:stroke fill markers"
inkscape:label="cog"
d="M 47.818359 18 L 50.361328 20.630859 C 47.830237 18.024948 43.989727 20.210837 43.931641 20.244141 L 43.927734 20.246094 L 36.144531 24.740234 C 36.106488 24.762299 32.281781 26.992617 33.255859 30.486328 L 33.257812 30.498047 L 33.263672 30.513672 L 36.291016 41.099609 C 33.755028 44.164957 31.716606 47.658467 30.302734 51.455078 L 19.623047 54.126953 C 16.073152 55.021138 16.072266 59.507721 16.072266 59.507812 L 16.072266 68.492188 C 16.072266 68.492279 16.073278 72.985361 19.630859 73.875 L 30.302734 76.544922 C 31.716606 80.341412 33.755028 83.833445 36.291016 86.898438 L 33.263672 97.484375 L 33.257812 97.501953 C 33.257633 97.502589 33.257991 97.50327 33.257812 97.503906 C 32.269112 101.02109 36.144531 103.25977 36.144531 103.25977 L 43.927734 107.75391 C 43.927734 107.75391 43.931579 107.75582 43.931641 107.75586 C 43.953413 107.7684 47.82009 109.98943 50.363281 107.36719 L 47.818359 110 L 50.367188 107.36523 L 58.019531 99.451172 C 59.964687 99.777081 61.962283 99.945313 64 99.945312 C 66.037713 99.945312 68.035313 99.777081 69.980469 99.451172 L 77.630859 107.36328 L 77.632812 107.36523 L 80.181641 110 L 77.636719 107.36719 C 80.159522 109.96841 83.985855 107.80302 84.068359 107.75586 L 91.855469 103.25977 C 91.855469 103.25977 95.730889 101.02109 94.742188 97.503906 L 94.742188 97.501953 L 94.736328 97.484375 L 91.708984 86.898438 C 94.245149 83.833445 96.283273 80.341412 97.697266 76.544922 L 108.37109 73.875 C 111.92871 72.985352 111.92773 68.492188 111.92773 68.492188 L 111.92773 59.507812 C 111.92773 59.507813 111.92871 55.014649 108.37109 54.125 L 97.697266 51.455078 C 96.283273 47.658406 94.245149 44.164957 91.708984 41.099609 L 94.736328 30.513672 L 94.742188 30.498047 L 94.744141 30.486328 C 95.720526 26.984137 91.875035 24.751558 91.855469 24.740234 L 84.072266 20.246094 C 84.072175 20.246042 84.068504 20.244224 84.068359 20.244141 C 84.01404 20.212982 80.171032 18.023649 77.638672 20.630859 L 80.181641 18 L 77.630859 20.636719 L 69.980469 28.548828 C 68.035313 28.223033 66.037713 28.054688 64 28.054688 C 61.962283 28.054688 59.964687 28.223025 58.019531 28.548828 L 50.369141 20.636719 L 47.818359 18 z M 64 36 A 27.999999 27.999997 0 0 1 92 64 A 27.999999 27.999997 0 0 1 64 92 A 27.999999 27.999997 0 0 1 36 64 A 27.999999 27.999997 0 0 1 64 36 z " /><path
id="Y"
style="fill:none;stroke:#282000;stroke-width:8;stroke-linecap:round;stroke-dasharray:none"
d="M 64.000002,62.00022 50.000001,48.000008 m 13.999997,38.000215 4e-6,-24.000003 m -4e-6,0 14.000001,-14.000212 m -13.999997,38.000215 -4e-6,-24.000003"
inkscape:label="Y"
sodipodi:nodetypes="cccccccc" /></g></g></svg>

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -1,5 +1,5 @@
project('yafi',
version: '0.1.0',
version: '0.7',
meson_version: '>= 1.0.0',
default_options: [ 'warning_level=2', 'werror=false', ],
)

BIN
pyinstaller/splash.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

384
pyinstaller/splash.svg Normal file
View File

@@ -0,0 +1,384 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="256"
height="256"
viewBox="0 0 256 256"
version="1.1"
id="svg1"
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
sodipodi:docname="splash.svg"
inkscape:export-filename="splash.png"
inkscape:export-xdpi="180"
inkscape:export-ydpi="180"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview1"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="px"
showguides="false"
inkscape:zoom="2.8284271"
inkscape:cx="118.26361"
inkscape:cy="131.87541"
inkscape:window-width="1876"
inkscape:window-height="1025"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
id="defs1">
<filter
style="color-interpolation-filters:sRGB"
inkscape:label="Drop Shadow"
id="filter151"
x="0"
y="0"
width="1.0384614"
height="1.0384614">
<feFlood
result="flood"
in="SourceGraphic"
flood-opacity="0.498039"
flood-color="rgb(0,0,0)"
id="feFlood150" />
<feGaussianBlur
result="blur"
in="SourceGraphic"
stdDeviation="0.000000"
id="feGaussianBlur150" />
<feOffset
result="offset"
in="blur"
dx="4.000000"
dy="4.000000"
id="feOffset150" />
<feComposite
result="comp1"
operator="in"
in="flood"
in2="offset"
id="feComposite150" />
<feComposite
result="comp2"
operator="over"
in="SourceGraphic"
in2="comp1"
id="feComposite151" />
</filter>
<filter
style="color-interpolation-filters:sRGB"
inkscape:label="Drop Shadow"
id="filter151-6"
x="0"
y="0"
width="1.0384615"
height="1.0384615">
<feFlood
result="flood"
in="SourceGraphic"
flood-opacity="0.498039"
flood-color="rgb(0,0,0)"
id="feFlood150-1" />
<feGaussianBlur
result="blur"
in="SourceGraphic"
stdDeviation="0.000000"
id="feGaussianBlur150-8" />
<feOffset
result="offset"
in="blur"
dx="4.000000"
dy="4.000000"
id="feOffset150-7" />
<feComposite
result="comp1"
operator="in"
in="flood"
in2="offset"
id="feComposite150-9" />
<feComposite
result="comp2"
operator="over"
in="SourceGraphic"
in2="comp1"
id="feComposite151-2" />
</filter>
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#7c916f;stroke-width:1;paint-order:stroke fill markers"
id="rect1"
width="256"
height="256"
x="0"
y="0" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="M 0,117.875 H 23.874996 L 43.75,97.999992 H 76"
id="path4"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="M 0,97.875016 H 23.874996 L 43.75,78.000008 H 76"
id="path4-4"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="M 0,77.875008 H 23.874996 L 43.75,58 H 76"
id="path4-9"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="M 0,57.875008 H 23.874996 L 43.75,38 H 76"
id="path4-2"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="M 97.999999,0 V 16.000001"
id="path5"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="M 118,0 V 16.000001"
id="path5-8"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="M 138.00001,0 V 16.000001"
id="path5-4"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="M 157.99999,0 V 16.000001"
id="path5-89"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="M 256,117.87502 H 232.125 L 212.25,98.000008 H 180"
id="path4-1"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="M 255.625,97.875035 H 231.75 L 211.875,78.000023 H 180"
id="path4-1-5"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="M 255.625,77.875012 H 231.75 L 211.875,58 H 180"
id="path4-1-6"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="M 255.625,57.875012 H 231.75 L 211.875,38 H 180"
id="path4-1-63"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="m 192.16536,142.16357 -9.15729,-9.15728 H 163.00629 L 158.00001,128 l 0,-8"
id="path9" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="m 147.1875,143.0625 v -5.875 L 138.00001,128 l 0,-8"
id="path10" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="M 119.98796,141.81833 118,139.83037 V 128 120"
id="path11" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;paint-order:normal"
d="m 101,142.125 -3.000001,-3 V 128 120"
id="path12" />
<text
xml:space="preserve"
style="font-weight:bold;font-size:64px;line-height:1;font-family:Comfortaa;-inkscape-font-specification:'Comfortaa Bold';text-align:center;text-decoration-color:#000000;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#282000;stroke-width:0.264583;paint-order:stroke fill markers"
x="131.10406"
y="189.98389"
id="text1"><tspan
sodipodi:role="line"
id="tspan1"
style="font-size:64px;fill:#282000;stroke-width:0.264583"
x="131.10406"
y="189.98389">YAFI</tspan></text>
<text
xml:space="preserve"
style="font-weight:bold;font-size:21.3333px;line-height:1;font-family:Comfortaa;-inkscape-font-specification:'Comfortaa Bold';text-align:center;text-decoration-color:#000000;writing-mode:lr-tb;direction:ltr;text-anchor:middle;fill:#282000;stroke-width:0.264583;paint-order:stroke fill markers"
x="128.032"
y="214.66127"
id="text1-6"><tspan
sodipodi:role="line"
id="tspan1-0"
style="font-size:21.3333px;fill:#282000;stroke-width:0.264583"
x="128.032"
y="214.66127">Yet Another</tspan><tspan
sodipodi:role="line"
style="font-size:21.3333px;fill:#282000;stroke-width:0.264583"
x="128.032"
y="235.99457"
id="tspan2">Framework Interface</tspan></text>
<g
id="icon"
inkscape:label="icon"
transform="translate(63.999984,4)">
<g
id="chip"
inkscape:label="chip"
style="stroke-width:0;stroke-dasharray:none">
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 34,8 v 8"
id="pin1"
sodipodi:nodetypes="cc"
inkscape:label="pin1" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="M 54,8.0000019 V 16.000002"
id="pin2"
sodipodi:nodetypes="cc"
inkscape:label="pin2" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="M 74.000009,8.0000019 V 16.000002"
id="pin3"
sodipodi:nodetypes="cc"
inkscape:label="pin3" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 93.999992,8 v 8"
id="pin4"
sodipodi:nodetypes="cc"
inkscape:label="pin4" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 120,34.000001 h -8"
id="pin5"
sodipodi:nodetypes="cc"
inkscape:label="pin5" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 120,54.000001 h -8"
id="pin6"
sodipodi:nodetypes="cc"
inkscape:label="pin6" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 120,74.000009 h -8"
id="pin7"
sodipodi:nodetypes="cc"
inkscape:label="pin7" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 120,93.999992 h -8"
id="pin8"
sodipodi:nodetypes="cc"
inkscape:label="pin8" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 94.000009,112 v 8"
id="pin9"
sodipodi:nodetypes="cc"
inkscape:label="pin9" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 74.000026,112 v 8"
id="pin10"
sodipodi:nodetypes="cc"
inkscape:label="pin10" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 54.000017,112 v 8"
id="pin11"
sodipodi:nodetypes="cc"
inkscape:label="pin11" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="m 34.000017,112 v 8"
id="pin12"
sodipodi:nodetypes="cc"
inkscape:label="pin12" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="M 16.000002,93.999992 H 8.0000019"
id="pin13"
sodipodi:nodetypes="cc"
inkscape:label="pin13" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="M 16.000002,74.000009 H 8.0000019"
id="pin14"
sodipodi:nodetypes="cc"
inkscape:label="pin14" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="M 16.000002,54.000001 H 8.0000019"
id="pin15"
sodipodi:nodetypes="cc"
inkscape:label="pin15" />
<path
style="fill:none;stroke:#ffcc00;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
d="M 16.000002,34.000001 H 8.0000019"
id="pin16"
sodipodi:nodetypes="cc"
inkscape:label="pin16" />
<rect
style="fill:#000000;fill-opacity:0.5;stroke-width:0;stroke-dasharray:none"
id="shadow"
width="104"
height="104"
x="16"
y="16"
rx="16.000002"
ry="16.000002"
inkscape:label="shadow" />
<rect
style="fill:#808080;stroke-width:0;stroke-dasharray:none"
id="package"
width="104"
height="104"
x="12.000001"
y="12.000001"
rx="16.000002"
ry="16.000002"
inkscape:label="package" />
<circle
style="fill:#b3b3b3;fill-opacity:1;stroke:none;stroke-width:8;stroke-linecap:round;stroke-dasharray:none;paint-order:stroke fill markers"
id="dot"
cx="22"
cy="22"
r="2"
inkscape:label="dot" />
</g>
<path
id="cog"
style="display:inline;fill:#282000;fill-opacity:1;stroke:none;stroke-width:8.00001;stroke-dasharray:none;paint-order:stroke fill markers"
inkscape:label="cog"
d="m 47.818359,18 2.542969,2.630859 c -2.531091,-2.605911 -6.371601,-0.420022 -6.429687,-0.386718 l -0.0039,0.002 -7.783203,4.49414 c -0.03804,0.02206 -3.86275,2.252383 -2.888672,5.746094 l 0.002,0.01172 0.0059,0.01563 3.027344,10.585937 C 33.755122,44.16501 31.7167,47.65852 30.302828,51.455131 l -10.679687,2.671875 c -3.549895,0.894185 -3.550781,5.380768 -3.550781,5.380859 v 8.984376 c 0,9.1e-5 0.001,4.493173 3.558593,5.382812 l 10.671875,2.669922 c 1.413872,3.79649 3.452294,7.288523 5.988282,10.353516 l -3.027344,10.585937 -0.0059,0.01758 c -1.79e-4,6.36e-4 1.79e-4,0.0013 0,0.002 -0.9887,3.517182 2.886719,5.755862 2.886719,5.755862 l 7.783203,4.49414 c 0,0 0.0038,0.002 0.0039,0.002 0.02177,0.0125 3.888449,2.23357 6.43164,-0.38867 L 47.818359,110 l 2.548829,-2.63477 7.652343,-7.914058 c 1.945156,0.325909 3.942752,0.494141 5.980469,0.49414 2.037713,0 4.035313,-0.168231 5.980469,-0.49414 l 7.65039,7.912108 0.002,0.002 2.548829,2.63477 -2.544922,-2.63281 c 2.522803,2.60122 6.349136,0.43583 6.43164,0.38867 l 7.78711,-4.49609 c 0,0 3.87542,-2.23868 2.886719,-5.755864 v -0.002 l -0.0059,-0.01758 -3.027344,-10.585937 c 2.536165,-3.064993 4.574289,-6.557026 5.988282,-10.353516 L 108.37109,73.875 c 3.55762,-0.889648 3.55664,-5.382812 3.55664,-5.382812 v -8.984376 c 0,10e-7 9.8e-4,-4.493163 -3.55664,-5.382812 L 97.697266,51.455078 C 96.283273,47.658406 94.245149,44.164957 91.708984,41.099609 l 3.027344,-10.585937 0.0059,-0.01563 0.002,-0.01172 c 0.976385,-3.502191 -2.869106,-5.73477 -2.888672,-5.746094 l -7.783203,-4.49414 c -9.1e-5,-5.2e-5 -0.0038,-0.0019 -0.0039,-0.002 -0.05432,-0.03116 -3.897327,-2.220492 -6.429687,0.386718 L 80.181641,18 l -2.550782,2.636719 -7.65039,7.912109 C 68.035313,28.223033 66.037713,28.054688 64,28.054688 c -2.037717,0 -4.035313,0.168337 -5.980469,0.49414 l -7.65039,-7.912109 z M 64,36 A 27.999999,27.999997 0 0 1 92,64 27.999999,27.999997 0 0 1 64,92 27.999999,27.999997 0 0 1 36,64 27.999999,27.999997 0 0 1 64,36 Z" />
<path
id="Y"
style="fill:none;stroke:#282000;stroke-width:8;stroke-linecap:round;stroke-dasharray:none"
d="M 64.000002,62.00022 50.000001,48.000008 m 13.999997,38.000215 4e-6,-24.000003 m -4e-6,0 14.000001,-14.000212 m -13.999997,38.000215 -4e-6,-24.000003"
inkscape:label="Y"
sodipodi:nodetypes="cccccccc" />
</g>
<rect
style="fill:#ffcc00;stroke:none;stroke-width:8;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;paint-order:normal"
id="rect12"
width="256"
height="12"
x="0"
y="244" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 149 KiB

View File

@@ -1,11 +1,15 @@
# -*- mode: python ; coding: utf-8 -*-
from PyInstaller.utils.hooks import collect_data_files
import os
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("--onefile", action="store_true")
options = parser.parse_args()
datas = [('LpcCrOSEC.bin', '.')] if os.name == 'nt' and os.path.exists('LpcCrOSEC.bin') else []
datas += collect_data_files('yafi')
a = Analysis(
['entrypoint.py'],
pathex=[],
@@ -21,12 +25,34 @@ a = Analysis(
)
pyz = PYZ(a.pure)
if options.onefile:
splash = Splash(
'splash.png',
binaries=a.binaries,
datas=a.datas,
text_pos=(4, 480),
# Text doesn't scale on Linux, but does on Windows
text_size=12 if os.name == 'nt' else 6,
minify_script=True,
always_on_top=True,
)
exe_args = (
[
a.scripts,
a.binaries,
a.datas,
splash,
splash.binaries,
]
if options.onefile
else [a.scripts]
)
exe = EXE(
pyz,
a.scripts,
a.binaries,
a.datas,
[],
*exe_args,
exclude_binaries=not options.onefile,
name='YAFI',
debug=False,
bootloader_ignore_signals=False,
@@ -42,3 +68,14 @@ exe = EXE(
entitlements_file=None,
icon=['yafi.ico'],
)
if not options.onefile:
coll = COLLECT(
exe,
a.binaries,
a.datas,
strip=False,
upx=True,
upx_exclude=[],
name='YAFI',
)

View File

@@ -1,6 +1,6 @@
[project]
name = "yafi"
version = "0.5"
version = "0.7"
authors = [
{ name="Steve-Tech" }
]
@@ -8,7 +8,7 @@ description = "Yet Another Framework Interface"
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"cros_ec_python >= 0.2.0",
"cros_ec_python >= 0.3.0",
"PyGObject"
]
classifiers = [

View File

@@ -0,0 +1,14 @@
{
"name": "python3-cros_ec_python",
"buildsystem": "simple",
"build-commands": [
"pip3 install --verbose --exists-action=i --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} \"cros_ec_python\" --no-build-isolation"
],
"sources": [
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/6c/7a/10d978a02bbe37530490cfd14e0994c433dc29c81b3afcdbde453d512528/cros_ec_python-0.3.0-py3-none-any.whl",
"sha256": "aeb14ebdbd60ec6d6a4b11df1482a295466da4a908a468d168efd4cc141e7e3d"
}
]
}

View File

@@ -18,9 +18,7 @@
#
# SPDX-License-Identifier: GPL-2.0-or-later
from gi.repository import Adw
from gi.repository import Gtk
from gi.repository import GLib
from gi.repository import Gtk, Adw, GLib
import cros_ec_python.commands as ec_commands
import cros_ec_python.exceptions as ec_exceptions

View File

@@ -18,9 +18,7 @@
#
# SPDX-License-Identifier: GPL-2.0-or-later
from gi.repository import Adw
from gi.repository import Gtk
from gi.repository import GLib
from gi.repository import Gtk, Adw, GLib
import cros_ec_python.commands as ec_commands
import cros_ec_python.exceptions as ec_exceptions

View File

@@ -18,9 +18,7 @@
#
# SPDX-License-Identifier: GPL-2.0-or-later
from gi.repository import Adw
from gi.repository import Gtk
from gi.repository import GLib
from gi.repository import Gtk, Adw, GLib
import cros_ec_python.commands as ec_commands
import cros_ec_python.exceptions as ec_exceptions
@@ -29,17 +27,24 @@ import cros_ec_python.exceptions as ec_exceptions
class HardwarePage(Gtk.Box):
__gtype_name__ = 'HardwarePage'
hw_als = Gtk.Template.Child()
hw_als_label = Gtk.Template.Child()
hw_chassis = Gtk.Template.Child()
hw_chassis_label = Gtk.Template.Child()
hw_fp_pwr = Gtk.Template.Child()
hw_fp_pwr_en = Gtk.Template.Child()
hw_fp_pwr_dis = Gtk.Template.Child()
hw_priv_cam = Gtk.Template.Child()
hw_priv_cam_sw = Gtk.Template.Child()
hw_priv_mic = Gtk.Template.Child()
hw_priv_mic_sw = Gtk.Template.Child()
hw_fp_pwr = Gtk.Template.Child()
hw_fp_pwr_en = Gtk.Template.Child()
hw_fp_pwr_dis = Gtk.Template.Child()
hw_lid_open = Gtk.Template.Child()
hw_lid_open_sw = Gtk.Template.Child()
hw_pwr_btn = Gtk.Template.Child()
hw_pwr_btn_sw = Gtk.Template.Child()
def __init__(self, **kwargs):
super().__init__(**kwargs)
@@ -68,7 +73,13 @@ class HardwarePage(Gtk.Box):
GLib.timeout_add_seconds(1, self._update_hardware, app)
def _update_hardware(self, app):
success = False
# Memmap (ALS and Lid Open)
als = ec_commands.memmap.get_als(app.cros_ec)
self.hw_als_label.set_label(f"{als[0]} lux" if als[0] != 65535 else "MAX")
switches = ec_commands.memmap.get_switches(app.cros_ec)
self.hw_lid_open_sw.set_active(switches["lid_open"])
self.hw_pwr_btn_sw.set_active(switches["power_button_pressed"])
# Chassis
if not ec_commands.framework_laptop.EC_CMD_CHASSIS_INTRUSION in app.no_support:
try:
@@ -84,8 +95,6 @@ class HardwarePage(Gtk.Box):
self.hw_chassis.set_subtitle(
"Currently " + ("Open" if ec_chassis_open else "Closed")
)
success = True
except ec_exceptions.ECError as e:
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
app.no_support.append(
@@ -106,8 +115,6 @@ class HardwarePage(Gtk.Box):
)
self.hw_priv_cam_sw.set_active(ec_privacy["camera"])
self.hw_priv_mic_sw.set_active(ec_privacy["microphone"])
success = True
except ec_exceptions.ECError as e:
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
app.no_support.append(
@@ -118,4 +125,4 @@ class HardwarePage(Gtk.Box):
else:
raise e
return app.current_page == 4 and success
return app.current_page == 4

View File

@@ -18,8 +18,7 @@
#
# SPDX-License-Identifier: GPL-2.0-or-later
from gi.repository import Adw
from gi.repository import Gtk
from gi.repository import Gtk, Adw, GLib
import cros_ec_python.commands as ec_commands
import cros_ec_python.exceptions as ec_exceptions
@@ -28,6 +27,8 @@ import cros_ec_python.exceptions as ec_exceptions
class LedsPage(Gtk.Box):
__gtype_name__ = 'LedsPage'
first_run = True
led_pwr = Gtk.Template.Child()
led_pwr_scale = Gtk.Template.Child()
@@ -36,27 +37,30 @@ class LedsPage(Gtk.Box):
led_advanced = Gtk.Template.Child()
led_pwr_colour = Gtk.Template.Child()
led_chg_colour = Gtk.Template.Child()
def __init__(self, **kwargs):
super().__init__(**kwargs)
def setup(self, app):
# Power LED
try:
def handle_led_pwr(scale):
value = int(abs(scale.get_value() - 2))
ec_commands.framework_laptop.set_fp_led_level(app.cros_ec, value)
self.led_pwr.set_subtitle(["High", "Medium", "Low"][value])
current_fp_level = ec_commands.framework_laptop.get_fp_led_level(
app.cros_ec
).value
self.led_pwr_scale.set_value(abs(current_fp_level - 2))
self.led_pwr.set_subtitle(["High", "Medium", "Low"][current_fp_level])
try:
current_fp_level = ec_commands.framework_laptop.get_fp_led_level(
app.cros_ec
).value
self.led_pwr_scale.set_value(abs(current_fp_level - 2))
self.led_pwr.set_subtitle(["High", "Medium", "Low"][current_fp_level])
except ValueError:
# LED isn't a normal value
current_fp_level = ec_commands.framework_laptop.get_fp_led_level_int(
app.cros_ec
)
self.led_pwr.set_subtitle(f"Custom ({current_fp_level}%)")
self.led_pwr_scale.connect("value-changed", handle_led_pwr)
except ec_exceptions.ECError as e:
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
@@ -85,31 +89,38 @@ class LedsPage(Gtk.Box):
self.led_kbd.set_visible(False)
# Advanced options
if ec_commands.general.get_cmd_versions(
app.cros_ec, ec_commands.leds.EC_CMD_LED_CONTROL
if (
ec_commands.general.get_cmd_versions(
app.cros_ec, ec_commands.leds.EC_CMD_LED_CONTROL
)
and self.first_run
):
# Advanced: Power LED
led_pwr_colour_strings = self.led_pwr_colour.get_model()
all_colours = ["Red", "Green", "Blue", "Yellow", "White", "Amber"]
def add_colours(strings, led_id):
# Auto and Off should already be present
if strings.get_n_items() <= 2:
supported_colours = ec_commands.leds.led_control_get_max_values(
led_names = {
ec_commands.leds.EcLedId.EC_LED_ID_BATTERY_LED: "Battery LED",
ec_commands.leds.EcLedId.EC_LED_ID_POWER_LED: "Power LED",
ec_commands.leds.EcLedId.EC_LED_ID_ADAPTER_LED: "Adapter LED",
ec_commands.leds.EcLedId.EC_LED_ID_LEFT_LED: "Left LED",
ec_commands.leds.EcLedId.EC_LED_ID_RIGHT_LED: "Right LED",
ec_commands.leds.EcLedId.EC_LED_ID_RECOVERY_HW_REINIT_LED: "Recovery LED",
ec_commands.leds.EcLedId.EC_LED_ID_SYSRQ_DEBUG_LED: "SysRq LED",
}
leds = {}
for i in range(ec_commands.leds.EcLedId.EC_LED_ID_COUNT.value):
try:
led_id = ec_commands.leds.EcLedId(i)
leds[led_id] = ec_commands.leds.led_control_get_max_values(
app.cros_ec, led_id
)
for i, colour in enumerate(all_colours):
if supported_colours[i]:
strings.append(colour)
try:
add_colours(
led_pwr_colour_strings, ec_commands.leds.EcLedId.EC_LED_ID_POWER_LED
)
except ec_exceptions.ECError as e:
self.led_pwr_colour.set_sensitive(False)
except ec_exceptions.ECError as e:
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_PARAM:
continue
else:
raise e
# Power LED does not support Blue, even though Intel models think they do
leds[ec_commands.leds.EcLedId.EC_LED_ID_POWER_LED][2] = 0
def handle_led_colour(combobox, led_id):
colour = combobox.get_selected() - 2
@@ -133,30 +144,20 @@ class LedsPage(Gtk.Box):
100,
ec_commands.leds.EcLedColors(colour_idx),
)
for led_id, supported_colours in leds.items():
if any(supported_colours):
combo = Adw.ComboRow(title=led_names[led_id])
model = Gtk.StringList.new(["Auto", "Off"])
for i, colour in enumerate(all_colours):
if supported_colours[i]:
model.append(colour)
combo.set_model(model)
combo.connect(
"notify::selected",
lambda combobox, _, led_id=led_id: handle_led_colour(
combobox, led_id
),
)
self.led_advanced.add_row(combo)
self.led_pwr_colour.connect(
"notify::selected",
lambda combo, _: handle_led_colour(
combo, ec_commands.leds.EcLedId.EC_LED_ID_POWER_LED
),
)
# Advanced: Charging LED
led_chg_colour_strings = self.led_chg_colour.get_model()
try:
add_colours(
led_chg_colour_strings,
ec_commands.leds.EcLedId.EC_LED_ID_BATTERY_LED,
)
except ec_exceptions.ECError as e:
self.led_chg_colour.set_sensitive(False)
self.led_chg_colour.connect(
"notify::selected",
lambda combo, _: handle_led_colour(
combo, ec_commands.leds.EcLedId.EC_LED_ID_BATTERY_LED
),
)
else:
self.led_advanced.set_visible(False)
self.first_run = False

View File

@@ -18,15 +18,16 @@
#
# SPDX-License-Identifier: GPL-2.0-or-later
import os
import sys
import traceback
import threading
import gi
from gi.repository import Gio
gi.require_version('Gtk', '4.0')
gi.require_version('Adw', '1')
from gi.repository import Gtk, Gio, Adw
from gi.repository import Gtk, Gio, Adw, GLib
from .window import YafiWindow
from .thermals import ThermalsPage
from .leds import LedsPage
@@ -66,6 +67,12 @@ class YafiApplication(Adw.Application):
if not self.win:
self.win = YafiWindow(application=self)
# Update the splash screen
splash = getattr(sys, 'frozen', False) and '_PYI_SPLASH_IPC' in os.environ
if splash:
import pyi_splash
pyi_splash.update_text("Detecting EC")
try:
self.cros_ec = get_cros_ec()
pass
@@ -81,9 +88,16 @@ class YafiApplication(Adw.Application):
+ "If you are using a Framework Laptop, there are additional troubleshooting steps in the README."
)
self.show_error("EC Initalisation Error", message)
if splash:
pyi_splash.close()
self.win.present()
return
if splash:
pyi_splash.update_text("Building Interface")
self.change_page(self.win.content, ThermalsPage())
pages = (
@@ -91,7 +105,7 @@ class YafiApplication(Adw.Application):
("LEDs", LedsPage()),
("Battery", BatteryPage()),
("Battery Limiter", BatteryLimiterPage()),
("Hardware", HardwarePage()),
("Sensors", HardwarePage()),
("About", None),
)
@@ -111,6 +125,9 @@ class YafiApplication(Adw.Application):
self.win.navbar.connect("row-activated", lambda box, row: switch_page(row.get_index()))
if splash:
pyi_splash.close()
self.win.present()
def on_about_action(self, *args):
@@ -118,20 +135,25 @@ class YafiApplication(Adw.Application):
about = Adw.AboutDialog(
application_icon="au.stevetech.yafi",
application_name="Yet Another Framework Interface",
comments="YAFI is another GUI for the Framework Laptop Embedded Controller.\n"
+ "It is written in Python with a GTK3 theme, and uses the `CrOS_EC_Python` library to communicate with the EC.",
comments="YAFI is another GUI for the Framework Laptop Embedded Controller.\n\n"
+ "It is written in Python with a GTK4 Adwaita theme, and uses the CrOS_EC_Python library to communicate with the EC.\n\n"
+ "YAFI is not affiliated with Framework Computer Inc. in any way.",
copyright="© 2025 Stephen Horvath",
debug_info=self.generate_debug_info(),
developer_name="Stephen Horvath",
developers=["Stephen Horvath"],
developers=["Stephen Horvath https://github.com/Steve-Tech"],
issue_url="https://github.com/Steve-Tech/YAFI/issues",
license_type=Gtk.License.GPL_2_0,
version="0.5",
version="0.7",
website="https://github.com/Steve-Tech/YAFI",
)
about.add_acknowledgement_section(None, ["Framework Computer Inc. https://frame.work/"])
about.present(self.props.active_window)
if hasattr(self, 'debug_info'):
about.set_debug_info(self.debug_info)
else:
threading.Thread(target=lambda: GLib.idle_add(about.set_debug_info, self.generate_debug_info())).start()
def show_error(self, heading, message):
dialog = Adw.AlertDialog(heading=heading, body=message)
dialog.add_response("exit", "Exit")
@@ -179,6 +201,8 @@ class YafiApplication(Adw.Application):
info += f"Model: {f.read().strip()}\n"
with open('/sys/devices/virtual/dmi/id/product_sku', 'r') as f:
info += f"SKU: {f.read().strip()}\n"
with open('/sys/devices/virtual/dmi/id/board_name', 'r') as f:
info += f"Board: {f.read().strip()}\n"
with open('/sys/devices/virtual/dmi/id/bios_vendor', 'r') as f:
info += f"BIOS Vendor: {f.read().strip()}\n"
with open('/sys/devices/virtual/dmi/id/bios_version', 'r') as f:
@@ -190,10 +214,12 @@ class YafiApplication(Adw.Application):
ps_cmd = (
"powershell -Command "
"\"$cs = Get-CimInstance -ClassName Win32_ComputerSystem; "
"$board = Get-CimInstance -ClassName Win32_BaseBoard; "
"$bios = Get-CimInstance -ClassName Win32_BIOS; "
"Write-Output $cs.Manufacturer; "
"Write-Output $cs.Model; "
"Write-Output $cs.SystemSKUNumber; "
"Write-Output $board.Product; "
"Write-Output $bios.Manufacturer; "
"Write-Output $bios.Name; "
"Write-Output $bios.ReleaseDate\""
@@ -202,10 +228,11 @@ class YafiApplication(Adw.Application):
info += f"Manufacturer: {output[0]}\n"
info += f"Model: {output[1]}\n"
info += f"SKU: {output[2]}\n"
info += f"BIOS Vendor: {output[3]}\n"
info += f"BIOS Version: {output[4]}\n"
info += f"Board: {output[3]}\n"
info += f"BIOS Vendor: {output[4]}\n"
info += f"BIOS Version: {output[5]}\n"
# Blank line in the output for some reason
info += f"BIOS Date: {output[6]}\n"
info += f"BIOS Date: {output[7]}\n"
except Exception as e:
info += f"System Info Error: {type(e).__name__}: {e}\n"

View File

@@ -18,9 +18,7 @@
#
# SPDX-License-Identifier: GPL-2.0-or-later
from gi.repository import Adw
from gi.repository import Gtk
from gi.repository import GLib
from gi.repository import Gtk, Adw, GLib
import cros_ec_python.commands as ec_commands
import cros_ec_python.exceptions as ec_exceptions
@@ -29,11 +27,17 @@ import cros_ec_python.exceptions as ec_exceptions
class ThermalsPage(Gtk.Box):
__gtype_name__ = 'ThermalsPage'
first_run = True
fan_rpm = Gtk.Template.Child()
fan_mode = Gtk.Template.Child()
fan_set_rpm = Gtk.Template.Child()
fan_set_percent = Gtk.Template.Child()
fan_percent_scale = Gtk.Template.Child()
fan_set_points = Gtk.Template.Child()
set_points = []
ec_set_points_supported = False
ec_set_points = []
temperatures = Gtk.Template.Child()
temp_items = []
@@ -42,10 +46,40 @@ class ThermalsPage(Gtk.Box):
super().__init__(**kwargs)
def setup(self, app):
# Temperature sensors
while temp_child := self.temperatures.get_last_child():
self.temperatures.remove(temp_child)
self.temp_items.clear()
try:
ec_temp_sensors = ec_commands.thermal.get_temp_sensors(app.cros_ec)
except ec_exceptions.ECError as e:
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
# Generate some labels if the command is not supported
ec_temp_sensors = {}
temps = ec_commands.memmap.get_temps(app.cros_ec)
for i, temp in enumerate(temps):
ec_temp_sensors[f"Sensor {i}"] = (temp, None)
else:
raise e
for key, value in ec_temp_sensors.items():
off_row = Adw.ActionRow(title=key, subtitle=f"{value[0]}°C")
off_row.add_css_class("property")
self.temperatures.append(off_row)
self.temp_items.append(off_row)
self._update_thermals(app)
# Don't let the user change the fans if they can't get back to auto
if ec_commands.general.get_cmd_versions(
app.cros_ec, ec_commands.thermal.EC_CMD_THERMAL_AUTO_FAN_CTRL
):
self.ec_set_points_supported = ec_commands.general.check_cmd_version(
app.cros_ec, ec_commands.thermal.EC_CMD_THERMAL_GET_THRESHOLD, 1
) and ec_commands.general.check_cmd_version(
app.cros_ec, ec_commands.thermal.EC_CMD_THERMAL_SET_THRESHOLD, 1
)
def handle_fan_mode(mode):
match mode:
@@ -53,13 +87,18 @@ class ThermalsPage(Gtk.Box):
self.fan_set_rpm.set_visible(False)
self.fan_set_percent.set_visible(False)
ec_commands.thermal.thermal_auto_fan_ctrl(app.cros_ec)
self.fan_set_points.set_visible(self.ec_set_points_supported)
case 1: # Percent
self.fan_set_points.set_visible(False)
self.fan_set_rpm.set_visible(False)
self.fan_set_percent.set_visible(True)
case 2: # RPM
self.fan_set_points.set_visible(False)
self.fan_set_rpm.set_visible(True)
self.fan_set_percent.set_visible(False)
handle_fan_mode(self.fan_mode.get_selected())
self.fan_mode.connect(
"notify::selected",
lambda combo, _: handle_fan_mode(combo.get_selected()),
@@ -83,41 +122,77 @@ class ThermalsPage(Gtk.Box):
):
def handle_fan_rpm(entry):
rpm = int(entry.get_text())
rpm = int(entry.get_value())
ec_commands.pwm.pwm_set_fan_rpm(app.cros_ec, rpm)
self.fan_set_rpm.connect(
"notify::text", lambda entry, _: handle_fan_rpm(entry)
"notify::value", lambda entry, _: handle_fan_rpm(entry)
)
else:
self.fan_set_rpm.set_sensitive(False)
else:
self.fan_mode.set_sensitive(False)
# Temperature sensors
while temp_child := self.temperatures.get_last_child():
self.temperatures.remove(temp_child)
self.temp_items.clear()
# Set points
if self.ec_set_points_supported and self.first_run:
def handle_set_point(entry, key):
index = entry.ec_index
temp = int(entry.get_value())
# Don't allow an off temp higher than max temp and vice versa
match key:
case "temp_fan_off":
if temp > self.ec_set_points[index]["temp_fan_max"]:
entry.set_value(self.ec_set_points[index]["temp_fan_off"])
return
case "temp_fan_max":
if temp < self.ec_set_points[index]["temp_fan_off"]:
entry.set_value(self.ec_set_points[index]["temp_fan_max"])
return
self.ec_set_points[entry.ec_index][key] = temp
ec_commands.thermal.thermal_set_thresholds(
app.cros_ec, index,
self.ec_set_points[index]
)
try:
ec_temp_sensors = ec_commands.thermal.get_temp_sensors(app.cros_ec)
except ec_exceptions.ECError as e:
if e.ec_status == ec_exceptions.EcStatus.EC_RES_INVALID_COMMAND:
# Generate some labels if the command is not supported
ec_temp_sensors = {}
temps = ec_commands.memmap.get_temps(app.cros_ec)
for i, temp in enumerate(temps):
ec_temp_sensors[f"Sensor {i}"] = (temp, None)
else:
raise e
for i, sensor in enumerate(ec_temp_sensors):
ec_set_point = ec_commands.thermal.thermal_get_thresholds(app.cros_ec, i)
self.ec_set_points.append(ec_set_point)
off_row = Adw.SpinRow(
title=f"Fan On - {sensor}",
subtitle=f"Turn fan on when above temp (°C)",
)
off_row.ec_index = i
# 0K to 65535K for 16bit unsigned range
# Actually the EC takes 32bits, but let's keep it like this for sanity
off_row.set_adjustment(Gtk.Adjustment(
lower=-273,
upper=65_262,
page_increment=10,
step_increment=1,
value=ec_set_point["temp_fan_off"],
))
off_row.connect(
"notify::value", lambda entry, _: handle_set_point(entry, "temp_fan_off")
)
max_row = Adw.SpinRow(
title=f"Fan Max - {sensor}",
subtitle=f"Max fan speed when above temp (°C)",
)
max_row.ec_index = i
max_row.set_adjustment(Gtk.Adjustment(
lower=-273,
upper=65_262,
page_increment=10,
step_increment=1,
value=ec_set_point["temp_fan_max"],
))
max_row.connect(
"notify::value", lambda entry, _: handle_set_point(entry, "temp_fan_max")
)
self.fan_set_points.add_row(off_row)
self.fan_set_points.add_row(max_row)
for key, value in ec_temp_sensors.items():
new_row = Adw.ActionRow(title=key, subtitle=f"{value[0]}°C")
new_row.add_css_class("property")
self.temperatures.append(new_row)
self.temp_items.append(new_row)
self._update_thermals(app)
self.first_run = False
# Schedule _update_thermals to run every second
GLib.timeout_add_seconds(1, self._update_thermals, app)

View File

@@ -2,7 +2,7 @@
<!-- Created with Cambalache 0.96.1 -->
<interface>
<!-- interface-name hardware.ui -->
<!-- interface-description The Hardware page for YAFI -->
<!-- interface-description The Sensors page for YAFI -->
<!-- interface-copyright Steve-Tech -->
<requires lib="gtk" version="4.0"/>
<requires lib="libadwaita" version="1.0"/>
@@ -15,7 +15,15 @@
<child>
<object class="AdwActionRow">
<property name="selectable">False</property>
<property name="title">Hardware Status</property>
<property name="title">Sensors</property>
</object>
</child>
<child>
<object class="AdwActionRow" id="hw_als">
<property name="title">Ambient Light Sensor</property>
<child>
<object class="GtkLabel" id="hw_als_label"/>
</child>
</object>
</child>
<child>
@@ -26,6 +34,30 @@
</child>
</object>
</child>
<child>
<object class="AdwActionRow" id="hw_fp_pwr">
<property name="title">Fingerprint</property>
<property name="visible">False</property>
<child>
<object class="GtkBox">
<property name="halign">end</property>
<property name="homogeneous">True</property>
<property name="spacing">5</property>
<property name="valign">center</property>
<child>
<object class="GtkButton" id="hw_fp_pwr_en">
<property name="label">Enable</property>
</object>
</child>
<child>
<object class="GtkButton" id="hw_fp_pwr_dis">
<property name="label">Disable</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="AdwActionRow" id="hw_priv_cam">
<property name="subtitle">Use Privacy Switch</property>
@@ -65,23 +97,35 @@
</object>
</child>
<child>
<object class="AdwActionRow" id="hw_fp_pwr">
<property name="title">Fingerprint</property>
<property name="visible">False</property>
<object class="AdwActionRow" id="hw_lid_open">
<property name="title">Lid Open</property>
<child>
<object class="GtkBox">
<property name="halign">end</property>
<property name="homogeneous">True</property>
<property name="spacing">5</property>
<property name="valign">center</property>
<child>
<object class="GtkButton" id="hw_fp_pwr_en">
<property name="label">Enable</property>
<object class="GtkSwitch" id="hw_lid_open_sw">
<property name="active">True</property>
<property name="sensitive">False</property>
</object>
</child>
</object>
</child>
</object>
</child>
<child>
<object class="AdwActionRow" id="hw_pwr_btn">
<property name="title">Power Button Pressed</property>
<child>
<object class="GtkBox">
<property name="halign">end</property>
<property name="homogeneous">True</property>
<property name="valign">center</property>
<child>
<object class="GtkButton" id="hw_fp_pwr_dis">
<property name="label">Disable</property>
<object class="GtkSwitch" id="hw_pwr_btn_sw">
<property name="active">True</property>
<property name="sensitive">False</property>
</object>
</child>
</object>

View File

@@ -69,52 +69,6 @@
<property name="selectable">False</property>
<property name="subtitle">These options break normal functionality</property>
<property name="title">Advanced Options</property>
<child>
<object class="AdwPreferencesGroup">
<property name="description">When using these options, the power LED may not turn off with the computer.</property>
<property name="margin-bottom">5</property>
<property name="margin-end">5</property>
<property name="margin-start">5</property>
<property name="margin-top">5</property>
<property name="title">Power Button LED</property>
<child>
<object class="AdwComboRow" id="led_pwr_colour">
<property name="model">
<object class="GtkStringList">
<items>
<item>Auto</item>
<item>Off</item>
</items>
</object>
</property>
<property name="title">Colour</property>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup">
<property name="description">When using these options, the charging indicator will no longer indicate charging.</property>
<property name="margin-bottom">5</property>
<property name="margin-end">5</property>
<property name="margin-start">5</property>
<property name="margin-top">5</property>
<property name="title">Charging Indicators</property>
<child>
<object class="AdwComboRow" id="led_chg_colour">
<property name="model">
<object class="GtkStringList">
<items>
<item>Auto</item>
<item>Off</item>
</items>
</object>
</property>
<property name="title">Colour</property>
</object>
</child>
</object>
</child>
</object>
</child>
<style>

View File

@@ -1,5 +1,5 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Created with Cambalache 0.96.1 -->
<!-- Created with Cambalache 0.96.3 -->
<interface>
<!-- interface-name thermals.ui -->
<!-- interface-description The Thermals page for YAFI -->
@@ -92,6 +92,12 @@
<property name="visible">False</property>
</object>
</child>
<child>
<object class="AdwExpanderRow" id="fan_set_points">
<property name="selectable">False</property>
<property name="title">Fan Set Points</property>
</object>
</child>
<style>
<class name="boxed-list"/>
</style>

View File

@@ -1,11 +1,11 @@
<?xml version='1.0' encoding='UTF-8' standalone='no'?>
<!DOCTYPE cambalache-project SYSTEM "cambalache-project.dtd">
<!-- Created with Cambalache 0.96.1 -->
<!-- Created with Cambalache 0.96.3 -->
<cambalache-project version="0.96.0" target_tk="gtk-4.0">
<ui template-class="YafiWindow" filename="yafi.ui" sha256="9d1b2f030e4a816eb0b1aa53ae1d80c5b50a2f4646e32c7a64803eb6f6ed3947"/>
<ui template-class="ThermalsPage" filename="thermals.ui" sha256="e301e65649005315ff60d250b60a47f6250ad6feb27db104051fcf0143cde173"/>
<ui template-class="LedsPage" filename="leds.ui" sha256="0350d22d570de039d63602ba40925f17fff6680fd5909d2ebf19600f351eb0f2"/>
<ui template-class="ThermalsPage" filename="thermals.ui" sha256="89f5b68da04abad587d8b949d18357cd956313680e663b10e5d42697f9bfbf6e"/>
<ui template-class="LedsPage" filename="leds.ui" sha256="abc3ee759974a5c92feb48cc258dbe7271d0402facf71fd5e779f2bb1a277e16"/>
<ui template-class="BatteryLimiterPage" filename="battery-limiter.ui" sha256="b5d41b19cb1fb7ca5b4bcfae43244e54111f5e8d8c51d95448d6a92b5185d2c4"/>
<ui template-class="HardwarePage" filename="hardware.ui" sha256="f4deec4e38e683fde97656802dbfb2a638ab46a0e36af5c9a37f277f49e2aabb"/>
<ui template-class="HardwarePage" filename="hardware.ui" sha256="37ea282198d9f60435f80e4adf8256cd2249e590dcad4b63af634d828673f1bf"/>
<ui template-class="BatteryPage" filename="battery.ui" sha256="d495280cb543a26cdee1c2939e5af980d8d3878c326e360eeab4f05195cdbdc9"/>
</cambalache-project>

View File

@@ -18,8 +18,7 @@
#
# SPDX-License-Identifier: GPL-2.0-or-later
from gi.repository import Adw
from gi.repository import Gtk
from gi.repository import Gtk, Adw
@Gtk.Template(resource_path='/au/stevetech/yafi/ui/yafi.ui')
class YafiWindow(Adw.ApplicationWindow):

Binary file not shown.