This commit is contained in:
Cristian Cezar Moisés 2026-05-01 09:58:47 -03:00
commit e5f5d32aab
124 changed files with 11892 additions and 2461 deletions

View file

@ -1,34 +1,21 @@
================================================================================
ZUPT-GUI — Cross-Platform Desktop Frontend for Zupt
MIT License
Copyright (C) 2026 Cristian Cezar Moisés
Contact for commercial licensing: sac@securityops.co
Copyright (c) 2026 Cristian Cezar Moisés
================================================================================
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
Zupt-GUI is free software: you can redistribute it and/or modify it under
the terms of the GNU Affero General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
Zupt-GUI is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
--------------------------------------------------------------------------------
COMMERCIAL LICENSING
--------------------------------------------------------------------------------
For commercial licensing — including embedding into proprietary appliances,
hosted backup-as-a-service products, or any other use that is incompatible
with the AGPL's source-disclosure requirements — please contact:
sac@securityops.co
The full AGPL-3.0 text is available in the parent directory's LICENSE file
and at <https://www.gnu.org/licenses/agpl-3.0.txt>.
================================================================================
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,6 +1,6 @@
# Zupt GUI — Cross-Platform Post-Quantum Backup
Desktop application for [zupt](https://github.com/cristiancmoises/zupt) backup compression with ML-KEM-768 + X25519 post-quantum hybrid encryption.
Desktop application for [zupt](https://git.securityops.co/cristiancmoises/zupt) backup compression with ML-KEM-768 + X25519 post-quantum hybrid encryption.
Works on GNU/Linux, BSD, macOS, and Windows.
@ -111,7 +111,7 @@ The GUI calls the zupt CLI binary — all cryptography runs in native C, not Pyt
|----------|--------|------|
| Any | pip | `pip install .` |
| Debian/Ubuntu/Mint | .deb | `packaging/deb/control` |
| Fedora/openSUSE | .rpm | `rpmbuild -ba packaging/rpm/zupt-gui.spec` |
| Fedora/RHEL | .rpm | `rpmbuild -ba packaging/rpm/zupt-gui.spec` |
| Universal Linux | .AppImage | `packaging/appimage/build-appimage.sh` |
| Sandboxed Linux | .flatpak | `packaging/flatpak/dev.zupt.gui.yml` |
| Windows | .exe installer | `packaging/windows/build-windows.bat` |
@ -119,11 +119,8 @@ The GUI calls the zupt CLI binary — all cryptography runs in native C, not Pyt
## Credits
- **zupt** v2.1.6 — Cristian Cezar Moises ([github](https://github.com/cristiancmoises/zupt))
- **libzupt** v1.0.2 — Alessandro de Oliveira Faria ([github](https://github.com/cabelo/libzupt))
- **zupt** v2.2.2 — Cristian Cezar Moisés ([github](https://git.securityops.co/cristiancmoises/zupt))
## License
**AGPL-3.0-or-later** — see [LICENSE-GUI](LICENSE-GUI) and the project-root [LICENSE](../LICENSE).
For commercial licensing inquiries, contact: **sac@securityops.co**
AGPL-3.0-or-later

2
gui/install.sh Normal file → Executable file
View file

@ -1,4 +1,6 @@
#!/bin/bash
# SPDX-License-Identifier: AGPL-3.0-or-later
# Copyright (c) 2025-2026 Cristian Cezar Moisés
# Install Zupt GUI + desktop integration
# Run: sudo ./install.sh (or ./install.sh --user for per-user install)
set -e

2
gui/packaging/appimage/build-appimage.sh Normal file → Executable file
View file

@ -1,4 +1,6 @@
#!/bin/bash
# SPDX-License-Identifier: AGPL-3.0-or-later
# Copyright (c) 2025-2026 Cristian Cezar Moisés
# Build Zupt GUI AppImage
# Requires: appimagetool, python3, pip
set -e

94
gui/packaging/build-gui-deb.sh Executable file
View file

@ -0,0 +1,94 @@
#!/bin/bash
# SPDX-License-Identifier: AGPL-3.0-or-later
# Copyright (c) 2025-2026 Cristian Cezar Moisés
# Build zupt-gui .deb package
set -e
cd "$(dirname "$0")/.."
VERSION="${VERSION:-2.2.1}"
ARCH="all"
PKG="zupt-gui_${VERSION}_${ARCH}"
ROOT="/tmp/$PKG"
rm -rf "$ROOT"
mkdir -p "$ROOT/DEBIAN" \
"$ROOT/usr/bin" \
"$ROOT/usr/lib/python3/dist-packages" \
"$ROOT/usr/share/applications" \
"$ROOT/usr/share/icons/hicolor/256x256/apps" \
"$ROOT/usr/share/doc/zupt-gui"
# Python module
install -m 644 src/zupt_gui.py "$ROOT/usr/lib/python3/dist-packages/zupt_gui.py"
# Launcher
cat > "$ROOT/usr/bin/zupt-gui" <<'LAUNCH'
#!/usr/bin/env python3
import sys
sys.path.insert(0, "/usr/lib/python3/dist-packages")
from zupt_gui import main
sys.exit(main())
LAUNCH
chmod +x "$ROOT/usr/bin/zupt-gui"
# Desktop file
install -m 644 packaging/zupt-gui.desktop "$ROOT/usr/share/applications/" 2>/dev/null || cat > "$ROOT/usr/share/applications/zupt-gui.desktop" <<'DESK'
[Desktop Entry]
Name=Zupt GUI
GenericName=Post-Quantum Backup Utility
Comment=Compress and encrypt files with hybrid PQ crypto
Exec=zupt-gui
Terminal=false
Type=Application
Categories=Utility;Archiving;Security;
Icon=zupt-gui
DESK
# Icon (use a real one if assets exist)
if [ -f assets/zupt-256.png ]; then
install -m 644 assets/zupt-256.png "$ROOT/usr/share/icons/hicolor/256x256/apps/zupt-gui.png"
elif [ -d ../assets ] && [ -f ../assets/zupt-256.png ]; then
install -m 644 ../assets/zupt-256.png "$ROOT/usr/share/icons/hicolor/256x256/apps/zupt-gui.png"
else
# 1×1 placeholder
printf '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\rIDATx\x9cc\xfc\xcf\xc0\x00\x00\x00\x05\x00\x01\xa5\xf6E@\x00\x00\x00\x00IEND\xaeB`\x82' > "$ROOT/usr/share/icons/hicolor/256x256/apps/zupt-gui.png"
fi
# Docs
install -m 644 README.md "$ROOT/usr/share/doc/zupt-gui/"
gzip -9n -c ../CHANGELOG.md > "$ROOT/usr/share/doc/zupt-gui/changelog.gz" 2>/dev/null || true
cat > "$ROOT/usr/share/doc/zupt-gui/copyright" <<'COPYRIGHT'
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: zupt-gui
Upstream-Contact: Cristian Cezar Moisés <zupt@riseup.net>
Source: https://git.securityops.co/cristiancmoises/zupt
Files: *
Copyright: 2025-2026 Cristian Cezar Moisés
License: AGPL-3.0+
COPYRIGHT
INSTALLED_SIZE=$(du -sk "$ROOT" | cut -f1)
cat > "$ROOT/DEBIAN/control" <<EOF
Package: zupt-gui
Version: $VERSION
Section: utils
Priority: optional
Architecture: $ARCH
Depends: python3 (>= 3.9), python3-pyside6, zupt (>= 2.2.0)
Maintainer: Cristian Cezar Moisés <zupt@riseup.net>
Installed-Size: $INSTALLED_SIZE
Homepage: https://git.securityops.co/cristiancmoises/zupt
Description: Zupt GUI — Post-Quantum Backup Utility
Cross-platform graphical interface for the zupt backup compression
utility with post-quantum hybrid encryption (ML-KEM-768 + X25519).
.
v2.2+ uses libzuptsdk under the hood for HKDF-SHA3 hybrid combiner,
32-byte key commitment, HPKE binding (RFC 9180), and anti-fault
double-decapsulation. Supports legacy archives via auto-detection.
EOF
dpkg-deb --build --root-owner-group "$ROOT" "/tmp/$PKG.deb"
echo "Built: /tmp/$PKG.deb"
dpkg-deb --info "/tmp/$PKG.deb" | head -12

View file

@ -1,3 +1,5 @@
# SPDX-License-Identifier: AGPL-3.0-or-later
# Copyright (c) 2026 Cristian Cezar Moisés
app-id: dev.zupt.gui
runtime: org.freedesktop.Platform
runtime-version: '24.08'
@ -24,7 +26,7 @@ modules:
- install -Dm755 zupt /app/bin/zupt
sources:
- type: git
url: https://github.com/cristiancmoises/zupt
url: https://git.securityops.co/cristiancmoises/zupt
tag: v2.1.6
- name: zupt-gui

View file

@ -1,43 +0,0 @@
Name: zupt-gui
Version: 1.0.0
Release: 1%{?dist}
Summary: Zupt GUI Post-Quantum Backup Utility
License: AGPL-3.0-or-later
URL: https://github.com/cristiancmoises/zupt
Source0: %{name}-%{version}.tar.gz
BuildArch: noarch
Requires: python3 >= 3.9
Requires: python3-pyside6
Requires: zupt >= 2.1.7
%description
Cross-platform graphical interface for the zupt backup compression
utility with post-quantum hybrid encryption (ML-KEM-768 + X25519),
hardware-adaptive codecs, block-level deduplication, and full-disk
backup/restore support.
%install
mkdir -p %{buildroot}%{_bindir}
mkdir -p %{buildroot}%{_datadir}/zupt-gui
mkdir -p %{buildroot}%{_datadir}/applications
mkdir -p %{buildroot}%{_datadir}/icons/hicolor/256x256/apps
install -m 755 src/zupt_gui.py %{buildroot}%{_datadir}/zupt-gui/zupt_gui.py
install -m 644 packaging/zupt-gui.desktop %{buildroot}%{_datadir}/applications/
cat > %{buildroot}%{_bindir}/zupt-gui << 'EOF'
#!/bin/sh
exec python3 %{_datadir}/zupt-gui/zupt_gui.py "$@"
EOF
chmod 755 %{buildroot}%{_bindir}/zupt-gui
%files
%license LICENSE
%{_bindir}/zupt-gui
%{_datadir}/zupt-gui/
%{_datadir}/applications/zupt-gui.desktop
%changelog
* Mon Apr 21 2026 Cristian Cezar Moises - 1.0.0-1
- Initial release

View file

@ -1,14 +1,16 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: AGPL-3.0-or-later
# Copyright (c) 2025-2026 Cristian Cezar Moisés
from setuptools import setup, find_packages
setup(
name="zupt-gui",
version="1.0.0",
version="1.1.1",
description="Zupt GUI — Cross-Platform Post-Quantum Backup Utility",
long_description=open("README.md").read(),
long_description_content_type="text/markdown",
author="Cristian Cezar Moisés",
url="https://github.com/cristiancmoises/zupt",
url="https://git.securityops.co/cristiancmoises/zupt",
license="AGPL-3.0-or-later",
packages=find_packages(where="src"),
package_dir={"": "src"},

View file

@ -1,15 +1,47 @@
#!/usr/bin/env python3
"""Zupt GUI — Cross-Platform Post-Quantum Backup. Requires PySide6."""
# SPDX-License-Identifier: AGPL-3.0-or-later
# Copyright (c) 2025-2026 Cristian Cezar Moisés
"""Zupt GUI — Cross-Platform Post-Quantum Backup.
Tries PySide6 first (preferred), falls back to PyQt6 if PySide6 is
not installed. PyQt6 is the default available package on Debian/Ubuntu
without requiring pip; PySide6 ships with broader signal/slot semantics
but the API surface used here is portable between the two.
"""
import sys, os, subprocess, shutil
from pathlib import Path
from PySide6.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QLabel, QPushButton, QLineEdit, QComboBox, QFileDialog,
QTextEdit, QProgressBar, QTabWidget, QFrame, QCheckBox,
QSpinBox, QMessageBox, QStatusBar, QScrollArea
)
from PySide6.QtCore import Qt, Signal, QObject, QThread
from PySide6.QtGui import QPalette, QColor, QIcon, QPixmap
# Try PySide6, fall back to PyQt6. The two have nearly-identical APIs;
# the only adjustment needed is Signal/pyqtSignal naming, handled below.
try:
from PySide6.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QLabel, QPushButton, QLineEdit, QComboBox, QFileDialog,
QTextEdit, QProgressBar, QTabWidget, QFrame, QCheckBox,
QSpinBox, QMessageBox, QStatusBar, QScrollArea
)
from PySide6.QtCore import Qt, Signal, QObject, QThread
from PySide6.QtGui import QPalette, QColor, QIcon, QPixmap
QT_BINDING = "PySide6"
except ImportError:
try:
from PyQt6.QtWidgets import (
QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QLabel, QPushButton, QLineEdit, QComboBox, QFileDialog,
QTextEdit, QProgressBar, QTabWidget, QFrame, QCheckBox,
QSpinBox, QMessageBox, QStatusBar, QScrollArea
)
from PyQt6.QtCore import Qt, pyqtSignal as Signal, QObject, QThread
from PyQt6.QtGui import QPalette, QColor, QIcon, QPixmap
QT_BINDING = "PyQt6"
except ImportError:
sys.stderr.write(
"ERROR: zupt-gui requires PySide6 or PyQt6. Install one of:\n"
" Debian/Ubuntu: sudo apt install python3-pyqt6\n"
" Fedora/RHEL: sudo dnf install python3-pyqt6\n"
" pip (any OS): pip install PySide6\n"
)
sys.exit(1)
# ── Find zupt binary ──
def _find_zupt():
@ -146,7 +178,7 @@ def Log(h=150):
t = QTextEdit(); t.setReadOnly(True); t.setMaximumHeight(h); return t
def PwField(ph="Optional"):
pw = QLineEdit(); pw.setEchoMode(QLineEdit.Password); pw.setPlaceholderText(ph); return pw
pw = QLineEdit(); pw.setEchoMode(QLineEdit.EchoMode.Password); pw.setPlaceholderText(ph); return pw
class PathField(QWidget):
def __init__(self, ph="", mode="open", filters="All Files (*)"):
@ -178,7 +210,7 @@ class PathField(QWidget):
if ps: self.edit.setText("|".join(ps) if self.mode == "multi" else ps[0])
def scrollable(w):
sa = QScrollArea(); sa.setWidgetResizable(True); sa.setWidget(w); sa.setFrameShape(QFrame.NoFrame); return sa
sa = QScrollArea(); sa.setWidgetResizable(True); sa.setWidget(w); sa.setFrameShape(QFrame.Shape.NoFrame); return sa
def run_async(parent, cmd, btn, log, progress=None):
log.clear(); btn.setEnabled(False)
@ -213,6 +245,11 @@ class KeysTab(QWidget):
self.gen_priv = PathField("e.g. ~/zupt_private.key", "save", "Key (*.key);;All (*)")
v.addWidget(self.gen_priv)
self.gen_sdk = QCheckBox("SDK v2 format (HKDF combiner + commitment + HPKE — recommended)")
self.gen_sdk.setChecked(True)
self.gen_sdk.setToolTip("Generates a libzuptsdk-format keypair. Use --pq-sdk in CLI or 'SDK v2' checkbox in compress to use these keys. Disable for legacy --pq compatibility.")
v.addWidget(self.gen_sdk)
self.gen_btn = QPushButton("Generate Keypair")
self.gen_btn.clicked.connect(self._generate)
v.addWidget(self.gen_btn)
@ -245,16 +282,25 @@ class KeysTab(QWidget):
p = self.gen_priv.path() or str(Path.home() / "zupt_private.key")
self.gen_priv.edit.setText(p)
self.gen_log.clear(); self.gen_btn.setEnabled(False)
code, _, err = run_zupt(["keygen", "-o", p])
self.gen_log.append(err.strip())
if code == 0:
pub = p.rsplit(".", 1)[0] + "_public.key" if "." in p else p + ".pub"
c2, _, e2 = run_zupt(["keygen", "--pub", "-o", pub, "-k", p])
self.gen_log.append(e2.strip())
if c2 == 0:
self.gen_log.append(f"\nPrivate key: {p}\nPublic key: {pub}")
if self.gen_sdk.isChecked():
# SDK keygen creates both files in one step.
code, _, err = run_zupt(["keygen", "--sdk", "-o", p])
self.gen_log.append(err.strip())
if code == 0:
self.gen_log.append(f"\nPrivate key: {p}\nPublic key: {p}.pub")
else:
self.gen_log.append("\nFailed.")
else:
self.gen_log.append("\nFailed.")
code, _, err = run_zupt(["keygen", "-o", p])
self.gen_log.append(err.strip())
if code == 0:
pub = p.rsplit(".", 1)[0] + "_public.key" if "." in p else p + ".pub"
c2, _, e2 = run_zupt(["keygen", "--pub", "-o", pub, "-k", p])
self.gen_log.append(e2.strip())
if c2 == 0:
self.gen_log.append(f"\nPrivate key: {p}\nPublic key: {pub}")
else:
self.gen_log.append("\nFailed.")
self.gen_btn.setEnabled(True)
def _export(self):
@ -296,7 +342,11 @@ class CompressTab(QWidget):
v.addLayout(row); v.addWidget(Sep())
enc = QHBoxLayout(); enc.setSpacing(16)
pw = QVBoxLayout(); pw.addWidget(H("Password")); self.pw = PwField("AES-256"); pw.addWidget(self.pw); enc.addLayout(pw)
pq = QVBoxLayout(); pq.addWidget(H("PQ public key")); self.pq = PathField("Optional .key", filters="Key (*.key);;All (*)"); pq.addWidget(self.pq); enc.addLayout(pq)
pq = QVBoxLayout(); pq.addWidget(H("PQ public key")); self.pq = PathField("Optional .key", filters="Key (*.key *.pub);;All (*)"); pq.addWidget(self.pq); enc.addLayout(pq)
sdk_box = QVBoxLayout(); sdk_box.addWidget(H("Mode"))
self.sdk = QCheckBox("Use SDK v2 (HKDF + commitment + HPKE)"); self.sdk.setChecked(True)
self.sdk.setToolTip("v2.2+ uses libzuptsdk: HKDF-SHA3 combiner, key commitment, HPKE binding, Argon2id. Disable for legacy --pq compatibility.")
sdk_box.addWidget(self.sdk); sdk_box.addStretch(); enc.addLayout(sdk_box)
v.addLayout(enc)
self.btn = QPushButton("Compress"); self.btn.clicked.connect(self._run); v.addWidget(self.btn)
self.progress = QProgressBar(); self.progress.setRange(0,0); self.progress.hide(); v.addWidget(self.progress)
@ -319,7 +369,9 @@ class CompressTab(QWidget):
if self.dedup.isChecked(): cmd.append("--dedup")
if self.solid.isChecked(): cmd.append("--solid")
if self.pw.text(): cmd += ["-p", self.pw.text()]
if self.pq.path(): cmd += ["--pq", self.pq.path()]
if self.pq.path():
flag = "--pq-sdk" if self.sdk.isChecked() else "--pq"
cmd += [flag, self.pq.path()]
cmd.append(dst); cmd.extend(srcs)
run_async(self, cmd, self.btn, self.log, self.progress)
@ -337,6 +389,10 @@ class ExtractTab(QWidget):
enc = QHBoxLayout(); enc.setSpacing(16)
pw = QVBoxLayout(); pw.addWidget(H("Password")); self.pw = PwField(); pw.addWidget(self.pw); enc.addLayout(pw)
pq = QVBoxLayout(); pq.addWidget(H("PQ private key")); self.pq = PathField("Optional .key", filters="Key (*.key);;All (*)"); pq.addWidget(self.pq); enc.addLayout(pq)
sdk_box = QVBoxLayout(); sdk_box.addWidget(H("Mode"))
self.sdk = QCheckBox("Auto-detect (SDK or legacy)"); self.sdk.setChecked(True)
self.sdk.setToolTip("Tries --pq-sdk first, falls back to --pq for legacy archives.")
sdk_box.addWidget(self.sdk); sdk_box.addStretch(); enc.addLayout(sdk_box)
v.addLayout(enc)
self.btn = QPushButton("Extract"); self.btn.setObjectName("green"); self.btn.clicked.connect(self._run); v.addWidget(self.btn)
self.progress = QProgressBar(); self.progress.setRange(0,0); self.progress.hide(); v.addWidget(self.progress)
@ -350,7 +406,12 @@ class ExtractTab(QWidget):
cmd = ["extract"]
if self.out.path(): cmd += ["-o", self.out.path()]
if self.pw.text(): cmd += ["-p", self.pw.text()]
if self.pq.path(): cmd += ["--pq", self.pq.path()]
if self.pq.path():
# Auto-detect: zupt's extract auto-discovers enc type from header,
# so passing --pq-sdk works for both SDK and legacy keyfiles when
# the archive is SDK-encoded; --pq is needed for legacy archives.
flag = "--pq-sdk" if self.sdk.isChecked() else "--pq"
cmd += [flag, self.pq.path()]
cmd.append(arc)
run_async(self, cmd, self.btn, self.log, self.progress)
@ -433,7 +494,8 @@ class DiskTab(QWidget):
def _restore(self):
a, t = self.rarc.path(), self.rtgt.path()
if not a or not t: QMessageBox.warning(self, "Zupt", "Set archive and target."); return
if QMessageBox.warning(self, "Confirm", f"OVERWRITE {t}?", QMessageBox.Yes|QMessageBox.Cancel) != QMessageBox.Yes: return
SB = QMessageBox.StandardButton
if QMessageBox.warning(self, "Confirm", f"OVERWRITE {t}?", SB.Yes|SB.Cancel) != SB.Yes: return
cmd = ["disk", "restore"]
if self.rpw.text(): cmd += ["-p", self.rpw.text()]
cmd += [a, t]; run_async(self, cmd, self.rbtn, self.rlog)
@ -462,11 +524,11 @@ class AboutTab(QWidget):
("SHA3/SHAKE FIPS 202 Hash / XOF", "color:#5a7a88;font-size:12px;font-family:monospace;"),
("", ""),
("CREDITS", "color:#00dde0;font-size:10px;font-weight:700;letter-spacing:2px;font-family:monospace;"),
("zupt Cristian Cezar Moises AGPL-3.0", "color:#5a7a88;font-size:12px;font-family:monospace;"),
("github.com/cristiancmoises/zupt", "color:#3a5868;font-size:11px;font-family:monospace;"),
("zupt Cristian Cezar Moises MIT", "color:#5a7a88;font-size:12px;font-family:monospace;"),
("git.securityops.co/cristiancmoises/zupt", "color:#3a5868;font-size:11px;font-family:monospace;"),
("", ""),
("libzupt Alessandro de Oliveira Faria MIT", "color:#5a7a88;font-size:12px;font-family:monospace;"),
("github.com/cabelo/libzupt", "color:#3a5868;font-size:11px;font-family:monospace;"),
("zupt Cristian Cezar Moises AGPL-3.0+", "color:#5a7a88;font-size:12px;font-family:monospace;"),
("git.securityops.co/cristiancmoises/zupt", "color:#3a5868;font-size:11px;font-family:monospace;"),
("", ""),
("WEBSITE", "color:#00dde0;font-size:10px;font-weight:700;letter-spacing:2px;font-family:monospace;"),
("https://zupt.securityops.co", "color:#5a7a88;font-size:12px;font-family:monospace;"),
@ -555,10 +617,13 @@ def main():
app.setStyle("Fusion")
app.setStyleSheet(STYLE)
pal = QPalette()
for role, c in [(QPalette.Window,"#0a0a0a"),(QPalette.WindowText,"#90acb8"),
(QPalette.Base,"#0e1820"),(QPalette.Text,"#c0dce8"),
(QPalette.Button,"#0a2028"),(QPalette.ButtonText,"#00dde0"),
(QPalette.Highlight,"#004858"),(QPalette.HighlightedText,"#00dde0")]:
# PyQt6 requires scoped enums (QPalette.ColorRole.Window); PySide6 supports
# both forms but we use the scoped form for cross-binding compatibility.
CR = QPalette.ColorRole
for role, c in [(CR.Window,"#0a0a0a"),(CR.WindowText,"#90acb8"),
(CR.Base,"#0e1820"),(CR.Text,"#c0dce8"),
(CR.Button,"#0a2028"),(CR.ButtonText,"#00dde0"),
(CR.Highlight,"#004858"),(CR.HighlightedText,"#00dde0")]:
pal.setColor(role, QColor(c))
app.setPalette(pal)
win = ZuptWindow(compress_files=compress_files, extract_file=extract_file)

0
gui/zupt-gui Normal file → Executable file
View file