Compare commits
10 Commits
e345602dc2
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 0fc4dd0e08 | |||
| 7d6abfc7b3 | |||
| cc4f3bb225 | |||
| 22eb0f3e48 | |||
| a5997cb52b | |||
| 81928bbbb2 | |||
| 2caf662c14 | |||
| 36eed5b1e9 | |||
| bcfdd85f54 | |||
| 00f07d76f6 |
@@ -15,6 +15,11 @@ dist-ssr
|
|||||||
# Claude Code
|
# Claude Code
|
||||||
.claude/
|
.claude/
|
||||||
|
|
||||||
|
# Tauri updater signing keys — never commit private keys
|
||||||
|
*.key
|
||||||
|
*.key.pub
|
||||||
|
.tauri/
|
||||||
|
|
||||||
# Editor directories and files
|
# Editor directories and files
|
||||||
.vscode/*
|
.vscode/*
|
||||||
!.vscode/extensions.json
|
!.vscode/extensions.json
|
||||||
|
|||||||
@@ -121,8 +121,8 @@ Zusätzlich im UI-Changelog: [`src/App.jsx`](src/App.jsx) — Konstante `CHANGEL
|
|||||||
# 1. Versionen anheben (package.json, tauri.conf.json, Cargo.toml)
|
# 1. Versionen anheben (package.json, tauri.conf.json, Cargo.toml)
|
||||||
# 2. Changelog in src/App.jsx ergänzen
|
# 2. Changelog in src/App.jsx ergänzen
|
||||||
# 3. Commit + Tag
|
# 3. Commit + Tag
|
||||||
git tag -a v0.6.0 -m "Rapport 0.6"
|
git tag -a v0.7.0 -m "Rapport 0.7"
|
||||||
git push origin main v0.6.0
|
git push origin main v0.7.0
|
||||||
|
|
||||||
# 4. Bundle bauen
|
# 4. Bundle bauen
|
||||||
npx tauri build
|
npx tauri build
|
||||||
|
|||||||
+11
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"version": "0.7.0",
|
||||||
|
"notes": "Rapport 0.7.0",
|
||||||
|
"pub_date": "2026-05-16T00:01:33Z",
|
||||||
|
"platforms": {
|
||||||
|
"darwin-aarch64": {
|
||||||
|
"signature": "dW50cnVzdGVkIGNvbW1lbnQ6IHNpZ25hdHVyZSBmcm9tIHRhdXJpIHNlY3JldCBrZXkKUlVSUjV0czVoWDR4NGxjZjNKU2k0bGhOOUdmbmM2ejdGS3BHTWhuM09iQ3VyaWtEc2twNlA5UjcxN3cxQ3M4MktoQ2o2RHVsSVAvbWdORWZodm5Hc20vZlJPa2g5V0FyTHd3PQp0cnVzdGVkIGNvbW1lbnQ6IHRpbWVzdGFtcDoxNzc4ODg5NjkzCWZpbGU6UkFQUE9SVCBQUkUtUkVMRUFTRS5hcHAudGFyLmd6CmVzUkZmZkVlZ3V6KzhaMVpxYTZaRlFaZVJQZzVoQldNbUpuQlZFdkhWaHVoTCtkVndCaytQMGVOSkFZVHB4UTcvb0FReWJVMW1nd0xXMTlKTkRwL0FRPT0K",
|
||||||
|
"url": "https://git.kgva.ch/karim/RAPPORT/releases/download/0.7.0/RAPPORT%20PRE-RELEASE.app.tar.gz"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+24
-5
@@ -1,13 +1,15 @@
|
|||||||
{
|
{
|
||||||
"name": "rapportv01",
|
"name": "rapport",
|
||||||
"version": "0.0.0",
|
"version": "0.6.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "rapportv01",
|
"name": "rapport",
|
||||||
"version": "0.0.0",
|
"version": "0.6.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@tauri-apps/plugin-process": "^2.3.1",
|
||||||
|
"@tauri-apps/plugin-updater": "^2.10.1",
|
||||||
"react": "^19.2.5",
|
"react": "^19.2.5",
|
||||||
"react-dom": "^19.2.5",
|
"react-dom": "^19.2.5",
|
||||||
"swissqrbill": "^4.3.0"
|
"swissqrbill": "^4.3.0"
|
||||||
@@ -859,7 +861,6 @@
|
|||||||
"version": "2.10.1",
|
"version": "2.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.10.1.tgz",
|
||||||
"integrity": "sha512-hKL/jWf293UDSUN09rR69hrToyIXBb8CjGaWC7gfinvnQrBVvnLr08FeFi38gxtugAVyVcTa5/FD/Xnkb1siBw==",
|
"integrity": "sha512-hKL/jWf293UDSUN09rR69hrToyIXBb8CjGaWC7gfinvnQrBVvnLr08FeFi38gxtugAVyVcTa5/FD/Xnkb1siBw==",
|
||||||
"dev": true,
|
|
||||||
"license": "Apache-2.0 OR MIT",
|
"license": "Apache-2.0 OR MIT",
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
@@ -1098,6 +1099,24 @@
|
|||||||
"node": ">= 10"
|
"node": ">= 10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@tauri-apps/plugin-process": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-process/-/plugin-process-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-nCa4fGVaDL/B9ai03VyPOjfAHRHSBz5v6F/ObsB73r/dA3MHHhZtldaDMIc0V/pnUw9ehzr2iEG+XkSEyC0JJA==",
|
||||||
|
"license": "MIT OR Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@tauri-apps/api": "^2.8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@tauri-apps/plugin-updater": {
|
||||||
|
"version": "2.10.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@tauri-apps/plugin-updater/-/plugin-updater-2.10.1.tgz",
|
||||||
|
"integrity": "sha512-NFYMg+tWOZPJdzE/PpFj2qfqwAWwNS3kXrb1tm1gnBJ9mYzZ4WDRrwy8udzWoAnfGCHLuePNLY1WVCNHnh3eRA==",
|
||||||
|
"license": "MIT OR Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@tauri-apps/api": "^2.10.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@tybys/wasm-util": {
|
"node_modules/@tybys/wasm-util": {
|
||||||
"version": "0.10.1",
|
"version": "0.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
|
||||||
|
|||||||
+3
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "rapport",
|
"name": "rapport",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.6.0",
|
"version": "0.7.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
@@ -10,6 +10,8 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@tauri-apps/plugin-process": "^2.3.1",
|
||||||
|
"@tauri-apps/plugin-updater": "^2.10.1",
|
||||||
"react": "^19.2.5",
|
"react": "^19.2.5",
|
||||||
"react-dom": "^19.2.5",
|
"react-dom": "^19.2.5",
|
||||||
"swissqrbill": "^4.3.0"
|
"swissqrbill": "^4.3.0"
|
||||||
|
|||||||
Executable
+93
@@ -0,0 +1,93 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build + sign Rapport for the updater, and emit latest.json.
|
||||||
|
#
|
||||||
|
# Voraussetzungen:
|
||||||
|
# - Private Key liegt unter ~/.tauri/rapport_updater.key
|
||||||
|
# - Version wurde in src-tauri/tauri.conf.json + package.json hochgezählt
|
||||||
|
#
|
||||||
|
# Ablauf:
|
||||||
|
# 1) npm run tauri build (mit Signing-Env)
|
||||||
|
# 2) liest die erzeugte .sig-Datei
|
||||||
|
# 3) schreibt latest.json im Repo-Root mit URLs auf Gitea-Release-Assets
|
||||||
|
#
|
||||||
|
# Danach manuell:
|
||||||
|
# - auf Gitea einen Release mit Tag v<VERSION> erstellen
|
||||||
|
# - die .app.tar.gz und (optional) die .dmg als Assets hochladen
|
||||||
|
# - latest.json committen + auf main pushen
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
cd "$(dirname "$0")/.."
|
||||||
|
|
||||||
|
KEY_PATH="${TAURI_SIGNING_PRIVATE_KEY_PATH:-$HOME/.tauri/rapport_updater.key}"
|
||||||
|
GITEA_REPO="https://git.kgva.ch/karim/RAPPORT"
|
||||||
|
|
||||||
|
if [ ! -f "$KEY_PATH" ]; then
|
||||||
|
echo "Private Key fehlt: $KEY_PATH" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
VERSION=$(node -p "require('./src-tauri/tauri.conf.json').version")
|
||||||
|
PKG_VERSION=$(node -p "require('./package.json').version")
|
||||||
|
if [ "$VERSION" != "$PKG_VERSION" ]; then
|
||||||
|
echo "Version mismatch: tauri.conf.json=$VERSION package.json=$PKG_VERSION" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ARCH=$(uname -m)
|
||||||
|
case "$ARCH" in
|
||||||
|
arm64|aarch64) PLATFORM_KEY="darwin-aarch64" ;;
|
||||||
|
x86_64) PLATFORM_KEY="darwin-x86_64" ;;
|
||||||
|
*) echo "Unsupported arch: $ARCH" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "→ Build Rapport $VERSION ($PLATFORM_KEY)"
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY="$(cat "$KEY_PATH")" \
|
||||||
|
TAURI_SIGNING_PRIVATE_KEY_PASSWORD="" \
|
||||||
|
npx tauri build
|
||||||
|
|
||||||
|
BUNDLE_DIR="src-tauri/target/release/bundle/macos"
|
||||||
|
TAR_GZ=$(ls "$BUNDLE_DIR"/*.app.tar.gz 2>/dev/null | head -n1 || true)
|
||||||
|
SIG_FILE="${TAR_GZ}.sig"
|
||||||
|
|
||||||
|
if [ -z "$TAR_GZ" ] || [ ! -f "$SIG_FILE" ]; then
|
||||||
|
echo "Bundle oder Signatur fehlt in $BUNDLE_DIR" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ASSET_NAME=$(basename "$TAR_GZ")
|
||||||
|
ASSET_URL_NAME=$(printf '%s' "$ASSET_NAME" | sed 's/ /%20/g')
|
||||||
|
SIGNATURE=$(cat "$SIG_FILE")
|
||||||
|
PUB_DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
||||||
|
DOWNLOAD_URL="$GITEA_REPO/releases/download/$VERSION/$ASSET_URL_NAME"
|
||||||
|
|
||||||
|
NOTES=${RELEASE_NOTES:-"Rapport $VERSION"}
|
||||||
|
|
||||||
|
cat > latest.json <<EOF
|
||||||
|
{
|
||||||
|
"version": "$VERSION",
|
||||||
|
"notes": $(node -e "process.stdout.write(JSON.stringify(process.argv[1]))" "$NOTES"),
|
||||||
|
"pub_date": "$PUB_DATE",
|
||||||
|
"platforms": {
|
||||||
|
"$PLATFORM_KEY": {
|
||||||
|
"signature": $(node -e "process.stdout.write(JSON.stringify(process.argv[1]))" "$SIGNATURE"),
|
||||||
|
"url": "$DOWNLOAD_URL"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "✓ Build fertig"
|
||||||
|
echo " Bundle: $TAR_GZ"
|
||||||
|
echo " Signatur: $SIG_FILE"
|
||||||
|
echo " DMG: $(ls "$BUNDLE_DIR"/*.dmg 2>/dev/null | head -n1 || echo '(keine DMG gefunden)')"
|
||||||
|
echo " Platform: $PLATFORM_KEY"
|
||||||
|
echo " latest.json wurde im Repo-Root geschrieben."
|
||||||
|
echo
|
||||||
|
echo "Nächste Schritte:"
|
||||||
|
echo " 1) Auf Gitea Release mit Tag $VERSION erstellen und folgende Assets hochladen:"
|
||||||
|
echo " - $ASSET_NAME"
|
||||||
|
echo " - (optional) DMG für Erstinstallation"
|
||||||
|
echo " 2) latest.json committen + auf main pushen:"
|
||||||
|
echo " git add latest.json && git commit -m 'Release $VERSION' && git push origin main"
|
||||||
Generated
+420
-6
@@ -75,6 +75,15 @@ version = "1.0.102"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arbitrary"
|
||||||
|
version = "1.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
|
||||||
|
dependencies = [
|
||||||
|
"derive_arbitrary",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayvec"
|
name = "arrayvec"
|
||||||
version = "0.7.6"
|
version = "0.7.6"
|
||||||
@@ -625,6 +634,17 @@ dependencies = [
|
|||||||
"serde_core",
|
"serde_core",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_arbitrary"
|
||||||
|
version = "1.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.117",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "derive_more"
|
name = "derive_more"
|
||||||
version = "0.99.20"
|
version = "0.99.20"
|
||||||
@@ -834,6 +854,16 @@ dependencies = [
|
|||||||
"typeid",
|
"typeid",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.3.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fastrand"
|
name = "fastrand"
|
||||||
version = "2.4.1"
|
version = "2.4.1"
|
||||||
@@ -868,6 +898,16 @@ dependencies = [
|
|||||||
"rustc_version",
|
"rustc_version",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "filetime"
|
||||||
|
version = "0.2.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "find-msvc-tools"
|
name = "find-msvc-tools"
|
||||||
version = "0.1.9"
|
version = "0.1.9"
|
||||||
@@ -1461,6 +1501,21 @@ dependencies = [
|
|||||||
"want",
|
"want",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hyper-rustls"
|
||||||
|
version = "0.27.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f"
|
||||||
|
dependencies = [
|
||||||
|
"http",
|
||||||
|
"hyper",
|
||||||
|
"hyper-util",
|
||||||
|
"rustls",
|
||||||
|
"tokio",
|
||||||
|
"tokio-rustls",
|
||||||
|
"tower-service",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper-util"
|
name = "hyper-util"
|
||||||
version = "0.1.20"
|
version = "0.1.20"
|
||||||
@@ -1726,6 +1781,36 @@ dependencies = [
|
|||||||
"windows-sys 0.45.0",
|
"windows-sys 0.45.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jni"
|
||||||
|
version = "0.22.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"combine",
|
||||||
|
"jni-macros",
|
||||||
|
"jni-sys 0.4.1",
|
||||||
|
"log",
|
||||||
|
"simd_cesu8",
|
||||||
|
"thiserror 2.0.18",
|
||||||
|
"walkdir",
|
||||||
|
"windows-link 0.2.1",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "jni-macros"
|
||||||
|
version = "0.22.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rustc_version",
|
||||||
|
"simd_cesu8",
|
||||||
|
"syn 2.0.117",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jni-sys"
|
name = "jni-sys"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@@ -1866,6 +1951,12 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "litemap"
|
name = "litemap"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
@@ -1959,6 +2050,12 @@ version = "0.3.17"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minisign-verify"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22f9645cb765ea72b8111f36c522475d2daa0d22c957a9826437e97534bc4e9e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.8.9"
|
version = "0.8.9"
|
||||||
@@ -2159,6 +2256,7 @@ checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.11.1",
|
"bitflags 2.11.1",
|
||||||
"block2",
|
"block2",
|
||||||
|
"libc",
|
||||||
"objc2",
|
"objc2",
|
||||||
"objc2-core-foundation",
|
"objc2-core-foundation",
|
||||||
]
|
]
|
||||||
@@ -2174,6 +2272,18 @@ dependencies = [
|
|||||||
"objc2-core-foundation",
|
"objc2-core-foundation",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "objc2-osa-kit"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f112d1746737b0da274ef79a23aac283376f335f4095a083a267a082f21db0c0"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.11.1",
|
||||||
|
"objc2",
|
||||||
|
"objc2-app-kit",
|
||||||
|
"objc2-foundation",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "objc2-quartz-core"
|
name = "objc2-quartz-core"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
@@ -2218,12 +2328,32 @@ version = "1.21.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl-probe"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "option-ext"
|
name = "option-ext"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "osakit"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "732c71caeaa72c065bb69d7ea08717bd3f4863a4f451402fc9513e29dbd5261b"
|
||||||
|
dependencies = [
|
||||||
|
"objc2",
|
||||||
|
"objc2-foundation",
|
||||||
|
"objc2-osa-kit",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror 2.0.18",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pango"
|
name = "pango"
|
||||||
version = "0.18.3"
|
version = "0.18.3"
|
||||||
@@ -2750,7 +2880,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rapport"
|
name = "rapport"
|
||||||
version = "0.6.0"
|
version = "0.7.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -2758,6 +2888,8 @@ dependencies = [
|
|||||||
"tauri",
|
"tauri",
|
||||||
"tauri-build",
|
"tauri-build",
|
||||||
"tauri-plugin-log",
|
"tauri-plugin-log",
|
||||||
|
"tauri-plugin-process",
|
||||||
|
"tauri-plugin-updater",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2858,15 +2990,20 @@ dependencies = [
|
|||||||
"http-body",
|
"http-body",
|
||||||
"http-body-util",
|
"http-body-util",
|
||||||
"hyper",
|
"hyper",
|
||||||
|
"hyper-rustls",
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"log",
|
"log",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
"rustls",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"rustls-platform-verifier",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sync_wrapper",
|
"sync_wrapper",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-rustls",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tower",
|
"tower",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
@@ -2878,6 +3015,20 @@ dependencies = [
|
|||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ring"
|
||||||
|
version = "0.17.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"getrandom 0.2.17",
|
||||||
|
"libc",
|
||||||
|
"untrusted",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rkyv"
|
name = "rkyv"
|
||||||
version = "0.7.46"
|
version = "0.7.46"
|
||||||
@@ -2939,6 +3090,92 @@ dependencies = [
|
|||||||
"semver",
|
"semver",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "1.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.11.1",
|
||||||
|
"errno",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls"
|
||||||
|
version = "0.23.40"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"ring",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"rustls-webpki",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-native-certs"
|
||||||
|
version = "0.8.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63"
|
||||||
|
dependencies = [
|
||||||
|
"openssl-probe",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"schannel",
|
||||||
|
"security-framework",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-pki-types"
|
||||||
|
version = "1.14.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9"
|
||||||
|
dependencies = [
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-platform-verifier"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"jni 0.22.4",
|
||||||
|
"log",
|
||||||
|
"once_cell",
|
||||||
|
"rustls",
|
||||||
|
"rustls-native-certs",
|
||||||
|
"rustls-platform-verifier-android",
|
||||||
|
"rustls-webpki",
|
||||||
|
"security-framework",
|
||||||
|
"security-framework-sys",
|
||||||
|
"webpki-root-certs",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-platform-verifier-android"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-webpki"
|
||||||
|
version = "0.103.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
|
||||||
|
dependencies = [
|
||||||
|
"ring",
|
||||||
|
"rustls-pki-types",
|
||||||
|
"untrusted",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.22"
|
version = "1.0.22"
|
||||||
@@ -2954,6 +3191,15 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "schannel"
|
||||||
|
version = "0.1.29"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "schemars"
|
name = "schemars"
|
||||||
version = "0.8.22"
|
version = "0.8.22"
|
||||||
@@ -3017,6 +3263,29 @@ version = "4.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "security-framework"
|
||||||
|
version = "3.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.11.1",
|
||||||
|
"core-foundation",
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
"security-framework-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "security-framework-sys"
|
||||||
|
version = "2.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3"
|
||||||
|
dependencies = [
|
||||||
|
"core-foundation-sys",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "selectors"
|
name = "selectors"
|
||||||
version = "0.24.0"
|
version = "0.24.0"
|
||||||
@@ -3254,6 +3523,16 @@ version = "0.3.9"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
|
checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "simd_cesu8"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33"
|
||||||
|
dependencies = [
|
||||||
|
"rustc_version",
|
||||||
|
"simdutf8",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "simdutf8"
|
name = "simdutf8"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@@ -3403,6 +3682,12 @@ version = "0.11.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "swift-rs"
|
name = "swift-rs"
|
||||||
version = "1.0.7"
|
version = "1.0.7"
|
||||||
@@ -3486,7 +3771,7 @@ dependencies = [
|
|||||||
"gdkwayland-sys",
|
"gdkwayland-sys",
|
||||||
"gdkx11-sys",
|
"gdkx11-sys",
|
||||||
"gtk",
|
"gtk",
|
||||||
"jni",
|
"jni 0.21.1",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"ndk",
|
"ndk",
|
||||||
@@ -3524,6 +3809,17 @@ version = "1.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tar"
|
||||||
|
version = "0.4.45"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22692a6476a21fa75fdfc11d452fda482af402c008cdbaf3476414e122040973"
|
||||||
|
dependencies = [
|
||||||
|
"filetime",
|
||||||
|
"libc",
|
||||||
|
"xattr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "target-lexicon"
|
name = "target-lexicon"
|
||||||
version = "0.12.16"
|
version = "0.12.16"
|
||||||
@@ -3547,7 +3843,7 @@ dependencies = [
|
|||||||
"gtk",
|
"gtk",
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"http",
|
"http",
|
||||||
"jni",
|
"jni 0.21.1",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"mime",
|
"mime",
|
||||||
@@ -3683,6 +3979,49 @@ dependencies = [
|
|||||||
"time",
|
"time",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tauri-plugin-process"
|
||||||
|
version = "2.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d55511a7bf6cd70c8767b02c97bf8134fa434daf3926cfc1be0a0f94132d165a"
|
||||||
|
dependencies = [
|
||||||
|
"tauri",
|
||||||
|
"tauri-plugin",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tauri-plugin-updater"
|
||||||
|
version = "2.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "806d9dac662c2e4594ff03c647a552f2c9bd544e7d0f683ec58f872f952ce4af"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.22.1",
|
||||||
|
"dirs",
|
||||||
|
"flate2",
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"infer",
|
||||||
|
"log",
|
||||||
|
"minisign-verify",
|
||||||
|
"osakit",
|
||||||
|
"percent-encoding",
|
||||||
|
"reqwest",
|
||||||
|
"rustls",
|
||||||
|
"semver",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"tar",
|
||||||
|
"tauri",
|
||||||
|
"tauri-plugin",
|
||||||
|
"tempfile",
|
||||||
|
"thiserror 2.0.18",
|
||||||
|
"time",
|
||||||
|
"tokio",
|
||||||
|
"url",
|
||||||
|
"windows-sys 0.60.2",
|
||||||
|
"zip",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-runtime"
|
name = "tauri-runtime"
|
||||||
version = "2.10.1"
|
version = "2.10.1"
|
||||||
@@ -3693,7 +4032,7 @@ dependencies = [
|
|||||||
"dpi",
|
"dpi",
|
||||||
"gtk",
|
"gtk",
|
||||||
"http",
|
"http",
|
||||||
"jni",
|
"jni 0.21.1",
|
||||||
"objc2",
|
"objc2",
|
||||||
"objc2-ui-kit",
|
"objc2-ui-kit",
|
||||||
"objc2-web-kit",
|
"objc2-web-kit",
|
||||||
@@ -3716,7 +4055,7 @@ checksum = "e11ea2e6f801d275fdd890d6c9603736012742a1c33b96d0db788c9cdebf7f9e"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"gtk",
|
"gtk",
|
||||||
"http",
|
"http",
|
||||||
"jni",
|
"jni 0.21.1",
|
||||||
"log",
|
"log",
|
||||||
"objc2",
|
"objc2",
|
||||||
"objc2-app-kit",
|
"objc2-app-kit",
|
||||||
@@ -3783,6 +4122,19 @@ dependencies = [
|
|||||||
"toml 1.1.2+spec-1.1.0",
|
"toml 1.1.2+spec-1.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tempfile"
|
||||||
|
version = "3.27.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
|
||||||
|
dependencies = [
|
||||||
|
"fastrand",
|
||||||
|
"getrandom 0.4.2",
|
||||||
|
"once_cell",
|
||||||
|
"rustix",
|
||||||
|
"windows-sys 0.61.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tendril"
|
name = "tendril"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
@@ -3916,6 +4268,16 @@ dependencies = [
|
|||||||
"windows-sys 0.61.2",
|
"windows-sys 0.61.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tokio-rustls"
|
||||||
|
version = "0.26.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
|
||||||
|
dependencies = [
|
||||||
|
"rustls",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-util"
|
name = "tokio-util"
|
||||||
version = "0.7.18"
|
version = "0.7.18"
|
||||||
@@ -4212,6 +4574,12 @@ version = "0.2.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "untrusted"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.5.8"
|
version = "2.5.8"
|
||||||
@@ -4522,6 +4890,15 @@ dependencies = [
|
|||||||
"system-deps",
|
"system-deps",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webpki-root-certs"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c"
|
||||||
|
dependencies = [
|
||||||
|
"rustls-pki-types",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "webview2-com"
|
name = "webview2-com"
|
||||||
version = "0.38.2"
|
version = "0.38.2"
|
||||||
@@ -4752,6 +5129,15 @@ dependencies = [
|
|||||||
"windows-targets 0.42.2",
|
"windows-targets 0.42.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.52.6",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.59.0"
|
version = "0.59.0"
|
||||||
@@ -5135,7 +5521,7 @@ dependencies = [
|
|||||||
"gtk",
|
"gtk",
|
||||||
"http",
|
"http",
|
||||||
"javascriptcore-rs",
|
"javascriptcore-rs",
|
||||||
"jni",
|
"jni 0.21.1",
|
||||||
"libc",
|
"libc",
|
||||||
"ndk",
|
"ndk",
|
||||||
"objc2",
|
"objc2",
|
||||||
@@ -5191,6 +5577,16 @@ dependencies = [
|
|||||||
"pkg-config",
|
"pkg-config",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xattr"
|
||||||
|
version = "1.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"rustix",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yoke"
|
name = "yoke"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
@@ -5255,6 +5651,12 @@ dependencies = [
|
|||||||
"synstructure",
|
"synstructure",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize"
|
||||||
|
version = "1.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerotrie"
|
name = "zerotrie"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
@@ -5288,6 +5690,18 @@ dependencies = [
|
|||||||
"syn 2.0.117",
|
"syn 2.0.117",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zip"
|
||||||
|
version = "4.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1"
|
||||||
|
dependencies = [
|
||||||
|
"arbitrary",
|
||||||
|
"crc32fast",
|
||||||
|
"indexmap 2.14.0",
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zmij"
|
name = "zmij"
|
||||||
version = "1.0.21"
|
version = "1.0.21"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rapport"
|
name = "rapport"
|
||||||
version = "0.6.0"
|
version = "0.7.0"
|
||||||
description = "Rapport — Studio-Management für Architekturbüros"
|
description = "Rapport — Studio-Management für Architekturbüros"
|
||||||
authors = ["Karim Gabriele Varano <karim@gabrielevarano.ch>"]
|
authors = ["Karim Gabriele Varano <karim@gabrielevarano.ch>"]
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
@@ -21,5 +21,7 @@ tauri-build = { version = "2.5.6", features = [] }
|
|||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
tauri = { version = "2.10.3", features = [] }
|
tauri = { version = "2.10.3", features = ["tray-icon"] }
|
||||||
tauri-plugin-log = "2"
|
tauri-plugin-log = "2"
|
||||||
|
tauri-plugin-updater = "2"
|
||||||
|
tauri-plugin-process = "2"
|
||||||
|
|||||||
@@ -7,6 +7,8 @@
|
|||||||
],
|
],
|
||||||
"permissions": [
|
"permissions": [
|
||||||
"core:default",
|
"core:default",
|
||||||
"core:webview:allow-print"
|
"core:webview:allow-print",
|
||||||
|
"updater:default",
|
||||||
|
"process:allow-restart"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+100
-13
@@ -1,16 +1,103 @@
|
|||||||
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use tauri::{
|
||||||
|
menu::{Menu, MenuItem, PredefinedMenuItem},
|
||||||
|
tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent},
|
||||||
|
Emitter, Manager,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn show_main_window(app: &tauri::AppHandle) {
|
||||||
|
if let Some(window) = app.get_webview_window("main") {
|
||||||
|
let _ = window.show();
|
||||||
|
let _ = window.unminimize();
|
||||||
|
let _ = window.set_focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
tauri::Builder::default()
|
let is_quitting = Arc::new(AtomicBool::new(false));
|
||||||
.setup(|app| {
|
|
||||||
if cfg!(debug_assertions) {
|
tauri::Builder::default()
|
||||||
app.handle().plugin(
|
.plugin(tauri_plugin_updater::Builder::new().build())
|
||||||
tauri_plugin_log::Builder::default()
|
.plugin(tauri_plugin_process::init())
|
||||||
.level(log::LevelFilter::Info)
|
.on_window_event({
|
||||||
.build(),
|
let is_quitting = is_quitting.clone();
|
||||||
)?;
|
move |window, event| {
|
||||||
}
|
if let tauri::WindowEvent::CloseRequested { api, .. } = event {
|
||||||
Ok(())
|
if !is_quitting.load(Ordering::SeqCst) {
|
||||||
})
|
api.prevent_close();
|
||||||
.run(tauri::generate_context!())
|
let _ = window.hide();
|
||||||
.expect("error while running tauri application");
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.setup({
|
||||||
|
let is_quitting = is_quitting.clone();
|
||||||
|
move |app| {
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
app.handle().plugin(
|
||||||
|
tauri_plugin_log::Builder::default()
|
||||||
|
.level(log::LevelFilter::Info)
|
||||||
|
.build(),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let show = MenuItem::with_id(app, "show", "Rapport öffnen", true, None::<&str>)?;
|
||||||
|
let dashboard = MenuItem::with_id(app, "nav:dashboard", "Dashboard", true, None::<&str>)?;
|
||||||
|
let time = MenuItem::with_id(app, "nav:time", "Zeiterfassung", true, None::<&str>)?;
|
||||||
|
let projects = MenuItem::with_id(app, "nav:projects", "Projekte", true, None::<&str>)?;
|
||||||
|
let accounting = MenuItem::with_id(app, "nav:buchhaltung", "Buchhaltung", true, None::<&str>)?;
|
||||||
|
let settings = MenuItem::with_id(app, "nav:settings", "Einstellungen", true, None::<&str>)?;
|
||||||
|
let sep1 = PredefinedMenuItem::separator(app)?;
|
||||||
|
let sep2 = PredefinedMenuItem::separator(app)?;
|
||||||
|
let quit = MenuItem::with_id(app, "quit", "Beenden", true, Some("Cmd+Q"))?;
|
||||||
|
|
||||||
|
let menu = Menu::with_items(
|
||||||
|
app,
|
||||||
|
&[&show, &sep1, &dashboard, &time, &projects, &accounting, &settings, &sep2, &quit],
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let is_quitting_menu = is_quitting.clone();
|
||||||
|
let _tray = TrayIconBuilder::with_id("main")
|
||||||
|
.icon(app.default_window_icon().unwrap().clone())
|
||||||
|
.icon_as_template(true)
|
||||||
|
.menu(&menu)
|
||||||
|
.show_menu_on_left_click(false)
|
||||||
|
.on_menu_event(move |app, event| match event.id.as_ref() {
|
||||||
|
"show" => show_main_window(app),
|
||||||
|
"quit" => {
|
||||||
|
is_quitting_menu.store(true, Ordering::SeqCst);
|
||||||
|
app.exit(0);
|
||||||
|
}
|
||||||
|
id if id.starts_with("nav:") => {
|
||||||
|
show_main_window(app);
|
||||||
|
let view = &id["nav:".len()..];
|
||||||
|
let _ = app.emit("rapport:navigate", view.to_string());
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
})
|
||||||
|
.on_tray_icon_event(|tray, event| {
|
||||||
|
if let TrayIconEvent::Click {
|
||||||
|
button: MouseButton::Left,
|
||||||
|
button_state: MouseButtonState::Up,
|
||||||
|
..
|
||||||
|
} = event
|
||||||
|
{
|
||||||
|
show_main_window(tray.app_handle());
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build(app)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.build(tauri::generate_context!())
|
||||||
|
.expect("error while building tauri application")
|
||||||
|
.run(move |_app, event| {
|
||||||
|
if let tauri::RunEvent::ExitRequested { .. } = event {
|
||||||
|
is_quitting.store(true, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
|
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
|
||||||
"productName": "RAPPORT PRE-RELEASE",
|
"productName": "RAPPORT PRE-RELEASE",
|
||||||
"version": "0.6.0",
|
"version": "0.7.0",
|
||||||
"identifier": "com.karimgabrielevarano.rapport",
|
"identifier": "com.karimgabrielevarano.rapport",
|
||||||
"build": {
|
"build": {
|
||||||
"frontendDist": "../dist",
|
"frontendDist": "../dist",
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
"bundle": {
|
"bundle": {
|
||||||
"active": true,
|
"active": true,
|
||||||
"targets": "all",
|
"targets": "all",
|
||||||
|
"createUpdaterArtifacts": true,
|
||||||
"icon": [
|
"icon": [
|
||||||
"icons/32x32.png",
|
"icons/32x32.png",
|
||||||
"icons/128x128.png",
|
"icons/128x128.png",
|
||||||
@@ -36,5 +37,13 @@
|
|||||||
"macOS": {
|
"macOS": {
|
||||||
"signingIdentity": "-"
|
"signingIdentity": "-"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"plugins": {
|
||||||
|
"updater": {
|
||||||
|
"endpoints": [
|
||||||
|
"https://git.kgva.ch/karim/RAPPORT/raw/branch/main/latest.json"
|
||||||
|
],
|
||||||
|
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEUyMzE3RTg1MzlEQkU2NTEKUldSUjV0czVoWDR4NHQwY2RHM3JUV0VCaFRTWjdEci9RYkFZQUJqV0NoRWxDV0prcWFoKzJubFAK"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+34
-8
@@ -4,6 +4,7 @@ import { migrateDashboardLayout, verifyPassword, withHashedPassword, stripCreden
|
|||||||
import Login from "./views/Login.jsx";
|
import Login from "./views/Login.jsx";
|
||||||
import Setup from "./views/Setup.jsx";
|
import Setup from "./views/Setup.jsx";
|
||||||
import MigrationScreen from "./views/MigrationScreen.jsx";
|
import MigrationScreen from "./views/MigrationScreen.jsx";
|
||||||
|
import UpdateNotifier from "./components/UpdateNotifier.jsx";
|
||||||
|
|
||||||
// Code-split: each view loads on demand to keep the initial bundle small.
|
// Code-split: each view loads on demand to keep the initial bundle small.
|
||||||
const Dashboard = lazy(() => import("./views/Dashboard.jsx"));
|
const Dashboard = lazy(() => import("./views/Dashboard.jsx"));
|
||||||
@@ -230,8 +231,8 @@ export default function App() {
|
|||||||
const [modal, setModal] = useState(null);
|
const [modal, setModal] = useState(null);
|
||||||
const [printContent, setPrintContent] = useState(null);
|
const [printContent, setPrintContent] = useState(null);
|
||||||
const [darkMode, setDarkMode] = useState(() => localStorage.getItem("rapport_dark") === "1");
|
const [darkMode, setDarkMode] = useState(() => localStorage.getItem("rapport_dark") === "1");
|
||||||
const [showChangelog, setShowChangelog] = useState(() => localStorage.getItem("rapport_changelog_seen") !== "0.6");
|
const [showChangelog, setShowChangelog] = useState(() => localStorage.getItem("rapport_changelog_seen") !== "0.7");
|
||||||
const [changelogVersion, setChangelogVersion] = useState("0.6");
|
const [changelogVersion, setChangelogVersion] = useState("0.7");
|
||||||
const [showAbout, setShowAbout] = useState(false);
|
const [showAbout, setShowAbout] = useState(false);
|
||||||
const [navOpen, setNavOpen] = useState(false);
|
const [navOpen, setNavOpen] = useState(false);
|
||||||
const [expandedNav, setExpandedNav] = useState(new Set(["buchhaltung"]));
|
const [expandedNav, setExpandedNav] = useState(new Set(["buchhaltung"]));
|
||||||
@@ -276,6 +277,22 @@ export default function App() {
|
|||||||
return () => window.removeEventListener("openProtokoll", handler);
|
return () => window.removeEventListener("openProtokoll", handler);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// Tray-Menü: „Zeiterfassung", „Projekte" usw. springen zur passenden View
|
||||||
|
useEffect(() => {
|
||||||
|
if (!window.__TAURI_INTERNALS__) return;
|
||||||
|
let unlisten = null;
|
||||||
|
import("@tauri-apps/api/event").then(({ listen }) => {
|
||||||
|
listen("rapport:navigate", (event) => {
|
||||||
|
const target = event.payload;
|
||||||
|
if (typeof target === "string") {
|
||||||
|
navigate(target);
|
||||||
|
setSelectedProjectId(null);
|
||||||
|
}
|
||||||
|
}).then((fn) => { unlisten = fn; });
|
||||||
|
});
|
||||||
|
return () => { if (unlisten) unlisten(); };
|
||||||
|
}, []);
|
||||||
|
|
||||||
// Auto-expand parent when navigating to a child
|
// Auto-expand parent when navigating to a child
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
NAV_ITEMS.forEach(item => {
|
NAV_ITEMS.forEach(item => {
|
||||||
@@ -317,7 +334,7 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!currentUser) {
|
if (!currentUser) {
|
||||||
return <Login verifyLogin={verifyLogin} settings={data.settings} version="0.6" />;
|
return <Login verifyLogin={verifyLogin} settings={data.settings} version="0.7" />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (printContent) {
|
if (printContent) {
|
||||||
@@ -590,8 +607,8 @@ export default function App() {
|
|||||||
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
<div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
|
||||||
<button onClick={() => setShowAbout(true)} style={{ background: "none", border: "none", padding: 0, color: "#555", fontSize: 10, letterSpacing: "0.08em", cursor: "pointer", fontFamily: "inherit", textAlign: "left" }}
|
<button onClick={() => setShowAbout(true)} style={{ background: "none", border: "none", padding: 0, color: "#555", fontSize: 10, letterSpacing: "0.08em", cursor: "pointer", fontFamily: "inherit", textAlign: "left" }}
|
||||||
onMouseEnter={e => e.currentTarget.style.color = "#aaa"} onMouseLeave={e => e.currentTarget.style.color = "#555"}>ÜBER RAPPORT</button>
|
onMouseEnter={e => e.currentTarget.style.color = "#aaa"} onMouseLeave={e => e.currentTarget.style.color = "#555"}>ÜBER RAPPORT</button>
|
||||||
<button onClick={() => { setChangelogVersion("0.6"); setShowChangelog(true); }} style={{ background: "none", border: "none", padding: 0, color: "#aaa", fontSize: 10, letterSpacing: "0.08em", cursor: "pointer", fontFamily: "inherit" }}
|
<button onClick={() => { setChangelogVersion("0.7"); setShowChangelog(true); }} style={{ background: "none", border: "none", padding: 0, color: "#aaa", fontSize: 10, letterSpacing: "0.08em", cursor: "pointer", fontFamily: "inherit" }}
|
||||||
onMouseEnter={e => e.currentTarget.style.color = "#f0ede8"} onMouseLeave={e => e.currentTarget.style.color = "#aaa"}>0.6</button>
|
onMouseEnter={e => e.currentTarget.style.color = "#f0ede8"} onMouseLeave={e => e.currentTarget.style.color = "#aaa"}>0.7</button>
|
||||||
</div>
|
</div>
|
||||||
</div>}
|
</div>}
|
||||||
|
|
||||||
@@ -652,8 +669,17 @@ export default function App() {
|
|||||||
</Suspense>
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<UpdateNotifier />
|
||||||
|
|
||||||
{showChangelog && (() => {
|
{showChangelog && (() => {
|
||||||
const CHANGELOGS = {
|
const CHANGELOGS = {
|
||||||
|
"0.7": {
|
||||||
|
items: [
|
||||||
|
["Automatische Updates", "Rapport prüft beim Start, ob eine neue Version unter git.kgva.ch verfügbar ist, und installiert sie auf Knopfdruck — kein manuelles DMG-Download mehr nötig. Updates lassen sich überspringen oder verschieben; Pakete werden vor der Installation per Signaturprüfung verifiziert."],
|
||||||
|
["System-Tray-Icon", "Rapport läuft im Hintergrund weiter, wenn das Fenster geschlossen wird, und ist über ein Menüleisten-Icon erreichbar. Schnellzugriff auf Dashboard, Zeiterfassung, Projekte und Buchhaltung; Cmd+Q beendet die App vollständig."],
|
||||||
|
["Einstellungen: Updates & Support", "Neuer Tab «Updates & Support» mit manueller Update-Suche, Zeitstempel der letzten Prüfung und Link zur Dokumentation auf rapport.kgva.ch."],
|
||||||
|
],
|
||||||
|
},
|
||||||
"0.6": {
|
"0.6": {
|
||||||
items: [
|
items: [
|
||||||
["Sicherheit: Passwort-Hashing", "Passwörter werden jetzt mit PBKDF2 (SHA-256, 100 000 Iterationen) und einem zufälligen Salt gespeichert. Bestehende Klartext-Passwörter werden beim ersten erfolgreichen Login transparent migriert."],
|
["Sicherheit: Passwort-Hashing", "Passwörter werden jetzt mit PBKDF2 (SHA-256, 100 000 Iterationen) und einem zufälligen Salt gespeichert. Bestehende Klartext-Passwörter werden beim ersten erfolgreichen Login transparent migriert."],
|
||||||
@@ -711,7 +737,7 @@ export default function App() {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
const versions = Object.keys(CHANGELOGS);
|
const versions = Object.keys(CHANGELOGS);
|
||||||
const current = CHANGELOGS[changelogVersion] || CHANGELOGS["0.6"];
|
const current = CHANGELOGS[changelogVersion] || CHANGELOGS["0.7"];
|
||||||
return (
|
return (
|
||||||
<div style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,0.55)", zIndex: 200, display: "flex", alignItems: "center", justifyContent: "center", padding: 24 }}>
|
<div style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,0.55)", zIndex: 200, display: "flex", alignItems: "center", justifyContent: "center", padding: 24 }}>
|
||||||
<div style={{ background: "#fff", borderRadius: 10, width: "100%", maxWidth: 480, boxShadow: "0 8px 40px rgba(0,0,0,0.18)", overflow: "hidden" }}>
|
<div style={{ background: "#fff", borderRadius: 10, width: "100%", maxWidth: 480, boxShadow: "0 8px 40px rgba(0,0,0,0.18)", overflow: "hidden" }}>
|
||||||
@@ -740,7 +766,7 @@ export default function App() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div style={{ padding: "12px 32px 24px" }}>
|
<div style={{ padding: "12px 32px 24px" }}>
|
||||||
<button className="btn btn-primary" style={{ width: "100%", fontSize: 13 }} onClick={() => { setShowChangelog(false); localStorage.setItem("rapport_changelog_seen", "0.6"); }}>
|
<button className="btn btn-primary" style={{ width: "100%", fontSize: 13 }} onClick={() => { setShowChangelog(false); localStorage.setItem("rapport_changelog_seen", "0.7"); }}>
|
||||||
Schliessen
|
Schliessen
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -755,7 +781,7 @@ export default function App() {
|
|||||||
<div style={{ background: "#1a1a18", padding: "28px 32px 24px" }}>
|
<div style={{ background: "#1a1a18", padding: "28px 32px 24px" }}>
|
||||||
<div style={{ fontSize: 10, letterSpacing: "0.18em", color: "#b07848", marginBottom: 8, fontWeight: 600 }}>ÜBER RAPPORT</div>
|
<div style={{ fontSize: 10, letterSpacing: "0.18em", color: "#b07848", marginBottom: 8, fontWeight: 600 }}>ÜBER RAPPORT</div>
|
||||||
<div style={{ fontFamily: "'Playfair Display', serif", fontSize: 28, color: "#f0ede8", fontWeight: 400, lineHeight: 1.1 }}>Rapport</div>
|
<div style={{ fontFamily: "'Playfair Display', serif", fontSize: 28, color: "#f0ede8", fontWeight: 400, lineHeight: 1.1 }}>Rapport</div>
|
||||||
<div style={{ fontSize: 11, color: "#888", marginTop: 6, letterSpacing: "0.04em" }}>Alpha 0.6 · Studio-Management für Architekturbüros</div>
|
<div style={{ fontSize: 11, color: "#888", marginTop: 6, letterSpacing: "0.04em" }}>Alpha 0.7 · Studio-Management für Architekturbüros</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ padding: "20px 32px 8px" }}>
|
<div style={{ padding: "20px 32px 8px" }}>
|
||||||
<div style={{ fontSize: 11, fontWeight: 600, color: "#888", letterSpacing: "0.1em", marginBottom: 12 }}>LIZENZ</div>
|
<div style={{ fontSize: 11, fontWeight: 600, color: "#888", letterSpacing: "0.1em", marginBottom: 12 }}>LIZENZ</div>
|
||||||
|
|||||||
@@ -0,0 +1,163 @@
|
|||||||
|
import { useEffect, useState, useCallback } from "react";
|
||||||
|
import { checkForAppUpdate, installAppUpdate, skipUpdateVersion, isTauri } from "../utils/updater.js";
|
||||||
|
|
||||||
|
export default function UpdateNotifier() {
|
||||||
|
const [update, setUpdate] = useState(null);
|
||||||
|
const [state, setState] = useState("idle");
|
||||||
|
const [downloaded, setDownloaded] = useState(0);
|
||||||
|
const [total, setTotal] = useState(0);
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
|
||||||
|
const runCheck = useCallback(async ({ silent }) => {
|
||||||
|
if (!isTauri()) return;
|
||||||
|
try {
|
||||||
|
setState("checking");
|
||||||
|
setError(null);
|
||||||
|
const res = await checkForAppUpdate({ respectSkip: silent });
|
||||||
|
if (!res.available) {
|
||||||
|
setUpdate(null);
|
||||||
|
setState("idle");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setUpdate(res.update);
|
||||||
|
setState("available");
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Update-Check fehlgeschlagen:", e);
|
||||||
|
setError(String(e?.message || e));
|
||||||
|
setState("idle");
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const t = setTimeout(() => runCheck({ silent: true }), 1500);
|
||||||
|
return () => clearTimeout(t);
|
||||||
|
}, [runCheck]);
|
||||||
|
|
||||||
|
// Settings → "Nach Updates suchen" feuert dieses Event; wir lassen das Modal aufpoppen,
|
||||||
|
// falls etwas gefunden wird (Skip-Flag wird dabei ignoriert).
|
||||||
|
useEffect(() => {
|
||||||
|
const handler = () => runCheck({ silent: false });
|
||||||
|
window.addEventListener("rapport:check-update", handler);
|
||||||
|
return () => window.removeEventListener("rapport:check-update", handler);
|
||||||
|
}, [runCheck]);
|
||||||
|
|
||||||
|
const install = async () => {
|
||||||
|
if (!update) return;
|
||||||
|
try {
|
||||||
|
setState("downloading");
|
||||||
|
setDownloaded(0);
|
||||||
|
setTotal(0);
|
||||||
|
await installAppUpdate(update, (event) => {
|
||||||
|
if (event.event === "Started") {
|
||||||
|
setTotal(event.data.contentLength || 0);
|
||||||
|
} else if (event.event === "Progress") {
|
||||||
|
setDownloaded((d) => d + (event.data.chunkLength || 0));
|
||||||
|
} else if (event.event === "Finished") {
|
||||||
|
setState("installing");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Update-Installation fehlgeschlagen:", e);
|
||||||
|
setError(String(e?.message || e));
|
||||||
|
setState("available");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const skipVersion = () => {
|
||||||
|
skipUpdateVersion(update?.version);
|
||||||
|
setUpdate(null);
|
||||||
|
setState("idle");
|
||||||
|
};
|
||||||
|
|
||||||
|
const later = () => {
|
||||||
|
setUpdate(null);
|
||||||
|
setState("idle");
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!isTauri() || !update || state === "idle" || state === "checking") return null;
|
||||||
|
|
||||||
|
const isBusy = state === "downloading" || state === "installing";
|
||||||
|
const pct = total > 0 ? Math.min(100, Math.round((downloaded / total) * 100)) : null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ position: "fixed", inset: 0, background: "rgba(0,0,0,0.55)", zIndex: 300, display: "flex", alignItems: "center", justifyContent: "center", padding: 24, backdropFilter: "blur(4px)" }}>
|
||||||
|
<div style={{ background: "#fff", borderRadius: 10, width: "100%", maxWidth: 460, boxShadow: "0 8px 40px rgba(0,0,0,0.18)", overflow: "hidden" }}>
|
||||||
|
<div style={{ background: "#1a1a18", padding: "28px 32px 20px" }}>
|
||||||
|
<div style={{ fontSize: 10, letterSpacing: "0.18em", color: "#b07848", marginBottom: 8, fontWeight: 600 }}>UPDATE VERFÜGBAR</div>
|
||||||
|
<div style={{ display: "flex", alignItems: "baseline", gap: 12 }}>
|
||||||
|
<div style={{ fontFamily: "'Playfair Display', serif", fontSize: 26, color: "#f0ede8", fontWeight: 400, lineHeight: 1.1 }}>
|
||||||
|
Rapport {update.version}
|
||||||
|
</div>
|
||||||
|
{update.currentVersion && (
|
||||||
|
<div style={{ fontSize: 11, color: "#666", letterSpacing: "0.04em" }}>von {update.currentVersion}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ padding: "20px 32px 8px", maxHeight: 320, overflowY: "auto" }}>
|
||||||
|
{update.body && (
|
||||||
|
<div style={{ display: "flex", gap: 14, marginBottom: 14 }}>
|
||||||
|
<div style={{ width: 4, flexShrink: 0, background: "#b07848", borderRadius: 2, marginTop: 2 }} />
|
||||||
|
<div style={{ fontSize: 12, color: "#555", lineHeight: 1.55, whiteSpace: "pre-wrap" }}>{update.body}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{error && (
|
||||||
|
<div style={{ marginTop: 8, padding: "10px 12px", background: "#fdf0f0", border: "1px solid #f3c4c4", borderRadius: 6, fontSize: 12, color: "#8a1a1a" }}>
|
||||||
|
{error}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isBusy && (
|
||||||
|
<div style={{ marginTop: 12 }}>
|
||||||
|
<div style={{ fontSize: 11, color: "#888", marginBottom: 6, letterSpacing: "0.04em" }}>
|
||||||
|
{state === "downloading"
|
||||||
|
? (pct !== null ? `Wird heruntergeladen … ${pct}%` : "Wird heruntergeladen …")
|
||||||
|
: "Wird installiert …"}
|
||||||
|
</div>
|
||||||
|
<div style={{ height: 4, background: "#eee", borderRadius: 2, overflow: "hidden" }}>
|
||||||
|
<div style={{
|
||||||
|
height: "100%",
|
||||||
|
width: pct !== null ? `${pct}%` : "100%",
|
||||||
|
background: "#b07848",
|
||||||
|
transition: "width 0.2s",
|
||||||
|
animation: pct === null ? "rapport-update-pulse 1.2s ease-in-out infinite" : undefined,
|
||||||
|
}} />
|
||||||
|
</div>
|
||||||
|
<style>{`@keyframes rapport-update-pulse { 0%,100% { opacity: 0.5; } 50% { opacity: 1; } }`}</style>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ padding: "16px 32px 24px", display: "flex", flexDirection: "column", gap: 8 }}>
|
||||||
|
<button
|
||||||
|
className="btn btn-primary"
|
||||||
|
style={{ width: "100%", fontSize: 13 }}
|
||||||
|
onClick={install}
|
||||||
|
disabled={isBusy}
|
||||||
|
>
|
||||||
|
{isBusy ? "Bitte warten …" : "Jetzt installieren"}
|
||||||
|
</button>
|
||||||
|
<div style={{ display: "flex", gap: 8 }}>
|
||||||
|
<button
|
||||||
|
className="btn btn-ghost"
|
||||||
|
style={{ flex: 1, fontSize: 12 }}
|
||||||
|
onClick={later}
|
||||||
|
disabled={isBusy}
|
||||||
|
>
|
||||||
|
Später
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="btn btn-ghost"
|
||||||
|
style={{ flex: 1, fontSize: 12 }}
|
||||||
|
onClick={skipVersion}
|
||||||
|
disabled={isBusy}
|
||||||
|
>
|
||||||
|
Diese Version überspringen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
import { useEffect, useState, useCallback } from "react";
|
||||||
|
import {
|
||||||
|
checkForAppUpdate,
|
||||||
|
installAppUpdate,
|
||||||
|
skipUpdateVersion,
|
||||||
|
getLastUpdateCheck,
|
||||||
|
formatLastCheck,
|
||||||
|
isTauri,
|
||||||
|
} from "../utils/updater.js";
|
||||||
|
|
||||||
|
function Section({ title, children }) {
|
||||||
|
return (
|
||||||
|
<div style={{ marginBottom: 28 }}>
|
||||||
|
<div style={{ fontSize: 10, letterSpacing: "0.12em", color: "#aaa", fontWeight: 600, marginBottom: 14, paddingBottom: 8, borderBottom: "1px solid #ece8e2" }}>{title}</div>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function UpdatesSupport({ fallbackVersion }) {
|
||||||
|
const [version, setVersion] = useState(fallbackVersion || "");
|
||||||
|
const [lastCheck, setLastCheck] = useState(() => getLastUpdateCheck());
|
||||||
|
const [state, setState] = useState("idle");
|
||||||
|
const [update, setUpdate] = useState(null);
|
||||||
|
const [error, setError] = useState(null);
|
||||||
|
const [downloaded, setDownloaded] = useState(0);
|
||||||
|
const [total, setTotal] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isTauri()) return;
|
||||||
|
import("@tauri-apps/api/app").then(({ getVersion }) => {
|
||||||
|
getVersion().then(setVersion).catch(() => {});
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const runCheck = useCallback(async () => {
|
||||||
|
setError(null);
|
||||||
|
if (!isTauri()) {
|
||||||
|
setState("not-tauri");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setState("checking");
|
||||||
|
try {
|
||||||
|
const res = await checkForAppUpdate({ respectSkip: false });
|
||||||
|
setLastCheck(getLastUpdateCheck());
|
||||||
|
if (res.available) {
|
||||||
|
setUpdate(res.update);
|
||||||
|
setState("available");
|
||||||
|
} else {
|
||||||
|
setUpdate(null);
|
||||||
|
setState("no-update");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Update-Check fehlgeschlagen:", e);
|
||||||
|
setError(String(e?.message || e));
|
||||||
|
setState("idle");
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const install = async () => {
|
||||||
|
if (!update) return;
|
||||||
|
setError(null);
|
||||||
|
try {
|
||||||
|
setState("downloading");
|
||||||
|
setDownloaded(0);
|
||||||
|
setTotal(0);
|
||||||
|
await installAppUpdate(update, (event) => {
|
||||||
|
if (event.event === "Started") {
|
||||||
|
setTotal(event.data.contentLength || 0);
|
||||||
|
} else if (event.event === "Progress") {
|
||||||
|
setDownloaded((d) => d + (event.data.chunkLength || 0));
|
||||||
|
} else if (event.event === "Finished") {
|
||||||
|
setState("installing");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Update-Installation fehlgeschlagen:", e);
|
||||||
|
setError(String(e?.message || e));
|
||||||
|
setState("available");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const skipVersion = () => {
|
||||||
|
skipUpdateVersion(update?.version);
|
||||||
|
setUpdate(null);
|
||||||
|
setState("idle");
|
||||||
|
};
|
||||||
|
|
||||||
|
const isBusy = state === "downloading" || state === "installing";
|
||||||
|
const pct = total > 0 ? Math.min(100, Math.round((downloaded / total) * 100)) : null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 20 }} className="responsive-grid-2">
|
||||||
|
<div className="card">
|
||||||
|
<Section title="UPDATES">
|
||||||
|
<div style={{ display: "flex", justifyContent: "space-between", padding: "4px 0", fontSize: 12, borderBottom: "1px solid #f0ede8" }}>
|
||||||
|
<span style={{ color: "#888" }}>Aktuelle Version</span>
|
||||||
|
<strong style={{ color: "#555" }}>{version || "—"}</strong>
|
||||||
|
</div>
|
||||||
|
<div style={{ display: "flex", justifyContent: "space-between", padding: "4px 0", fontSize: 12, borderBottom: "1px solid #f0ede8", marginBottom: 16 }}>
|
||||||
|
<span style={{ color: "#888" }}>Letzte Prüfung</span>
|
||||||
|
<span style={{ color: "#555" }}>{formatLastCheck(lastCheck)}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="btn btn-primary"
|
||||||
|
style={{ width: "100%", marginBottom: 10 }}
|
||||||
|
onClick={runCheck}
|
||||||
|
disabled={state === "checking" || isBusy}
|
||||||
|
>
|
||||||
|
{state === "checking" ? "Wird geprüft …" : "Nach Updates suchen"}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{state === "no-update" && (
|
||||||
|
<div style={{ marginTop: 6, padding: "10px 12px", background: "#f0f7f0", border: "1px solid #c8e0c8", borderRadius: 8, fontSize: 12, color: "#2d6a4f", lineHeight: 1.5 }}>
|
||||||
|
✓ Rapport ist auf dem neuesten Stand.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{state === "available" && update && (
|
||||||
|
<div style={{ marginTop: 10, padding: "14px 14px 12px", background: "#fbf6ef", border: "1px solid #e6d4b3", borderRadius: 8 }}>
|
||||||
|
<div style={{ fontSize: 10, letterSpacing: "0.12em", color: "#b07848", fontWeight: 600, marginBottom: 4 }}>UPDATE VERFÜGBAR</div>
|
||||||
|
<div style={{ fontSize: 14, fontWeight: 600, color: "#1a1a18", marginBottom: 6 }}>Rapport {update.version}</div>
|
||||||
|
{update.body && (
|
||||||
|
<div style={{ fontSize: 12, color: "#666", lineHeight: 1.5, whiteSpace: "pre-wrap", marginBottom: 10, maxHeight: 160, overflowY: "auto" }}>
|
||||||
|
{update.body}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<button className="btn btn-primary" style={{ width: "100%", marginBottom: 8 }} onClick={install} disabled={isBusy}>
|
||||||
|
{isBusy ? "Bitte warten …" : "Installieren und neu starten"}
|
||||||
|
</button>
|
||||||
|
<button className="btn btn-ghost" style={{ width: "100%", fontSize: 12 }} onClick={skipVersion} disabled={isBusy}>
|
||||||
|
Diese Version überspringen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{isBusy && (
|
||||||
|
<div style={{ marginTop: 12 }}>
|
||||||
|
<div style={{ fontSize: 11, color: "#888", marginBottom: 6, letterSpacing: "0.04em" }}>
|
||||||
|
{state === "downloading"
|
||||||
|
? (pct !== null ? `Wird heruntergeladen … ${pct}%` : "Wird heruntergeladen …")
|
||||||
|
: "Wird installiert …"}
|
||||||
|
</div>
|
||||||
|
<div style={{ height: 4, background: "#eee", borderRadius: 2, overflow: "hidden" }}>
|
||||||
|
<div style={{
|
||||||
|
height: "100%",
|
||||||
|
width: pct !== null ? `${pct}%` : "100%",
|
||||||
|
background: "#b07848",
|
||||||
|
transition: "width 0.2s",
|
||||||
|
animation: pct === null ? "rapport-us-pulse 1.2s ease-in-out infinite" : undefined,
|
||||||
|
}} />
|
||||||
|
</div>
|
||||||
|
<style>{`@keyframes rapport-us-pulse { 0%,100% { opacity: 0.5; } 50% { opacity: 1; } }`}</style>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{state === "not-tauri" && (
|
||||||
|
<div style={{ marginTop: 6, padding: "10px 12px", background: "#f5f5f0", border: "1px solid #e0dbd4", borderRadius: 8, fontSize: 12, color: "#666" }}>
|
||||||
|
Updates sind nur in der Desktop-App verfügbar.
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{error && (
|
||||||
|
<div style={{ marginTop: 10, padding: "10px 12px", background: "#fdf0f0", border: "1px solid #f3c4c4", borderRadius: 6, fontSize: 12, color: "#8a1a1a" }}>
|
||||||
|
{error}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<p style={{ fontSize: 11, color: "#888", lineHeight: 1.6, marginTop: 16 }}>
|
||||||
|
Updates werden automatisch beim Start der App geprüft. Hier kannst du manuell suchen — z.B. wenn die App selten geschlossen wird.
|
||||||
|
</p>
|
||||||
|
</Section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="card">
|
||||||
|
<Section title="SUPPORT & DOKUMENTATION">
|
||||||
|
<p style={{ fontSize: 13, color: "#666", lineHeight: 1.7, marginBottom: 16 }}>
|
||||||
|
Anleitungen, Dokumentation und Support findest du auf der offiziellen Website:
|
||||||
|
</p>
|
||||||
|
<a
|
||||||
|
href="https://rapport.kgva.ch/"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className="btn btn-ghost"
|
||||||
|
style={{ width: "100%", textDecoration: "none", marginBottom: 10 }}
|
||||||
|
>
|
||||||
|
rapport.kgva.ch ↗
|
||||||
|
</a>
|
||||||
|
<p style={{ fontSize: 11, color: "#888", lineHeight: 1.6, marginTop: 16 }}>
|
||||||
|
Dort findest du auch das Changelog, Fehlerberichte und Kontaktmöglichkeiten für direkten Support.
|
||||||
|
</p>
|
||||||
|
</Section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
// Shared helpers for the Tauri app updater. Used by the auto-check modal
|
||||||
|
// (UpdateNotifier) and the manual check in Settings → Updates & Support.
|
||||||
|
|
||||||
|
export const SKIP_KEY = "rapport_update_skipped_version";
|
||||||
|
export const LAST_CHECK_KEY = "rapport_update_last_check";
|
||||||
|
|
||||||
|
export function isTauri() {
|
||||||
|
return typeof window !== "undefined" && !!window.__TAURI_INTERNALS__;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function checkForAppUpdate({ respectSkip = true } = {}) {
|
||||||
|
if (!isTauri()) return { available: false, isTauri: false };
|
||||||
|
const { check } = await import("@tauri-apps/plugin-updater");
|
||||||
|
const result = await check();
|
||||||
|
localStorage.setItem(LAST_CHECK_KEY, new Date().toISOString());
|
||||||
|
if (!result?.available) return { available: false, update: null, isTauri: true };
|
||||||
|
if (respectSkip && localStorage.getItem(SKIP_KEY) === result.version) {
|
||||||
|
return { available: false, update: result, isTauri: true, skipped: true };
|
||||||
|
}
|
||||||
|
return { available: true, update: result, isTauri: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function installAppUpdate(update, onProgress) {
|
||||||
|
await update.downloadAndInstall(onProgress);
|
||||||
|
const { relaunch } = await import("@tauri-apps/plugin-process");
|
||||||
|
await relaunch();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function skipUpdateVersion(version) {
|
||||||
|
if (version) localStorage.setItem(SKIP_KEY, version);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getLastUpdateCheck() {
|
||||||
|
return localStorage.getItem(LAST_CHECK_KEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatLastCheck(iso) {
|
||||||
|
if (!iso) return "noch nie";
|
||||||
|
try {
|
||||||
|
const d = new Date(iso);
|
||||||
|
return d.toLocaleString("de-CH", {
|
||||||
|
day: "2-digit", month: "long", year: "numeric",
|
||||||
|
hour: "2-digit", minute: "2-digit",
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
return "—";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import React, { useState } from "react";
|
|||||||
import { STORAGE_KEY, DEFAULT_ABSENZ_TYPES } from "../constants.js";
|
import { STORAGE_KEY, DEFAULT_ABSENZ_TYPES } from "../constants.js";
|
||||||
import { formatIban, isQRIban, applyProjectNumberFormat, applyProtoNumberFormat, generateId, getFeiertageForYear, getAbsenzTypes } from "../utils.js";
|
import { formatIban, isQRIban, applyProjectNumberFormat, applyProtoNumberFormat, generateId, getFeiertageForYear, getAbsenzTypes } from "../utils.js";
|
||||||
import { Header, FormField, Modal, DateInput, useConfirm } from "../components/UI.jsx";
|
import { Header, FormField, Modal, DateInput, useConfirm } from "../components/UI.jsx";
|
||||||
|
import UpdatesSupport from "../components/UpdatesSupport.jsx";
|
||||||
|
|
||||||
const PERMISSION_GROUPS = [
|
const PERMISSION_GROUPS = [
|
||||||
{ label: "Grundmodule", items: [
|
{ label: "Grundmodule", items: [
|
||||||
@@ -37,6 +38,7 @@ const TABS = [
|
|||||||
{ id: "team", label: "Team & Rollen" },
|
{ id: "team", label: "Team & Rollen" },
|
||||||
{ id: "kalender", label: "Feiertage & Absenzen" },
|
{ id: "kalender", label: "Feiertage & Absenzen" },
|
||||||
{ id: "system", label: "System" },
|
{ id: "system", label: "System" },
|
||||||
|
{ id: "support", label: "Updates & Support" },
|
||||||
{ id: "profil", label: "Mein Profil" },
|
{ id: "profil", label: "Mein Profil" },
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -858,6 +860,9 @@ export default function Settings({ data, update, currentUser, uiZoom, setUiZoom
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* ── Tab: Updates & Support ── */}
|
||||||
|
{tab === "support" && <UpdatesSupport />}
|
||||||
</>}
|
</>}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user