[RELEASE] Final version.
85
.gitignore
vendored
@@ -1,17 +1,94 @@
|
|||||||
venv/
|
# ── Python ────────────────────────────────────────────────────────────────────
|
||||||
__pycache__/
|
__pycache__/
|
||||||
*.pyc
|
*.py[cod]
|
||||||
*.pyo
|
*$py.class
|
||||||
*.pyd
|
*.pyd
|
||||||
.Python
|
*.pyo
|
||||||
|
*.so
|
||||||
|
*.egg
|
||||||
*.egg-info/
|
*.egg-info/
|
||||||
|
.eggs/
|
||||||
|
pip-wheel-metadata/
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# ── Virtual environments ──────────────────────────────────────────────────────
|
||||||
|
venv/
|
||||||
|
.venv/
|
||||||
|
env/
|
||||||
|
.env/
|
||||||
|
ENV/
|
||||||
|
|
||||||
|
# ── Distribution / build ──────────────────────────────────────────────────────
|
||||||
dist/
|
dist/
|
||||||
build/
|
build/
|
||||||
*.spec
|
*.spec
|
||||||
|
|
||||||
|
# ── PyInstaller work files (keep *.spec only if custom; generated ones are noise)
|
||||||
|
# Uncomment if you commit a hand-crafted spec file:
|
||||||
|
# !APIClient-Agent.spec
|
||||||
|
|
||||||
|
# ── Database & secrets ───────────────────────────────────────────────────────
|
||||||
*.db
|
*.db
|
||||||
*.sqlite
|
*.sqlite
|
||||||
*.sqlite3
|
*.sqlite3
|
||||||
.env
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
secrets.json
|
||||||
|
credentials.json
|
||||||
|
|
||||||
|
# ── Logs ─────────────────────────────────────────────────────────────────────
|
||||||
*.log
|
*.log
|
||||||
|
logs/
|
||||||
|
|
||||||
|
# ── OS artifacts ─────────────────────────────────────────────────────────────
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
.DS_Store?
|
||||||
|
._*
|
||||||
|
.Spotlight-V100
|
||||||
|
.Trashes
|
||||||
|
ehthumbs.db
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
desktop.ini
|
||||||
|
|
||||||
|
# ── IDE / editor ─────────────────────────────────────────────────────────────
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
.project
|
||||||
|
.pydevproject
|
||||||
|
*.sublime-project
|
||||||
|
*.sublime-workspace
|
||||||
|
|
||||||
|
# ── Testing & coverage ───────────────────────────────────────────────────────
|
||||||
|
.pytest_cache/
|
||||||
|
.mypy_cache/
|
||||||
|
.ruff_cache/
|
||||||
|
htmlcov/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
nosetests.xml
|
||||||
|
test-results/
|
||||||
|
|
||||||
|
# ── Jupyter ───────────────────────────────────────────────────────────────────
|
||||||
|
.ipynb_checkpoints/
|
||||||
|
*.ipynb
|
||||||
|
|
||||||
|
# ── Packaging scratch ─────────────────────────────────────────────────────────
|
||||||
|
*.tar.gz
|
||||||
|
*.zip
|
||||||
|
*.deb
|
||||||
|
*.rpm
|
||||||
|
*.dmg
|
||||||
|
*.AppImage
|
||||||
|
*.exe
|
||||||
|
*.msi
|
||||||
|
|
||||||
|
# ── Assets: generated logo PNGs are committed (app requires them at runtime).
|
||||||
|
# Only ignore temp/intermediate files produced during asset generation.
|
||||||
|
assets/*.webp
|
||||||
|
assets/app-ss.png
|
||||||
|
|||||||
25
LICENSE
@@ -1,6 +1,6 @@
|
|||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2026 EKIKA.co
|
Copyright (c) 2025-2026 EKIKA.co
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -19,3 +19,26 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
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
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
SOFTWARE.
|
SOFTWARE.
|
||||||
|
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Third-Party Notices
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
This software bundles or depends on the following open-source packages.
|
||||||
|
Each package retains its own license.
|
||||||
|
|
||||||
|
PyQt6 - GPL v3 / Commercial https://riverbankcomputing.com/software/pyqt
|
||||||
|
httpx - BSD 3-Clause https://github.com/encode/httpx
|
||||||
|
websockets - BSD 3-Clause https://github.com/python-websockets/websockets
|
||||||
|
anthropic-sdk - MIT https://github.com/anthropics/anthropic-sdk-python
|
||||||
|
PyYAML - MIT https://github.com/yaml/pyyaml
|
||||||
|
PyInstaller - GPL v2+ with bootloader exception
|
||||||
|
https://github.com/pyinstaller/pyinstaller
|
||||||
|
|
||||||
|
Font licenses
|
||||||
|
Quicksand - Open Font License 1.1 https://fonts.google.com/specimen/Quicksand
|
||||||
|
Open Sans - Apache License 2.0 https://fonts.google.com/specimen/Open+Sans
|
||||||
|
|
||||||
|
The full text of each third-party license is available in the respective
|
||||||
|
package source repository or distribution.
|
||||||
|
|||||||
@@ -59,21 +59,16 @@ class EnvBar(QWidget):
|
|||||||
layout.setContentsMargins(12, 0, 12, 0)
|
layout.setContentsMargins(12, 0, 12, 0)
|
||||||
layout.setSpacing(6)
|
layout.setSpacing(6)
|
||||||
|
|
||||||
# EKIKA logo
|
# App logo icon
|
||||||
logo_path = os.path.join(_ASSETS_DIR, "ekika_logo.png")
|
app_logo_path = os.path.join(_ASSETS_DIR, "app_logo_32.png")
|
||||||
if os.path.exists(logo_path):
|
if os.path.exists(app_logo_path):
|
||||||
logo_lbl = QLabel()
|
logo_lbl = QLabel()
|
||||||
logo_lbl.setObjectName("ekikaLogo")
|
logo_lbl.setObjectName("appLogo")
|
||||||
pix = QPixmap(logo_path)
|
pix = QPixmap(app_logo_path)
|
||||||
logo_lbl.setPixmap(pix.scaledToHeight(26, Qt.TransformationMode.SmoothTransformation))
|
logo_lbl.setPixmap(pix.scaledToHeight(32, Qt.TransformationMode.SmoothTransformation))
|
||||||
logo_lbl.setToolTip("EKIKA")
|
logo_lbl.setToolTip("APIClient - Agent")
|
||||||
layout.addWidget(logo_lbl)
|
layout.addWidget(logo_lbl)
|
||||||
# Thin separator
|
layout.addSpacing(4)
|
||||||
sep = QLabel("|")
|
|
||||||
sep.setObjectName("brandSub")
|
|
||||||
sep.setFixedWidth(12)
|
|
||||||
sep.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
||||||
layout.addWidget(sep)
|
|
||||||
|
|
||||||
brand = QLabel("APIClient")
|
brand = QLabel("APIClient")
|
||||||
brand.setObjectName("brandName")
|
brand.setObjectName("brandName")
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 137 KiB |
BIN
assets/app_logo.png
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
assets/app_logo_16.png
Normal file
|
After Width: | Height: | Size: 864 B |
BIN
assets/app_logo_32.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
assets/app_logo_48.png
Normal file
|
After Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 5.1 KiB |
@@ -1,34 +1,258 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
set -e
|
# =============================================================================
|
||||||
|
# build_installer.sh - Build APIClient-Agent standalone distribution
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ./build_installer.sh [--onefile] [--clean] [--deb] [--dmg]
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
# --onefile Bundle into a single executable (larger, slower start) instead
|
||||||
|
# of the default one-directory layout (recommended for Linux/Mac)
|
||||||
|
# --clean Remove previous dist/ and build/ before building
|
||||||
|
# --deb Also build a .deb package after the build (requires fpm)
|
||||||
|
# --dmg Also build a .dmg image after the build (macOS only, requires
|
||||||
|
# create-dmg: brew install create-dmg)
|
||||||
|
#
|
||||||
|
# Requirements:
|
||||||
|
# Python 3.11+, a virtual environment at ./venv, and all pip deps installed.
|
||||||
|
# PyInstaller is installed automatically from requirements.txt.
|
||||||
|
# =============================================================================
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
echo "=== Building API Client installer ==="
|
APP_NAME="APIClient-Agent"
|
||||||
|
APP_VERSION="2.0.0"
|
||||||
|
APP_BUNDLE_ID="co.ekika.apiclient-agent"
|
||||||
|
MAIN_SCRIPT="main.py"
|
||||||
|
ICON_PNG="assets/app_logo.png"
|
||||||
|
ICON_ICO="assets/app_logo.ico"
|
||||||
|
ICON_ICNS="assets/app_logo.icns"
|
||||||
|
|
||||||
# Install deps
|
PYTHON="${PYTHON:-venv/bin/python}"
|
||||||
pip install -r requirements.txt
|
PIP="${PIP:-venv/bin/pip}"
|
||||||
|
|
||||||
# Build with PyInstaller
|
# ── Argument parsing ──────────────────────────────────────────────────────────
|
||||||
pyinstaller \
|
OPT_ONEFILE=false
|
||||||
--onedir \
|
OPT_CLEAN=false
|
||||||
--windowed \
|
OPT_DEB=false
|
||||||
--name "APIClient" \
|
OPT_DMG=false
|
||||||
--add-data "app:app" \
|
|
||||||
main.py
|
|
||||||
|
|
||||||
echo ""
|
for arg in "$@"; do
|
||||||
echo "=== Build complete ==="
|
case $arg in
|
||||||
echo "Executable: dist/APIClient/APIClient"
|
--onefile) OPT_ONEFILE=true ;;
|
||||||
echo ""
|
--clean) OPT_CLEAN=true ;;
|
||||||
|
--deb) OPT_DEB=true ;;
|
||||||
|
--dmg) OPT_DMG=true ;;
|
||||||
|
*)
|
||||||
|
echo "Unknown option: $arg"
|
||||||
|
echo "Usage: $0 [--onefile] [--clean] [--deb] [--dmg]"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
# Optional: create .deb (requires fpm: gem install fpm)
|
# ── Helpers ───────────────────────────────────────────────────────────────────
|
||||||
if command -v fpm &> /dev/null; then
|
info() { echo -e "\033[1;34m[INFO]\033[0m $*"; }
|
||||||
echo "Creating .deb package..."
|
ok() { echo -e "\033[1;32m[ OK ]\033[0m $*"; }
|
||||||
fpm -s dir -t deb \
|
warn() { echo -e "\033[1;33m[WARN]\033[0m $*"; }
|
||||||
-n api-client \
|
die() { echo -e "\033[1;31m[FAIL]\033[0m $*" >&2; exit 1; }
|
||||||
-v 1.0.0 \
|
|
||||||
--description "Postman-like API client" \
|
# ── Platform detection ────────────────────────────────────────────────────────
|
||||||
dist/APIClient/=/opt/api-client \
|
OS="$(uname -s)"
|
||||||
--after-install /dev/null
|
case "$OS" in
|
||||||
echo "Package: api-client_1.0.0_amd64.deb"
|
Linux*) PLATFORM=linux ;;
|
||||||
else
|
Darwin*) PLATFORM=macos ;;
|
||||||
echo "Tip: install fpm (gem install fpm) to also generate a .deb package"
|
MINGW*|CYGWIN*|MSYS*) PLATFORM=windows ;;
|
||||||
|
*) die "Unsupported platform: $OS" ;;
|
||||||
|
esac
|
||||||
|
info "Platform: $PLATFORM"
|
||||||
|
|
||||||
|
# ── Preflight checks ──────────────────────────────────────────────────────────
|
||||||
|
[[ -f "$MAIN_SCRIPT" ]] || die "$MAIN_SCRIPT not found. Run from the project root."
|
||||||
|
[[ -d "venv" ]] || die "Virtual environment 'venv/' not found. Create it first:
|
||||||
|
python3 -m venv venv && venv/bin/pip install -r requirements.txt"
|
||||||
|
[[ -f "requirements.txt" ]] || die "requirements.txt not found."
|
||||||
|
|
||||||
|
info "Python: $($PYTHON --version)"
|
||||||
|
|
||||||
|
# ── Clean ─────────────────────────────────────────────────────────────────────
|
||||||
|
if $OPT_CLEAN; then
|
||||||
|
info "Cleaning previous build artifacts..."
|
||||||
|
rm -rf dist/ build/ "${APP_NAME}.spec"
|
||||||
|
ok "Cleaned."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# ── Install / upgrade dependencies ───────────────────────────────────────────
|
||||||
|
info "Installing/verifying dependencies..."
|
||||||
|
$PIP install -q -r requirements.txt
|
||||||
|
ok "Dependencies ready."
|
||||||
|
|
||||||
|
# ── Prepare icon ─────────────────────────────────────────────────────────────
|
||||||
|
# PyInstaller needs .ico on Windows, .icns on macOS, .png on Linux.
|
||||||
|
ICON_ARG=""
|
||||||
|
if [[ "$PLATFORM" == "windows" ]]; then
|
||||||
|
if [[ -f "$ICON_ICO" ]]; then
|
||||||
|
ICON_ARG="--icon=$ICON_ICO"
|
||||||
|
else
|
||||||
|
warn ".ico icon not found at $ICON_ICO - building without icon."
|
||||||
|
fi
|
||||||
|
elif [[ "$PLATFORM" == "macos" ]]; then
|
||||||
|
if [[ -f "$ICON_ICNS" ]]; then
|
||||||
|
ICON_ARG="--icon=$ICON_ICNS"
|
||||||
|
elif [[ -f "$ICON_PNG" ]]; then
|
||||||
|
# Convert PNG -> ICNS using sips + iconutil if available
|
||||||
|
if command -v sips &>/dev/null && command -v iconutil &>/dev/null; then
|
||||||
|
info "Converting PNG to ICNS..."
|
||||||
|
ICONSET_DIR="/tmp/${APP_NAME}.iconset"
|
||||||
|
mkdir -p "$ICONSET_DIR"
|
||||||
|
for sz in 16 32 64 128 256 512; do
|
||||||
|
sips -z $sz $sz "$ICON_PNG" --out "$ICONSET_DIR/icon_${sz}x${sz}.png" &>/dev/null
|
||||||
|
sips -z $((sz*2)) $((sz*2)) "$ICON_PNG" --out "$ICONSET_DIR/icon_${sz}x${sz}@2x.png" &>/dev/null
|
||||||
|
done
|
||||||
|
iconutil -c icns "$ICONSET_DIR" -o "$ICON_ICNS" && ok "ICNS created."
|
||||||
|
ICON_ARG="--icon=$ICON_ICNS"
|
||||||
|
else
|
||||||
|
warn "sips/iconutil not available; building without .icns icon."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
elif [[ "$PLATFORM" == "linux" ]]; then
|
||||||
|
[[ -f "$ICON_PNG" ]] && ICON_ARG="--icon=$ICON_PNG" || warn "PNG icon not found."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── PyInstaller arguments ─────────────────────────────────────────────────────
|
||||||
|
PYINSTALLER_ARGS=(
|
||||||
|
$([[ $OPT_ONEFILE == true ]] && echo "--onefile" || echo "--onedir")
|
||||||
|
"--windowed"
|
||||||
|
"--name" "$APP_NAME"
|
||||||
|
"--add-data" "app:app"
|
||||||
|
"--add-data" "assets:assets"
|
||||||
|
"--clean"
|
||||||
|
"--noconfirm"
|
||||||
|
)
|
||||||
|
[[ -n "$ICON_ARG" ]] && PYINSTALLER_ARGS+=("$ICON_ARG")
|
||||||
|
|
||||||
|
# macOS bundle metadata
|
||||||
|
if [[ "$PLATFORM" == "macos" ]]; then
|
||||||
|
PYINSTALLER_ARGS+=(
|
||||||
|
"--osx-bundle-identifier" "$APP_BUNDLE_ID"
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Hidden imports that PyInstaller may miss
|
||||||
|
PYINSTALLER_ARGS+=(
|
||||||
|
"--hidden-import" "PyQt6.QtSvg"
|
||||||
|
"--hidden-import" "PyQt6.QtNetwork"
|
||||||
|
"--hidden-import" "httpx._transports.default"
|
||||||
|
"--hidden-import" "websockets.legacy.client"
|
||||||
|
"--hidden-import" "anthropic"
|
||||||
|
)
|
||||||
|
|
||||||
|
PYINSTALLER_ARGS+=("$MAIN_SCRIPT")
|
||||||
|
|
||||||
|
# ── Build ─────────────────────────────────────────────────────────────────────
|
||||||
|
info "Running PyInstaller..."
|
||||||
|
$PYTHON -m PyInstaller "${PYINSTALLER_ARGS[@]}"
|
||||||
|
|
||||||
|
# ── Verify output ─────────────────────────────────────────────────────────────
|
||||||
|
if [[ $OPT_ONEFILE == true ]]; then
|
||||||
|
BIN="dist/${APP_NAME}"
|
||||||
|
[[ "$PLATFORM" == "windows" ]] && BIN="${BIN}.exe"
|
||||||
|
[[ -f "$BIN" ]] || die "Build failed: expected $BIN not found."
|
||||||
|
ok "Executable: $BIN ($(du -sh "$BIN" | cut -f1))"
|
||||||
|
else
|
||||||
|
OUT_DIR="dist/${APP_NAME}"
|
||||||
|
[[ -d "$OUT_DIR" ]] || die "Build failed: expected $OUT_DIR not found."
|
||||||
|
ok "Distribution directory: $OUT_DIR ($(du -sh "$OUT_DIR" | cut -f1))"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── .deb package ─────────────────────────────────────────────────────────────
|
||||||
|
if $OPT_DEB; then
|
||||||
|
if [[ "$PLATFORM" != "linux" ]]; then
|
||||||
|
warn ".deb packaging is only supported on Linux. Skipping."
|
||||||
|
elif ! command -v fpm &>/dev/null; then
|
||||||
|
warn "fpm not found. Install it with: gem install fpm
|
||||||
|
Then re-run with --deb."
|
||||||
|
else
|
||||||
|
info "Building .deb package..."
|
||||||
|
DEB_NAME="apiclient-agent_${APP_VERSION}_amd64.deb"
|
||||||
|
|
||||||
|
# Write a post-install script that creates a desktop entry
|
||||||
|
POSTINST=$(mktemp)
|
||||||
|
cat > "$POSTINST" <<'EOF'
|
||||||
|
#!/bin/bash
|
||||||
|
cat > /usr/share/applications/apiclient-agent.desktop <<DESK
|
||||||
|
[Desktop Entry]
|
||||||
|
Name=APIClient - Agent
|
||||||
|
Comment=AI-first API testing client
|
||||||
|
Exec=/opt/apiclient-agent/APIClient-Agent
|
||||||
|
Icon=/opt/apiclient-agent/_internal/assets/app_logo.png
|
||||||
|
Terminal=false
|
||||||
|
Type=Application
|
||||||
|
Categories=Development;Network;
|
||||||
|
Keywords=API;REST;HTTP;AI;
|
||||||
|
DESK
|
||||||
|
update-desktop-database /usr/share/applications 2>/dev/null || true
|
||||||
|
EOF
|
||||||
|
chmod +x "$POSTINST"
|
||||||
|
|
||||||
|
fpm \
|
||||||
|
--input-type dir \
|
||||||
|
--output-type deb \
|
||||||
|
--name "apiclient-agent" \
|
||||||
|
--version "$APP_VERSION" \
|
||||||
|
--architecture amd64 \
|
||||||
|
--maintainer "EKIKA.co <hello@ekika.co>" \
|
||||||
|
--description "APIClient - Agent: AI-first API testing desktop client (Python/PyQt6)" \
|
||||||
|
--url "https://git.ekika.co/EKIKA.co/APIClient-Agent" \
|
||||||
|
--license "MIT" \
|
||||||
|
--category "Development" \
|
||||||
|
--after-install "$POSTINST" \
|
||||||
|
--package "$DEB_NAME" \
|
||||||
|
"dist/${APP_NAME}/=/opt/apiclient-agent"
|
||||||
|
|
||||||
|
rm -f "$POSTINST"
|
||||||
|
[[ -f "$DEB_NAME" ]] && ok ".deb package: $DEB_NAME ($(du -sh "$DEB_NAME" | cut -f1))" \
|
||||||
|
|| warn ".deb build may have failed."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── .dmg image (macOS) ───────────────────────────────────────────────────────
|
||||||
|
if $OPT_DMG; then
|
||||||
|
if [[ "$PLATFORM" != "macos" ]]; then
|
||||||
|
warn ".dmg packaging is only supported on macOS. Skipping."
|
||||||
|
elif ! command -v create-dmg &>/dev/null; then
|
||||||
|
warn "create-dmg not found. Install it with: brew install create-dmg
|
||||||
|
Then re-run with --dmg."
|
||||||
|
else
|
||||||
|
info "Building .dmg image..."
|
||||||
|
DMG_NAME="${APP_NAME}-${APP_VERSION}.dmg"
|
||||||
|
APP_BUNDLE="dist/${APP_NAME}.app"
|
||||||
|
[[ -d "$APP_BUNDLE" ]] || die ".app bundle not found at $APP_BUNDLE"
|
||||||
|
|
||||||
|
create-dmg \
|
||||||
|
--volname "$APP_NAME $APP_VERSION" \
|
||||||
|
--window-pos 200 120 \
|
||||||
|
--window-size 600 400 \
|
||||||
|
--icon-size 100 \
|
||||||
|
--icon "${APP_NAME}.app" 175 190 \
|
||||||
|
--hide-extension "${APP_NAME}.app" \
|
||||||
|
--app-drop-link 425 190 \
|
||||||
|
"$DMG_NAME" \
|
||||||
|
"$APP_BUNDLE"
|
||||||
|
|
||||||
|
[[ -f "$DMG_NAME" ]] && ok ".dmg image: $DMG_NAME ($(du -sh "$DMG_NAME" | cut -f1))" \
|
||||||
|
|| warn ".dmg build may have failed."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── Summary ───────────────────────────────────────────────────────────────────
|
||||||
|
echo ""
|
||||||
|
echo " ┌─────────────────────────────────────────────┐"
|
||||||
|
printf " │ %-43s│\n" "${APP_NAME} v${APP_VERSION} build complete"
|
||||||
|
printf " │ Platform : %-31s│\n" "$PLATFORM"
|
||||||
|
if [[ $OPT_ONEFILE == true ]]; then
|
||||||
|
printf " │ Output : %-31s│\n" "dist/${APP_NAME}"
|
||||||
|
else
|
||||||
|
printf " │ Output : %-31s│\n" "dist/${APP_NAME}/"
|
||||||
|
fi
|
||||||
|
echo " └─────────────────────────────────────────────┘"
|
||||||
|
echo ""
|
||||||
|
|||||||
15
main.py
@@ -1,5 +1,6 @@
|
|||||||
import sys
|
import sys, os
|
||||||
from PyQt6.QtWidgets import QApplication
|
from PyQt6.QtWidgets import QApplication
|
||||||
|
from PyQt6.QtGui import QIcon
|
||||||
from app.core.fonts import load_fonts
|
from app.core.fonts import load_fonts
|
||||||
from app.ui.theme import apply
|
from app.ui.theme import apply
|
||||||
from app.ui.main_window import MainWindow
|
from app.ui.main_window import MainWindow
|
||||||
@@ -7,11 +8,23 @@ from app.ui.main_window import MainWindow
|
|||||||
APP_NAME = "APIClient - Agent"
|
APP_NAME = "APIClient - Agent"
|
||||||
APP_VERSION = "2.0.0"
|
APP_VERSION = "2.0.0"
|
||||||
|
|
||||||
|
_ASSETS = os.path.join(os.path.dirname(__file__), "assets")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app = QApplication(sys.argv)
|
app = QApplication(sys.argv)
|
||||||
app.setApplicationName(APP_NAME)
|
app.setApplicationName(APP_NAME)
|
||||||
app.setApplicationVersion(APP_VERSION)
|
app.setApplicationVersion(APP_VERSION)
|
||||||
app.setOrganizationName("EKIKA")
|
app.setOrganizationName("EKIKA")
|
||||||
|
|
||||||
|
# App icon (taskbar + window title bar)
|
||||||
|
icon = QIcon()
|
||||||
|
for size, fname in [(16, "app_logo_16.png"), (32, "app_logo_32.png"),
|
||||||
|
(48, "app_logo_48.png"), (256, "app_logo.png")]:
|
||||||
|
path = os.path.join(_ASSETS, fname)
|
||||||
|
if os.path.exists(path):
|
||||||
|
icon.addFile(path)
|
||||||
|
app.setWindowIcon(icon)
|
||||||
|
|
||||||
load_fonts()
|
load_fonts()
|
||||||
apply(app, dark=True)
|
apply(app, dark=True)
|
||||||
window = MainWindow()
|
window = MainWindow()
|
||||||
|
|||||||