Compare commits

..

6 Commits

Author SHA1 Message Date
Krishan d682e0d86e Merge branch 'main' into semantic-release 2026-04-12 18:15:28 +10:00
Krishan e7176d0d69 fix: script to not change cargo.lock 2026-03-11 03:48:39 +00:00
Krishan 491fac772f fix: make sure correct version is passed to dependent jobs 2026-03-11 03:37:55 +00:00
Krishan 8afc39be7a chore: merge archive and release into tauri.yml 2026-03-10 12:28:13 +00:00
Krishan 52291ef1bb chore: add release script and action 2026-03-10 04:16:07 +00:00
Krishan 6f1bd6a681 chore: add sematic-release related deps 2026-03-10 04:15:22 +00:00
42 changed files with 9670 additions and 10913 deletions
-1
View File
@@ -1 +0,0 @@
81e1a25de641f0292863b1404cba728c1eadd00d
-301
View File
@@ -1,301 +0,0 @@
name: Build Lotus Chat Desktop
on:
push:
branches: [main]
workflow_dispatch:
env:
GITEA_URL: https://code.lotusguild.org
REPO: LotusGuild/cinny-desktop
jobs:
prepare:
runs-on: ubuntu-latest
outputs:
version: ${{ steps.ver.outputs.version }}
release_id: ${{ steps.release.outputs.release_id }}
steps:
- name: Compute version
id: ver
run: echo "version=4.12.${{ github.run_number }}" >> $GITHUB_OUTPUT
- name: Create or update release
id: release
env:
TOKEN: ${{ secrets.RELEASE_TOKEN }}
run: |
VERSION="4.12.${{ github.run_number }}"
EXISTING=$(curl -sf "$GITEA_URL/api/v1/repos/$REPO/releases/tags/latest" \
-H "Authorization: token $TOKEN" 2>/dev/null || true)
RELEASE_ID=$(echo "$EXISTING" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('id',''))" 2>/dev/null || true)
if [ -n "$RELEASE_ID" ] && [ "$RELEASE_ID" != "None" ] && [ "$RELEASE_ID" != "" ]; then
curl -sf -X PATCH "$GITEA_URL/api/v1/repos/$REPO/releases/$RELEASE_ID" \
-H "Authorization: token $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"name\":\"Lotus Chat $VERSION\",\"body\":\"Built from ${{ github.sha }}\"}" > /dev/null
else
RELEASE_ID=$(curl -sf -X POST "$GITEA_URL/api/v1/repos/$REPO/releases" \
-H "Authorization: token $TOKEN" \
-H "Content-Type: application/json" \
-d "{\"tag_name\":\"latest\",\"name\":\"Lotus Chat $VERSION\",\"prerelease\":true,\"body\":\"Built from ${{ github.sha }}\"}" \
| python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
fi
echo "release_id=$RELEASE_ID" >> $GITHUB_OUTPUT
build-windows:
needs: prepare
runs-on: windows
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: .node-version
- name: Checkout submodules (shallow)
shell: powershell
run: git submodule update --init --depth=1
- name: Patch version
shell: powershell
run: |
$ver = '${{ needs.prepare.outputs.version }}'
node -e "const fs=require('fs');const d=JSON.parse(fs.readFileSync('src-tauri/tauri.conf.json','utf8'));d.version='$ver';fs.writeFileSync('src-tauri/tauri.conf.json',JSON.stringify(d,null,2),'utf8');"
- uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
- name: Install frontend deps
shell: powershell
run: cd cinny; npm ci
- name: Install Tauri deps
shell: powershell
run: npm ci
- name: Build
shell: powershell
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ''
NODE_OPTIONS: '--max_old_space_size=4096'
# Sparse registry avoids a full git clone of the crates.io index —
# eliminates the curl SSL handshake failures seen on Windows runners.
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
# Disable HTTP/2 multiplexing — ALPN negotiation can reset on Windows Schannel.
CARGO_HTTP_MULTIPLEXING: 'false'
# Retry transient network errors before failing.
CARGO_NET_RETRY: '5'
run: |
# USERPROFILE is set by Windows directly; more reliable than C:\Users\$USERNAME
$env:PATH = "$env:USERPROFILE\.cargo\bin;$env:PATH"
# Also add the actual toolchain bin to bypass rustup shim execution issues
$toolchain = Get-ChildItem "$env:USERPROFILE\.rustup\toolchains" -Directory -ErrorAction SilentlyContinue |
Where-Object { $_.Name -match 'stable' } | Select-Object -First 1
if ($toolchain) { $env:PATH = "$($toolchain.FullName)\bin;$env:PATH" }
Write-Host "cargo: $((Get-Command cargo -ErrorAction SilentlyContinue).Source)"
cargo --version
npm run tauri -- build --bundles nsis
- name: Upload to release
shell: powershell
env:
TOKEN: ${{ secrets.RELEASE_TOKEN }}
RELEASE_ID: ${{ needs.prepare.outputs.release_id }}
VERSION: ${{ needs.prepare.outputs.version }}
run: |
$releaseId = $env:RELEASE_ID
$VERSION = $env:VERSION
Write-Host "Version: $VERSION Release: $releaseId"
$nsis = "src-tauri\target\release\bundle\nsis"
$files = @(
"$nsis\Lotus Chat_${VERSION}_x64-setup.exe",
"$nsis\Lotus Chat_${VERSION}_x64-setup.nsis.zip",
"$nsis\Lotus Chat_${VERSION}_x64-setup.nsis.zip.sig"
)
$names = @("LotusChat-x86_64-setup.exe", "LotusChat-x86_64-setup.nsis.zip", "LotusChat-x86_64-setup.nsis.zip.sig")
for ($i = 0; $i -lt $files.Length; $i++) {
$existing = (Invoke-RestMethod -Uri "$env:GITEA_URL/api/v1/repos/$env:REPO/releases/$releaseId/assets" `
-Headers @{ Authorization = "token $env:TOKEN" }) | Where-Object { $_.name -eq $names[$i] }
if ($existing) {
Invoke-RestMethod -Uri "$env:GITEA_URL/api/v1/repos/$env:REPO/releases/$releaseId/assets/$($existing.id)" `
-Method Delete -Headers @{ Authorization = "token $env:TOKEN" }
}
$bytes = [System.IO.File]::ReadAllBytes($files[$i])
Invoke-RestMethod -Uri "$env:GITEA_URL/api/v1/repos/$env:REPO/releases/$releaseId/assets?name=$($names[$i])" `
-Method Post `
-Headers @{ Authorization = "token $env:TOKEN"; "Content-Type" = "application/octet-stream" } `
-Body $bytes
}
build-linux:
needs: prepare
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
submodules: true
- uses: actions/setup-node@v4
with:
node-version-file: .node-version
- name: Install system deps
run: |
apt-get update
apt-get install -y \
curl wget file gcc imagemagick \
libwebkit2gtk-4.1-dev \
libssl-dev \
libxdo-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
patchelf \
xdg-utils \
squashfs-tools
- name: Ensure icons are RGBA PNG
run: |
for f in src-tauri/icons/*.png; do
info=$(identify -verbose "$f" 2>/dev/null | grep "Type:" | head -1)
echo "$f: $info"
convert "$f" -type TrueColorAlpha PNG32:"$f"
done
- name: Set up Rust toolchain
run: |
source "$HOME/.cargo/env" 2>/dev/null || true
if command -v cargo >/dev/null 2>&1; then
echo "Using existing Rust: $(cargo --version)"
else
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --no-modify-path --default-toolchain stable --profile minimal
source "$HOME/.cargo/env"
fi
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
- name: Patch version
run: python3 -c "import json; d=json.load(open('src-tauri/tauri.conf.json')); d['version']='${{ needs.prepare.outputs.version }}'; open('src-tauri/tauri.conf.json','w').write(json.dumps(d,indent=2))"
- name: Install frontend deps
run: cd cinny && npm ci
- name: Install Tauri deps
run: npm ci
- name: Stage AppRun and linuxdeploy for AppImage bundler
run: |
set -e
mkdir -p ~/.cache/tauri
cp tools/AppRun-x86_64 ~/.cache/tauri/AppRun-x86_64
chmod +x ~/.cache/tauri/AppRun-x86_64
wget -q \
"https://github.com/tauri-apps/binary-releases/releases/download/linuxdeploy/linuxdeploy-x86_64.AppImage" \
-O /tmp/linuxdeploy.AppImage
chmod +x /tmp/linuxdeploy.AppImage
rm -rf /root/linuxdeploy-root
(cd /root && /tmp/linuxdeploy.AppImage --appimage-extract)
mv /root/squashfs-root /root/linuxdeploy-root
echo "Extracted linuxdeploy:"
ls /root/linuxdeploy-root/
ls /root/linuxdeploy-root/usr/bin/ 2>/dev/null || echo "no usr/bin"
# Pre-stage plugin scripts next to linuxdeploy so it finds them via /proc/self/exe lookup
wget -q "https://raw.githubusercontent.com/tauri-apps/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh" \
-O /root/linuxdeploy-root/usr/bin/linuxdeploy-plugin-gtk.sh
wget -q "https://raw.githubusercontent.com/tauri-apps/linuxdeploy-plugin-gstreamer/master/linuxdeploy-plugin-gstreamer.sh" \
-O /root/linuxdeploy-root/usr/bin/linuxdeploy-plugin-gstreamer.sh
chmod +x /root/linuxdeploy-root/usr/bin/linuxdeploy-plugin-gtk.sh \
/root/linuxdeploy-root/usr/bin/linuxdeploy-plugin-gstreamer.sh
gcc -o ~/.cache/tauri/linuxdeploy-x86_64.AppImage tools/ld_wrapper.c
chmod +x ~/.cache/tauri/linuxdeploy-x86_64.AppImage
- name: Build
env:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ''
NODE_OPTIONS: '--max_old_space_size=4096'
RUST_LOG: tauri_bundler=debug
run: npm run tauri -- build --bundles appimage,deb
- name: Show linuxdeploy wrapper log
if: always()
run: cat /tmp/ld-wrapper.log 2>/dev/null || echo "no wrapper log found"
- name: Upload to release
env:
TOKEN: ${{ secrets.RELEASE_TOKEN }}
RELEASE_ID: ${{ needs.prepare.outputs.release_id }}
VERSION: ${{ needs.prepare.outputs.version }}
run: |
APPIMAGE_DIR="src-tauri/target/release/bundle/appimage"
DEB_DIR="src-tauri/target/release/bundle/deb"
upload() {
local name="$1" path="$2"
local existing_id
existing_id=$(curl -sf "$GITEA_URL/api/v1/repos/$REPO/releases/$RELEASE_ID/assets" \
-H "Authorization: token $TOKEN" \
| python3 -c "import sys,json; assets=json.load(sys.stdin); print(next((str(a['id']) for a in assets if a['name']=='$name'), ''))" 2>/dev/null || true)
if [ -n "$existing_id" ]; then
curl -sf -X DELETE "$GITEA_URL/api/v1/repos/$REPO/releases/$RELEASE_ID/assets/$existing_id" \
-H "Authorization: token $TOKEN" || true
fi
echo "Uploading $name"
curl -sf -X POST \
"$GITEA_URL/api/v1/repos/$REPO/releases/$RELEASE_ID/assets?name=$name" \
-H "Authorization: token $TOKEN" \
-H "Content-Type: application/octet-stream" \
--data-binary @"$path"
}
upload "LotusChat-x86_64.AppImage" "$APPIMAGE_DIR/Lotus Chat_${VERSION}_amd64.AppImage"
upload "LotusChat-x86_64.AppImage.tar.gz" "$APPIMAGE_DIR/Lotus Chat_${VERSION}_amd64.AppImage.tar.gz"
upload "LotusChat-x86_64.AppImage.tar.gz.sig" "$APPIMAGE_DIR/Lotus Chat_${VERSION}_amd64.AppImage.tar.gz.sig"
upload "LotusChat-x86_64.deb" "$DEB_DIR/Lotus Chat_${VERSION}_amd64.deb"
update-manifest:
needs: [prepare, build-windows, build-linux]
runs-on: ubuntu-latest
steps:
- name: Generate and upload release.json
env:
TOKEN: ${{ secrets.RELEASE_TOKEN }}
RELEASE_ID: ${{ needs.prepare.outputs.release_id }}
VERSION: ${{ needs.prepare.outputs.version }}
run: |
BASE="$GITEA_URL/LotusGuild/cinny-desktop/releases/download/latest"
WIN_SIG=$(curl -sf "$BASE/LotusChat-x86_64-setup.nsis.zip.sig")
LIN_SIG=$(curl -sf "$BASE/LotusChat-x86_64.AppImage.tar.gz.sig")
DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)
python3 -c "import json,sys; v,d,wu,ws,lu,ls=sys.argv[1:]; print(json.dumps({'version':v,'notes':'Latest Lotus Chat release','pub_date':d,'platforms':{'windows-x86_64':{'url':wu,'signature':ws},'linux-x86_64':{'url':lu,'signature':ls}}},indent=2))" \
"$VERSION" "$DATE" \
"$BASE/LotusChat-x86_64-setup.nsis.zip" "$WIN_SIG" \
"$BASE/LotusChat-x86_64.AppImage.tar.gz" "$LIN_SIG" \
> release.json
cat release.json
OLD=$(curl -sf "$GITEA_URL/api/v1/repos/$REPO/releases/$RELEASE_ID/assets" \
-H "Authorization: token $TOKEN" \
| python3 -c "import sys,json; print(next((str(a['id']) for a in json.load(sys.stdin) if a['name']=='release.json'), ''))" 2>/dev/null || true)
[ -n "$OLD" ] && curl -sf -X DELETE \
"$GITEA_URL/api/v1/repos/$REPO/releases/$RELEASE_ID/assets/$OLD" \
-H "Authorization: token $TOKEN" || true
curl -sf -X POST \
"$GITEA_URL/api/v1/repos/$REPO/releases/$RELEASE_ID/assets?name=release.json" \
-H "Authorization: token $TOKEN" \
-H "Content-Type: application/json" \
--data-binary @release.json
-22
View File
@@ -1,22 +0,0 @@
name: "Upload zip-archive"
on:
release:
types: [published]
jobs:
zip-archive:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
submodules: true
- name: Create zip including submodules
run: |
cd ..
zip ${{ github.event.repository.name }}/${{ github.event.repository.name }}-${{ github.ref_name }}.zip ${{ github.event.repository.name }} -r
- name: Upload zip to release
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
with:
files: |
${{ github.event.repository.name }}-${{ github.ref_name }}.zip
+120 -31
View File
@@ -1,19 +1,82 @@
name: "Publish Tauri App"
on:
release:
types: [published]
workflow_dispatch:
jobs:
semantic-release:
name: Trigger release
outputs:
version: ${{ steps.vars.outputs.tag }}
runs-on: ubuntu-latest
permissions:
contents: write
actions: write
steps:
- name: Checkout repo
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
submodules: true
- name: Setup Node
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version-file: ".node-version"
package-manager-cache: false
- name: Install dependencies
run: npm ci
- name: Run semantic-release
run: npm run semantic-release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Get version from tag
id: vars
run: |
TAG=$(git describe --tags --abbrev=0)
echo "tag=$TAG" >> $GITHUB_OUTPUT
zip-archive:
needs: semantic-release
env:
TAURI_VERSION: ${{ needs.semantic-release.outputs.version }}
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ env.TAURI_VERSION }}
fetch-depth: 0
submodules: true
- name: Create zip including submodules
run: |
cd ..
zip ${{ github.event.repository.name }}/${{ github.event.repository.name }}-${{ env.TAURI_VERSION }}.zip ${{ github.event.repository.name }} -r
- name: Upload zip to release
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # v2.3.3
with:
tag_name: ${{ env.TAURI_VERSION }}
files: |
${{ github.event.repository.name }}-${{ env.TAURI_VERSION }}.zip
# Windows-x86_64
windows-x86_64:
needs: semantic-release
env:
TAURI_VERSION: ${{ needs.semantic-release.outputs.version }}
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ env.TAURI_VERSION }}
fetch-depth: 0
submodules: true
- name: Setup node
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version-file: ".node-version"
package-manager-cache: false
@@ -30,26 +93,25 @@ jobs:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
NODE_OPTIONS: "--max_old_space_size=4096"
- name: Get app version (windows)
run: |
$json = (Get-Content "src-tauri\tauri.conf.json" -Raw) | ConvertFrom-Json
$version = $json.version
echo "Version: ${version}"
echo "TAURI_VERSION=${version}" >> $Env:GITHUB_ENV
echo "${Env:TAURI_VERSION}"
shell: pwsh
- name: Move msi
run: Move-Item "src-tauri\target\release\bundle\msi\Cinny_${{ env.TAURI_VERSION }}_x64_en-US.msi" "src-tauri\target\release\bundle\msi\Cinny_desktop-x86_64.msi"
run: |
$version = $env:TAURI_VERSION.TrimStart('v')
Move-Item "src-tauri\target\release\bundle\msi\Cinny_${version}_x64_en-US.msi" "src-tauri\target\release\bundle\msi\Cinny_desktop-x86_64.msi"
shell: pwsh
- name: Move msi.zip
run: Move-Item "src-tauri\target\release\bundle\msi\Cinny_${{ env.TAURI_VERSION }}_x64_en-US.msi.zip" "src-tauri\target\release\bundle\msi\Cinny_desktop-x86_64.msi.zip"
run: |
$version = $env:TAURI_VERSION.TrimStart('v')
Move-Item "src-tauri\target\release\bundle\msi\Cinny_${version}_x64_en-US.msi.zip" "src-tauri\target\release\bundle\msi\Cinny_desktop-x86_64.msi.zip"
shell: pwsh
- name: Move msi.zip.sig
run: Move-Item "src-tauri\target\release\bundle\msi\Cinny_${{ env.TAURI_VERSION }}_x64_en-US.msi.zip.sig" "src-tauri\target\release\bundle\msi\Cinny_desktop-x86_64.msi.zip.sig"
run: |
$version = $env:TAURI_VERSION.TrimStart('v')
Move-Item "src-tauri\target\release\bundle\msi\Cinny_${version}_x64_en-US.msi.zip.sig" "src-tauri\target\release\bundle\msi\Cinny_desktop-x86_64.msi.zip.sig"
shell: pwsh
- name: Upload tagged release
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # v2.3.3
with:
tag_name: ${{ env.TAURI_VERSION }}
files: |
src-tauri/target/release/bundle/msi/Cinny_desktop-x86_64.msi
src-tauri/target/release/bundle/msi/Cinny_desktop-x86_64.msi.zip
@@ -57,14 +119,19 @@ jobs:
# Linux-x86_64
linux-x86_64:
needs: semantic-release
env:
TAURI_VERSION: ${{ needs.semantic-release.outputs.version }}
runs-on: ubuntu-22.04
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ env.TAURI_VERSION }}
fetch-depth: 0
submodules: true
- name: Setup node
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version-file: ".node-version"
package-manager-cache: false
@@ -85,20 +152,30 @@ jobs:
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
NODE_OPTIONS: "--max_old_space_size=4096"
- name: Get app version
id: vars
run: echo "tag=$(jq .version src-tauri/tauri.conf.json | tr -d '"')" >> $GITHUB_OUTPUT
- name: Move deb
run: mv "src-tauri/target/release/bundle/deb/Cinny_${{ steps.vars.outputs.tag }}_amd64.deb" "src-tauri/target/release/bundle/deb/Cinny_desktop-x86_64.deb"
run: |
VERSION="${{ env.TAURI_VERSION }}"
VERSION="${VERSION#v}"
mv "src-tauri/target/release/bundle/deb/Cinny_${VERSION}_amd64.deb" "src-tauri/target/release/bundle/deb/Cinny_desktop-x86_64.deb"
- name: Move AppImage
run: mv "src-tauri/target/release/bundle/appimage/Cinny_${{ steps.vars.outputs.tag }}_amd64.AppImage" "src-tauri/target/release/bundle/appimage/Cinny_desktop-x86_64.AppImage"
run: |
VERSION="${{ env.TAURI_VERSION }}"
VERSION="${VERSION#v}"
mv "src-tauri/target/release/bundle/appimage/Cinny_${VERSION}_amd64.AppImage" "src-tauri/target/release/bundle/appimage/Cinny_desktop-x86_64.AppImage"
- name: Move AppImage.tar.gz
run: mv "src-tauri/target/release/bundle/appimage/Cinny_${{ steps.vars.outputs.tag }}_amd64.AppImage.tar.gz" "src-tauri/target/release/bundle/appimage/Cinny_desktop-x86_64.AppImage.tar.gz"
run: |
VERSION="${{ env.TAURI_VERSION }}"
VERSION="${VERSION#v}"
mv "src-tauri/target/release/bundle/appimage/Cinny_${VERSION}_amd64.AppImage.tar.gz" "src-tauri/target/release/bundle/appimage/Cinny_desktop-x86_64.AppImage.tar.gz"
- name: Move AppImage.tar.gz.sig
run: mv "src-tauri/target/release/bundle/appimage/Cinny_${{ steps.vars.outputs.tag }}_amd64.AppImage.tar.gz.sig" "src-tauri/target/release/bundle/appimage/Cinny_desktop-x86_64.AppImage.tar.gz.sig"
run: |
VERSION="${{ env.TAURI_VERSION }}"
VERSION="${VERSION#v}"
mv "src-tauri/target/release/bundle/appimage/Cinny_${VERSION}_amd64.AppImage.tar.gz.sig" "src-tauri/target/release/bundle/appimage/Cinny_desktop-x86_64.AppImage.tar.gz.sig"
- name: Upload tagged release
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # v2.3.3
with:
tag_name: ${{ env.TAURI_VERSION }}
files: |
src-tauri/target/release/bundle/deb/Cinny_desktop-x86_64.deb
src-tauri/target/release/bundle/appimage/Cinny_desktop-x86_64.AppImage
@@ -107,14 +184,19 @@ jobs:
# macos-universal
macos-universal:
needs: semantic-release
env:
TAURI_VERSION: ${{ needs.semantic-release.outputs.version }}
runs-on: macos-latest
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ env.TAURI_VERSION }}
fetch-depth: 0
submodules: true
- name: Setup node
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version-file: ".node-version"
package-manager-cache: false
@@ -135,18 +217,19 @@ jobs:
NODE_OPTIONS: "--max_old_space_size=4096"
with:
args: "--target universal-apple-darwin"
- name: Get app version
id: vars
run: echo "tag=$(jq .version src-tauri/tauri.conf.json | tr -d '"')" >> $GITHUB_OUTPUT
- name: Move dmg
run: mv "src-tauri/target/universal-apple-darwin/release/bundle/dmg/Cinny_${{ steps.vars.outputs.tag }}_universal.dmg" "src-tauri/target/universal-apple-darwin/release/bundle/dmg/Cinny_desktop-universal.dmg"
run: |
VERSION="${{ env.TAURI_VERSION }}"
VERSION="${VERSION#v}"
mv "src-tauri/target/universal-apple-darwin/release/bundle/dmg/Cinny_${VERSION}_universal.dmg" "src-tauri/target/universal-apple-darwin/release/bundle/dmg/Cinny_desktop-universal.dmg"
- name: Move app.tar.gz
run: mv "src-tauri/target/universal-apple-darwin/release/bundle/macos/Cinny.app.tar.gz" "src-tauri/target/universal-apple-darwin/release/bundle/macos/Cinny_desktop-universal.app.tar.gz"
- name: Move app.tar.gz.sig
run: mv "src-tauri/target/universal-apple-darwin/release/bundle/macos/Cinny.app.tar.gz.sig" "src-tauri/target/universal-apple-darwin/release/bundle/macos/Cinny_desktop-universal.app.tar.gz.sig"
- name: Upload tagged release
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # v2.3.3
with:
tag_name: ${{ env.TAURI_VERSION }}
files: |
src-tauri/target/universal-apple-darwin/release/bundle/dmg/Cinny_desktop-universal.dmg
src-tauri/target/universal-apple-darwin/release/bundle/macos/Cinny_desktop-universal.app.tar.gz
@@ -155,11 +238,17 @@ jobs:
# Upload release.json
release-update:
if: always()
needs: [windows-x86_64, linux-x86_64, macos-universal]
needs: [windows-x86_64, linux-x86_64, macos-universal, semantic-release]
env:
TAURI_VERSION: ${{ needs.semantic-release.outputs.version }}
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ env.TAURI_VERSION }}
fetch-depth: 0
submodules: true
- name: Install dependencies
run: npm ci
- name: Run release.json
+1 -1
View File
@@ -23,7 +23,7 @@ jobs:
with:
submodules: true
- name: Setup node
uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
with:
node-version-file: ".node-version"
package-manager-cache: false
+1 -2
View File
@@ -1,4 +1,3 @@
[submodule "cinny"]
path = cinny
url = https://code.lotusguild.org/LotusGuild/cinny.git
branch = lotus
url = https://github.com/cinnyapp/cinny.git
+3
View File
@@ -0,0 +1,3 @@
# These are commented until we enable lint and typecheck
# npx tsc -p tsconfig.json --noEmit
# npx lint-staged
+1 -1
Submodule cinny updated: 79f8fabb1b...6a05ff5840
+28 -4
View File
@@ -1,13 +1,37 @@
{
"defaultHomeserver": 0,
"defaultHomeserver": 1,
"homeserverList": [
"matrix.lotusguild.org",
"converser.eu",
"matrix.org",
"mozilla.org"
"mozilla.org",
"unredacted.org",
"xmr.se"
],
"allowCustomHomeservers": true,
"featuredCommunities": {
"openAsDefault": false,
"spaces": [
"#cinny-space:matrix.org",
"#community:matrix.org",
"#space:unredacted.org",
"#science-space:matrix.org",
"#libregaming-games:tchncs.de",
"#mathematics-on:matrix.org"
],
"rooms": [
"#cinny:matrix.org",
"#freesoftware:matrix.org",
"#pcapdroid:matrix.org",
"#gentoo:matrix.org",
"#PrivSec.dev:arcticfoxes.net",
"#disroot:aria-net.org"
],
"servers": [ "matrix.org", "mozilla.org", "unredacted.org" ]
},
"hashRouter": {
"enabled": true,
"basename": "/"
}
}
}
+7405 -376
View File
File diff suppressed because it is too large Load Diff
+53 -4
View File
@@ -1,15 +1,59 @@
{
"name": "cinny",
"version": "4.12.2",
"version": "4.11.2",
"description": "Yet another matrix client",
"main": "index.js",
"engines": {
"node": ">=16.0.0"
},
"scripts": {
"tauri": "shx cp config.json cinny/ && tauri",
"tauri": "cp config.json cinny/ && tauri",
"release": "node scripts/release.mjs",
"bump": "node scripts/update-version.mjs"
"lint": "npm run check:eslint && npm run check:prettier",
"check:eslint": "eslint src/*",
"check:prettier": "prettier --check .",
"fix:prettier": "prettier --write .",
"typecheck": "tsc --noEmit",
"prepare": "husky install",
"commit": "git-cz",
"semantic-release": "semantic-release"
},
"lint-staged": {
"*.{ts,tsx,js,jsx}": "eslint",
"*": "prettier --ignore-unknown --write"
},
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
},
"release": {
"branches": [
"main"
],
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
[
"@semantic-release/exec",
{
"prepareCmd": "node scripts/update-version.mjs ${nextRelease.version}"
}
],
[
"@semantic-release/git",
{
"assets": [
"package.json",
"package-lock.json",
"src-tauri/Cargo.toml",
"src-tauri/tauri.conf.json"
],
"message": "chore(release): ${nextRelease.version} [skip ci]"
}
],
"@semantic-release/github"
]
},
"keywords": [],
"author": "Ajay Bura",
@@ -29,8 +73,13 @@
},
"devDependencies": {
"@actions/github": "6.0.0",
"@semantic-release/exec": "7.1.0",
"@semantic-release/git": "10.0.1",
"@tauri-apps/cli": "2.7.1",
"cz-conventional-changelog": "3.3.0",
"husky": "9.1.7",
"lint-staged": "16.3.2",
"node-fetch": "3.3.2",
"shx": "0.4.0"
"semantic-release": "25.0.3"
}
}
+7 -9
View File
@@ -46,19 +46,17 @@ console.log("Updating cinny web submodule");
execSync("git submodule update --init --recursive", { stdio: "inherit" });
execSync("git fetch --tags", { cwd: "cinny", stdio: "inherit" });
execSync("cd cinny && git fetch --tags", { stdio: "inherit" });
const latestCommit = execSync("git rev-list --tags --max-count=1", {
cwd: "cinny",
}).toString().trim();
const latestTag = execSync(`git describe --tags ${latestCommit}`, {
cwd: "cinny",
}).toString().trim();
const latestTag = execSync(
"cd cinny && git describe --tags $(git rev-list --tags --max-count=1)"
)
.toString()
.trim();
console.log(`Latest cinny tag: ${latestTag}`);
execSync(`git checkout ${latestTag}`, { cwd: "cinny", stdio: "inherit" });
execSync(`cd cinny && git checkout ${latestTag}`, { stdio: "inherit" });
execSync("git add cinny", { stdio: "inherit" });
+1981 -2185
View File
File diff suppressed because it is too large Load Diff
+2 -15
View File
@@ -2,7 +2,7 @@
[package]
name = "cinny"
version = "4.12.2"
version = "4.11.2"
description = "Yet another matrix client"
authors = ["Ajay Bura"]
license = "AGPL-3.0-only"
@@ -17,7 +17,7 @@ tauri-build = { version = "2", features = [] }
[dependencies]
serde_json = "1.0.109"
serde = { version = "1.0.193", features = ["derive"] }
tauri = { version = "2", features = ["devtools", "wry", "tray-icon", "image-png"] }
tauri = { version = "2", features = [ "devtools"] }
tauri-plugin-localhost = "2"
tauri-plugin-window-state = "2"
tauri-plugin-clipboard-manager = "2"
@@ -28,8 +28,6 @@ tauri-plugin-http = "2"
tauri-plugin-process = "2"
tauri-plugin-os = "2"
tauri-plugin-dialog = "2"
tauri-plugin-opener = "2"
tauri-plugin-deep-link = "2"
[features]
# by default Tauri runs in production mode
@@ -42,17 +40,6 @@ custom-protocol = [ "tauri/custom-protocol" ]
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
tauri-plugin-global-shortcut = "2"
tauri-plugin-updater = "2"
tauri-plugin-single-instance = "2"
[target.'cfg(target_os = "windows")'.dependencies]
webview2-com = "0.38"
window-vibrancy = "0.6"
windows = { version = "0.61", features = [
"Win32_Graphics_Gdi",
"Win32_System_Com",
"Win32_UI_Shell",
"Win32_UI_WindowsAndMessaging",
] }
[lib]
name = "app_lib"
-10
View File
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSCameraUsageDescription</key>
<string>Request camera access for WebRTC calls.</string>
<key>NSMicrophoneUsageDescription</key>
<string>Request microphone access for WebRTC calls.</string>
</dict>
</plist>
+1 -2
View File
@@ -10,7 +10,6 @@
],
"permissions": [
"updater:default",
"global-shortcut:default",
"deep-link:default"
"global-shortcut:default"
]
}
+1 -10
View File
@@ -2,11 +2,6 @@
"identifier": "migrated",
"description": "permissions that were migrated from v1",
"local": true,
"remote": {
"urls": [
"http://localhost:44548"
]
},
"windows": [
"main"
],
@@ -82,10 +77,6 @@
"clipboard-manager:allow-write-text",
"core:app:allow-app-show",
"core:app:allow-app-hide",
"clipboard-manager:default",
{
"identifier": "opener:allow-open-url",
"allow": [{ "url": "http://*" }, { "url": "https://*" }]
}
"clipboard-manager:default"
]
}
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
{"desktop-capability":{"identifier":"desktop-capability","description":"","local":true,"windows":["main"],"permissions":["updater:default","global-shortcut:default"],"platforms":["macOS","windows","linux"]},"migrated":{"identifier":"migrated","description":"permissions that were migrated from v1","local":true,"windows":["main"],"permissions":["core:default","fs:allow-read-file","fs:allow-write-file","fs:allow-read-dir","fs:allow-copy-file","fs:allow-mkdir","fs:allow-remove","fs:allow-remove","fs:allow-rename","fs:allow-exists","core:window:allow-create","core:window:allow-center","core:window:allow-request-user-attention","core:window:allow-set-resizable","core:window:allow-set-maximizable","core:window:allow-set-minimizable","core:window:allow-set-closable","core:window:allow-set-title","core:window:allow-maximize","core:window:allow-unmaximize","core:window:allow-minimize","core:window:allow-unminimize","core:window:allow-show","core:window:allow-hide","core:window:allow-close","core:window:allow-set-decorations","core:window:allow-set-always-on-top","core:window:allow-set-content-protected","core:window:allow-set-size","core:window:allow-set-min-size","core:window:allow-set-max-size","core:window:allow-set-position","core:window:allow-set-fullscreen","core:window:allow-set-focus","core:window:allow-set-icon","core:window:allow-set-skip-taskbar","core:window:allow-set-cursor-grab","core:window:allow-set-cursor-visible","core:window:allow-set-cursor-icon","core:window:allow-set-cursor-position","core:window:allow-set-ignore-cursor-events","core:window:allow-start-dragging","core:webview:allow-print","shell:allow-execute","shell:allow-open","dialog:allow-open","dialog:allow-save","dialog:allow-message","dialog:allow-ask","dialog:allow-confirm","http:default","notification:default","global-shortcut:allow-is-registered","global-shortcut:allow-register","global-shortcut:allow-register-all","global-shortcut:allow-unregister","global-shortcut:allow-unregister-all","os:allow-platform","os:allow-version","os:allow-os-type","os:allow-family","os:allow-arch","os:allow-exe-extension","os:allow-locale","os:allow-hostname","process:allow-restart","process:allow-exit","clipboard-manager:allow-read-text","clipboard-manager:allow-write-text","core:app:allow-app-show","core:app:allow-app-hide","clipboard-manager:default",{"identifier":"opener:allow-open-url","allow":[{"url":"http://*"},{"url":"https://*"}]}]}}
{"desktop-capability":{"identifier":"desktop-capability","description":"","local":true,"windows":["main"],"permissions":["updater:default","global-shortcut:default"],"platforms":["macOS","windows","linux"]},"migrated":{"identifier":"migrated","description":"permissions that were migrated from v1","local":true,"windows":["main"],"permissions":["core:default","fs:allow-read-file","fs:allow-write-file","fs:allow-read-dir","fs:allow-copy-file","fs:allow-mkdir","fs:allow-remove","fs:allow-remove","fs:allow-rename","fs:allow-exists","core:window:allow-create","core:window:allow-center","core:window:allow-request-user-attention","core:window:allow-set-resizable","core:window:allow-set-maximizable","core:window:allow-set-minimizable","core:window:allow-set-closable","core:window:allow-set-title","core:window:allow-maximize","core:window:allow-unmaximize","core:window:allow-minimize","core:window:allow-unminimize","core:window:allow-show","core:window:allow-hide","core:window:allow-close","core:window:allow-set-decorations","core:window:allow-set-always-on-top","core:window:allow-set-content-protected","core:window:allow-set-size","core:window:allow-set-min-size","core:window:allow-set-max-size","core:window:allow-set-position","core:window:allow-set-fullscreen","core:window:allow-set-focus","core:window:allow-set-icon","core:window:allow-set-skip-taskbar","core:window:allow-set-cursor-grab","core:window:allow-set-cursor-visible","core:window:allow-set-cursor-icon","core:window:allow-set-cursor-position","core:window:allow-set-ignore-cursor-events","core:window:allow-start-dragging","core:webview:allow-print","shell:allow-execute","shell:allow-open","dialog:allow-open","dialog:allow-save","dialog:allow-message","dialog:allow-ask","dialog:allow-confirm","http:default","notification:default","global-shortcut:allow-is-registered","global-shortcut:allow-register","global-shortcut:allow-register-all","global-shortcut:allow-unregister","global-shortcut:allow-unregister-all","os:allow-platform","os:allow-version","os:allow-os-type","os:allow-family","os:allow-arch","os:allow-exe-extension","os:allow-locale","os:allow-hostname","process:allow-restart","process:allow-exit","clipboard-manager:allow-read-text","clipboard-manager:allow-write-text","core:app:allow-app-show","core:app:allow-app-hide","clipboard-manager:default"]}}
-233
View File
@@ -2060,174 +2060,6 @@
}
}
},
{
"if": {
"properties": {
"identifier": {
"anyOf": [
{
"description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`",
"type": "string",
"const": "opener:default",
"markdownDescription": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`"
},
{
"description": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application.",
"type": "string",
"const": "opener:allow-default-urls",
"markdownDescription": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application."
},
{
"description": "Enables the open_path command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-open-path",
"markdownDescription": "Enables the open_path command without any pre-configured scope."
},
{
"description": "Enables the open_url command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-open-url",
"markdownDescription": "Enables the open_url command without any pre-configured scope."
},
{
"description": "Enables the reveal_item_in_dir command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-reveal-item-in-dir",
"markdownDescription": "Enables the reveal_item_in_dir command without any pre-configured scope."
},
{
"description": "Denies the open_path command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-open-path",
"markdownDescription": "Denies the open_path command without any pre-configured scope."
},
{
"description": "Denies the open_url command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-open-url",
"markdownDescription": "Denies the open_url command without any pre-configured scope."
},
{
"description": "Denies the reveal_item_in_dir command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-reveal-item-in-dir",
"markdownDescription": "Denies the reveal_item_in_dir command without any pre-configured scope."
}
]
}
}
},
"then": {
"properties": {
"allow": {
"items": {
"title": "OpenerScopeEntry",
"description": "Opener scope entry.",
"anyOf": [
{
"type": "object",
"required": [
"url"
],
"properties": {
"app": {
"description": "An application to open this url with, for example: firefox.",
"allOf": [
{
"$ref": "#/definitions/Application"
}
]
},
"url": {
"description": "A URL that can be opened by the webview when using the Opener APIs.\n\nWildcards can be used following the UNIX glob pattern.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
"type": "string"
}
}
},
{
"type": "object",
"required": [
"path"
],
"properties": {
"app": {
"description": "An application to open this path with, for example: xdg-open.",
"allOf": [
{
"$ref": "#/definitions/Application"
}
]
},
"path": {
"description": "A path that can be opened by the webview when using the Opener APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
"type": "string"
}
}
}
]
}
},
"deny": {
"items": {
"title": "OpenerScopeEntry",
"description": "Opener scope entry.",
"anyOf": [
{
"type": "object",
"required": [
"url"
],
"properties": {
"app": {
"description": "An application to open this url with, for example: firefox.",
"allOf": [
{
"$ref": "#/definitions/Application"
}
]
},
"url": {
"description": "A URL that can be opened by the webview when using the Opener APIs.\n\nWildcards can be used following the UNIX glob pattern.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"",
"type": "string"
}
}
},
{
"type": "object",
"required": [
"path"
],
"properties": {
"app": {
"description": "An application to open this path with, for example: xdg-open.",
"allOf": [
{
"$ref": "#/definitions/Application"
}
]
},
"path": {
"description": "A path that can be opened by the webview when using the Opener APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.",
"type": "string"
}
}
}
]
}
}
}
},
"properties": {
"identifier": {
"description": "Identifier of the permission or permission set.",
"allOf": [
{
"$ref": "#/definitions/Identifier"
}
]
}
}
},
{
"if": {
"properties": {
@@ -6590,54 +6422,6 @@
"const": "notification:deny-show",
"markdownDescription": "Denies the show command without any pre-configured scope."
},
{
"description": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`",
"type": "string",
"const": "opener:default",
"markdownDescription": "This permission set allows opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application\nas well as reveal file in directories using default file explorer\n#### This default permission set includes:\n\n- `allow-open-url`\n- `allow-reveal-item-in-dir`\n- `allow-default-urls`"
},
{
"description": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application.",
"type": "string",
"const": "opener:allow-default-urls",
"markdownDescription": "This enables opening `mailto:`, `tel:`, `https://` and `http://` urls using their default application."
},
{
"description": "Enables the open_path command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-open-path",
"markdownDescription": "Enables the open_path command without any pre-configured scope."
},
{
"description": "Enables the open_url command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-open-url",
"markdownDescription": "Enables the open_url command without any pre-configured scope."
},
{
"description": "Enables the reveal_item_in_dir command without any pre-configured scope.",
"type": "string",
"const": "opener:allow-reveal-item-in-dir",
"markdownDescription": "Enables the reveal_item_in_dir command without any pre-configured scope."
},
{
"description": "Denies the open_path command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-open-path",
"markdownDescription": "Denies the open_path command without any pre-configured scope."
},
{
"description": "Denies the open_url command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-open-url",
"markdownDescription": "Denies the open_url command without any pre-configured scope."
},
{
"description": "Denies the reveal_item_in_dir command without any pre-configured scope.",
"type": "string",
"const": "opener:deny-reveal-item-in-dir",
"markdownDescription": "Denies the reveal_item_in_dir command without any pre-configured scope."
},
{
"description": "This permission set configures which\noperating system information are available\nto gather from the frontend.\n\n#### Granted Permissions\n\nAll information except the host name are available.\n\n\n#### This default permission set includes:\n\n- `allow-arch`\n- `allow-exe-extension`\n- `allow-family`\n- `allow-locale`\n- `allow-os-type`\n- `allow-platform`\n- `allow-version`",
"type": "string",
@@ -7028,23 +6812,6 @@
}
]
},
"Application": {
"description": "Opener scope application.",
"anyOf": [
{
"description": "Open in default application.",
"type": "null"
},
{
"description": "If true, allow open with any application.",
"type": "boolean"
},
{
"description": "Allow specific application to open with.",
"type": "string"
}
]
},
"ShellScopeEntryAllowedArg": {
"description": "A command argument allowed to be executed by the webview API.",
"anyOf": [
File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 149 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 142 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 332 KiB

After

Width:  |  Height:  |  Size: 15 KiB

+13 -540
View File
@@ -3,565 +3,38 @@
windows_subsystem = "windows"
)]
use tauri::{
menu::{Menu, MenuItem, PredefinedMenuItem},
tray::{MouseButton, MouseButtonState, TrayIconBuilder, TrayIconEvent},
webview::{NewWindowResponse, PageLoadEvent, WebviewWindowBuilder},
Manager, WebviewUrl,
};
use tauri_plugin_opener::OpenerExt;
// mod menu;
/// Bring the main window to the foreground from the tray / a hidden /
/// minimized state. Shared by the tray, single-instance, and deep-link paths.
fn show_main(app: &tauri::AppHandle) {
if let Some(window) = app.get_webview_window("main") {
let _ = window.show();
let _ = window.unminimize();
let _ = window.set_focus();
}
}
/// Hand a `matrix:` / `matrix.to` URL to the web app by dispatching a DOM
/// CustomEvent the client listens for (see useDeepLinkNavigate.ts). Uses
/// `eval` so we don't need the @tauri-apps/api event package on the web side.
fn forward_deeplink(app: &tauri::AppHandle, url: &str) {
show_main(app);
if let Some(window) = app.get_webview_window("main") {
if let Ok(json) = serde_json::to_string(url) {
let _ = window.eval(&format!(
"window.dispatchEvent(new CustomEvent('lotus-deeplink',{{detail:{json}}}))"
));
}
}
}
/// Pull the first `matrix:` link out of a process's CLI args (Windows/Linux
/// pass deep-link URLs as argv to a freshly launched instance).
fn matrix_url_from_args(args: &[String]) -> Option<String> {
args.iter().find(|a| a.starts_with("matrix:")).cloned()
}
// Injected into every page before app scripts load.
// Patches window.Notification to route through tauri-plugin-notification so
// WebView2's default "denied" state never reaches cinny's permission check.
// Also patches navigator.permissions.query so the React hook sees "granted".
const NOTIFICATION_BRIDGE: &str = r#"(function(){
function TauriNotification(title,options){
var opts=options||{};
try{
window.__TAURI_INTERNALS__.invoke('send_notification',{
title:String(title),
body:opts.body!=null?String(opts.body):undefined
}).catch(function(){});
}catch(_){}
}
TauriNotification.prototype=Object.create(EventTarget.prototype);
TauriNotification.prototype.constructor=TauriNotification;
TauriNotification.prototype.close=function(){};
Object.defineProperty(TauriNotification,'permission',{get:function(){return 'granted';},configurable:true});
TauriNotification.requestPermission=function(){return Promise.resolve('granted');};
TauriNotification.maxActions=0;
Object.defineProperty(window,'Notification',{value:TauriNotification,writable:true,configurable:true});
var _q=navigator.permissions.query.bind(navigator.permissions);
navigator.permissions.query=function(desc){
if(desc&&desc.name==='notifications'){
return Promise.resolve(Object.assign(new EventTarget(),{state:'granted',onchange:null}));
}
return _q(desc);
};
})();"#;
#[tauri::command]
fn send_notification(
app: tauri::AppHandle,
title: String,
body: Option<String>,
) -> Result<(), String> {
use tauri_plugin_notification::NotificationExt;
let mut builder = app.notification().builder().title(&title);
if let Some(b) = &body {
builder = builder.body(b);
}
builder.show().map_err(|e| e.to_string())
}
#[derive(serde::Serialize)]
struct UpdateInfo {
available: bool,
version: Option<String>,
}
#[tauri::command]
async fn check_for_update(app: tauri::AppHandle) -> Result<UpdateInfo, String> {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
{
use tauri_plugin_updater::UpdaterExt;
return match app.updater().map_err(|e| e.to_string())?.check().await {
Ok(Some(update)) => Ok(UpdateInfo { available: true, version: Some(update.version) }),
Ok(None) => Ok(UpdateInfo { available: false, version: None }),
Err(e) => Err(e.to_string()),
};
}
#[cfg(any(target_os = "android", target_os = "ios"))]
Ok(UpdateInfo { available: false, version: None })
}
#[tauri::command]
async fn install_update(app: tauri::AppHandle) -> Result<(), String> {
#[cfg(not(any(target_os = "android", target_os = "ios")))]
{
use tauri_plugin_updater::UpdaterExt;
if let Some(update) = app
.updater()
.map_err(|e| e.to_string())?
.check()
.await
.map_err(|e| e.to_string())?
{
update
.download_and_install(|_chunk, _total| {}, || {})
.await
.map_err(|e| e.to_string())?;
}
}
Ok(())
}
#[tauri::command]
fn set_badge_count(count: u32, window: tauri::Window) -> Result<(), String> {
#[cfg(target_os = "windows")]
{
use windows::{
core::{BOOL, PCWSTR},
Win32::{
Foundation::{COLORREF, HWND, RECT},
Graphics::Gdi::{
CreateBitmap, CreateCompatibleDC, CreateDIBSection, CreateFontW, CreatePen,
CreateSolidBrush, DeleteDC, DeleteObject, DIB_RGB_COLORS, DrawTextW, Ellipse,
ReleaseDC, SelectObject, SetBkMode, SetTextColor, BITMAPINFO,
BITMAPINFOHEADER, BI_RGB, CLIP_DEFAULT_PRECIS, DEFAULT_CHARSET, DEFAULT_PITCH,
DEFAULT_QUALITY, DT_CENTER, DT_SINGLELINE, DT_VCENTER, FF_DONTCARE,
FW_BOLD, OUT_DEFAULT_PRECIS, PS_NULL, TRANSPARENT,
},
UI::{
Shell::{ITaskbarList3, TaskbarList},
WindowsAndMessaging::{CreateIconIndirect, DestroyIcon, HICON, ICONINFO},
},
System::Com::{CoCreateInstance, CLSCTX_INPROC_SERVER},
},
};
let hwnd = HWND(window.hwnd().map_err(|e| e.to_string())?.0 as _);
let hicon: Option<HICON> = if count > 0 {
let label = if count > 99 {
"99+".to_string()
} else {
count.to_string()
};
let mut label_wide: Vec<u16> = label.encode_utf16().chain(std::iter::once(0)).collect();
unsafe {
let size = 20i32;
let hdc_screen = windows::Win32::Graphics::Gdi::GetDC(None);
let hdc = CreateCompatibleDC(Some(hdc_screen));
let bmi = BITMAPINFO {
bmiHeader: BITMAPINFOHEADER {
biSize: std::mem::size_of::<BITMAPINFOHEADER>() as u32,
biWidth: size,
biHeight: -size,
biPlanes: 1,
biBitCount: 32,
biCompression: BI_RGB.0,
..Default::default()
},
..Default::default()
};
let mut bits: *mut std::ffi::c_void = std::ptr::null_mut();
let hbm_color =
CreateDIBSection(Some(hdc), &bmi, DIB_RGB_COLORS, &mut bits, None, 0)
.map_err(|e| e.to_string())?;
// Zero-init so undrawn pixels are fully transparent (CreateDIBSection
// does not guarantee zeroed memory; garbage bytes cause a black square).
if !bits.is_null() {
std::ptr::write_bytes(bits as *mut u8, 0, (size * size * 4) as usize);
}
let old_bm = SelectObject(hdc, hbm_color.into());
let hbrush = CreateSolidBrush(COLORREF(0x003030DD));
let old_brush = SelectObject(hdc, hbrush.into());
let hpen = CreatePen(PS_NULL, 0, COLORREF(0));
let old_pen = SelectObject(hdc, hpen.into());
let _ = Ellipse(hdc, 0, 0, size, size);
let hfont = CreateFontW(
14,
0,
0,
0,
FW_BOLD.0 as i32,
0,
0,
0,
DEFAULT_CHARSET,
OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY,
(DEFAULT_PITCH.0 | FF_DONTCARE.0) as u32,
windows::core::w!("Segoe UI"),
);
let old_font = SelectObject(hdc, hfont.into());
SetTextColor(hdc, COLORREF(0x00FFFFFF));
let _ = SetBkMode(hdc, TRANSPARENT);
let mut rect = RECT { left: 0, top: 0, right: size, bottom: size };
let label_len = label_wide.len() - 1;
let _ = DrawTextW(
hdc,
&mut label_wide[..label_len],
&mut rect,
DT_CENTER | DT_VCENTER | DT_SINGLELINE,
);
SelectObject(hdc, old_brush);
SelectObject(hdc, old_pen);
SelectObject(hdc, old_font);
SelectObject(hdc, old_bm);
let _ = DeleteObject(hbrush.into());
let _ = DeleteObject(hpen.into());
let _ = DeleteObject(hfont.into());
// GDI drawing leaves the alpha channel at 0 for all pixels.
// Set alpha=255 for every painted pixel so Windows uses per-pixel
// alpha compositing instead of falling back to the opaque mask,
// which would render unpainted corner pixels as a black square.
let pixel_count = (size * size) as usize;
let pixels =
std::slice::from_raw_parts_mut(bits as *mut u32, pixel_count);
for pixel in pixels.iter_mut() {
if *pixel != 0 {
*pixel |= 0xFF00_0000u32;
}
}
let hbm_mask = CreateBitmap(size, size, 1, 1, None);
if hbm_mask.0 as usize == 0 {
let _ = DeleteObject(hbm_color.into());
return Err("CreateBitmap failed".to_string());
}
let icon_info = ICONINFO {
fIcon: BOOL(1),
xHotspot: 0,
yHotspot: 0,
hbmMask: hbm_mask,
hbmColor: hbm_color,
};
let hicon = CreateIconIndirect(&icon_info).map_err(|e| {
let _ = DeleteObject(hbm_color.into());
let _ = DeleteObject(hbm_mask.into());
e.to_string()
})?;
let _ = DeleteObject(hbm_color.into());
let _ = DeleteObject(hbm_mask.into());
let _ = DeleteDC(hdc);
let _ = ReleaseDC(None, hdc_screen);
Some(hicon)
}
} else {
None
};
unsafe {
let taskbar: ITaskbarList3 =
CoCreateInstance(&TaskbarList, None, CLSCTX_INPROC_SERVER)
.map_err(|e| e.to_string())?;
taskbar.HrInit().map_err(|e| e.to_string())?;
taskbar
.SetOverlayIcon(hwnd, hicon.unwrap_or_default(), PCWSTR::null())
.map_err(|e| e.to_string())?;
if let Some(icon) = hicon {
let _ = DestroyIcon(icon);
}
}
}
Ok(())
}
/// Held in managed state so the tray's unread overlay can be updated at runtime.
/// Keeping the TrayIcon handle here also keeps the tray alive.
struct TrayUnreadState {
tray: tauri::tray::TrayIcon,
base_rgba: Vec<u8>,
width: u32,
height: u32,
}
/// Paint a small white-ringed red "unread" dot into the bottom-right corner of
/// an RGBA buffer, in place. Cross-platform (operates on raw pixels).
fn draw_unread_dot(rgba: &mut [u8], width: u32, height: u32) {
let w = width as i32;
let h = height as i32;
if w <= 0 || h <= 0 {
return;
}
let r = ((w.min(h) as f32) * 0.30) as i32;
let ring = ((r as f32) * 0.18).max(1.0) as i32;
let cx = w - r - ((w as f32) * 0.06) as i32;
let cy = h - r - ((h as f32) * 0.06) as i32;
for y in (cy - r - ring).max(0)..(cy + r + ring).min(h) {
for x in (cx - r - ring).max(0)..(cx + r + ring).min(w) {
let dx = x - cx;
let dy = y - cy;
let dist2 = dx * dx + dy * dy;
let idx = ((y * w + x) as usize) * 4;
if idx + 3 >= rgba.len() {
continue;
}
if dist2 <= r * r {
rgba[idx] = 0xDD;
rgba[idx + 1] = 0x30;
rgba[idx + 2] = 0x30;
rgba[idx + 3] = 0xFF;
} else if dist2 <= (r + ring) * (r + ring) {
rgba[idx] = 0xFF;
rgba[idx + 1] = 0xFF;
rgba[idx + 2] = 0xFF;
rgba[idx + 3] = 0xFF;
}
}
}
}
/// Overlay (or clear) the unread dot on the tray icon.
#[tauri::command]
fn set_tray_unread(unread: bool, state: tauri::State<'_, TrayUnreadState>) -> Result<(), String> {
let mut rgba = state.base_rgba.clone();
if unread {
draw_unread_dot(&mut rgba, state.width, state.height);
}
let icon = tauri::image::Image::new_owned(rgba, state.width, state.height);
state.tray.set_icon(Some(icon)).map_err(|e| e.to_string())
}
/// Flash the taskbar button to draw attention (e.g. a new mention while the
/// window is unfocused). Clears automatically once the window is focused.
#[tauri::command]
fn flash_window(window: tauri::Window) -> Result<(), String> {
window
.request_user_attention(Some(tauri::UserAttentionType::Informational))
.map_err(|e| e.to_string())
}
use tauri::{webview::WebviewWindowBuilder, WebviewUrl};
pub fn run() {
let port: u16 = 44548;
let context = tauri::generate_context!();
let builder = tauri::Builder::default();
#[allow(unused_mut)]
let mut builder = tauri::Builder::default();
// Single-instance MUST be registered first: a second launch focuses the
// existing window (and forwards any matrix: link) instead of colliding on
// the localhost port. Desktop-only plugin.
#[cfg(desktop)]
{
builder = builder.plugin(tauri_plugin_single_instance::init(|app, argv, _cwd| {
show_main(app);
if let Some(url) = matrix_url_from_args(&argv) {
forward_deeplink(app, &url);
}
}));
}
builder = builder
.invoke_handler(tauri::generate_handler![
set_badge_count,
set_tray_unread,
flash_window,
send_notification,
check_for_update,
install_update,
])
.plugin(tauri_plugin_localhost::Builder::new(port).build())
.plugin(tauri_plugin_window_state::Builder::default().build())
.plugin(tauri_plugin_opener::init())
.plugin(tauri_plugin_notification::init())
.plugin(tauri_plugin_deep_link::init());
#[cfg(not(any(target_os = "android", target_os = "ios")))]
{
builder = builder.plugin(tauri_plugin_updater::Builder::new().build());
}
// #[cfg(target_os = "macos")]
// {
// builder = builder.menu(menu::menu());
// }
builder
.plugin(tauri_plugin_localhost::Builder::new(port).build())
.plugin(tauri_plugin_window_state::Builder::default().build())
.setup(move |app| {
// --- System tray: keeps Lotus Chat running in the background so
// notifications keep arriving after the window is closed-to-tray. ---
let base_icon = app.default_window_icon().cloned();
let open_item = MenuItem::with_id(app, "open", "Open Lotus Chat", true, None::<&str>)?;
let quit_item = MenuItem::with_id(app, "quit", "Quit Lotus Chat", true, None::<&str>)?;
let separator = PredefinedMenuItem::separator(app)?;
let tray_menu = Menu::with_items(app, &[&open_item, &separator, &quit_item])?;
let tray = TrayIconBuilder::with_id("main-tray")
.icon(base_icon.clone().expect("bundled window icon"))
.tooltip("Lotus Chat")
.menu(&tray_menu)
.show_menu_on_left_click(false)
.on_menu_event(|app, event| match event.id.as_ref() {
"open" => show_main(app),
"quit" => app.exit(0),
_ => {}
})
.on_tray_icon_event(|tray, event| {
if let TrayIconEvent::Click {
button: MouseButton::Left,
button_state: MouseButtonState::Up,
..
} = event
{
let app = tray.app_handle();
if let Some(window) = app.get_webview_window("main") {
if window.is_visible().unwrap_or(false) {
let _ = window.hide();
} else {
show_main(app);
}
}
}
})
.build(app)?;
// Keep the tray handle (and base icon pixels) in managed state so
// set_tray_unread can re-render the icon at runtime.
if let Some(img) = base_icon {
let base_rgba = img.rgba().to_vec();
let (width, height) = (img.width(), img.height());
app.manage(TrayUnreadState {
tray,
base_rgba,
width,
height,
});
}
// Dev: use devUrl from tauri.conf.json (http://localhost:8080) to support HMR
#[cfg(debug_assertions)]
let window_url = WebviewUrl::App(Default::default());
// Release: tauri-plugin-localhost serves bundled frontend assets on this port
#[cfg(not(debug_assertions))]
let window_url = {
let url = format!("http://localhost:{}", port).parse().unwrap();
WebviewUrl::External(url)
};
let app_handle = app.handle().clone();
let window = WebviewWindowBuilder::new(app, "main".to_string(), window_url)
.title("Lotus Chat")
// First-run defaults; tauri-plugin-window-state restores geometry
// on later launches.
.inner_size(1100.0, 720.0)
.min_inner_size(480.0, 600.0)
.center()
// Start hidden and reveal once the page has painted, to avoid the
// white launch flash.
.visible(false)
.initialization_script(NOTIFICATION_BRIDGE)
.disable_drag_drop_handler()
.on_page_load(|window, payload| {
if matches!(payload.event(), PageLoadEvent::Finished) {
let _ = window.show();
}
})
.on_new_window(move |url, _features| {
let _ = app_handle.opener().open_url(url.as_str(), None::<&str>);
NewWindowResponse::Deny
})
WebviewWindowBuilder::new(app, "main".to_string(), window_url)
.title("Cinny")
.build()?;
// Close-to-tray: hide instead of exiting; the app is quit explicitly
// from the tray menu.
let window_for_close = window.clone();
window.on_window_event(move |event| {
if let tauri::WindowEvent::CloseRequested { api, .. } = event {
api.prevent_close();
let _ = window_for_close.hide();
}
});
// Failsafe: never leave the window stuck hidden if the page-load
// event doesn't fire for some reason.
let window_for_show = window.clone();
std::thread::spawn(move || {
std::thread::sleep(std::time::Duration::from_secs(8));
let _ = window_for_show.show();
});
// Deep links (matrix:): route both the cold-start case and the
// already-running case (forwarded via single-instance argv) into the
// web client.
{
use tauri_plugin_deep_link::DeepLinkExt;
// Runtime scheme registration is a Linux/Windows-only API; macOS
// registers the scheme from the bundle config at build time.
#[cfg(any(target_os = "linux", target_os = "windows"))]
let _ = app.deep_link().register_all();
let deep_link_handle = app.handle().clone();
app.deep_link().on_open_url(move |event| {
for url in event.urls() {
forward_deeplink(&deep_link_handle, url.as_str());
}
});
if let Some(url) = matrix_url_from_args(&std::env::args().collect::<Vec<_>>()) {
forward_deeplink(&app.handle().clone(), &url);
}
}
// Windows 11 Mica backdrop. The app paints an opaque TDS background,
// so this is subtle (mainly window chrome); harmless if unsupported.
#[cfg(target_os = "windows")]
{
let _ = window_vibrancy::apply_mica(&window, Some(true));
}
// Auto-grant camera, microphone, and notification permissions in WebView2.
#[cfg(target_os = "windows")]
window.with_webview(|webview| {
use webview2_com::{
Microsoft::Web::WebView2::Win32::{
COREWEBVIEW2_PERMISSION_KIND,
COREWEBVIEW2_PERMISSION_KIND_CAMERA,
COREWEBVIEW2_PERMISSION_KIND_MICROPHONE,
COREWEBVIEW2_PERMISSION_KIND_NOTIFICATIONS,
COREWEBVIEW2_PERMISSION_STATE_ALLOW,
},
PermissionRequestedEventHandler,
};
let controller = webview.controller();
if let Ok(core) = unsafe { controller.CoreWebView2() } {
let handler = PermissionRequestedEventHandler::create(Box::new(
|_sender, args| {
if let Some(args) = args {
let mut kind = COREWEBVIEW2_PERMISSION_KIND(0);
unsafe { args.PermissionKind(&mut kind) }?;
if kind == COREWEBVIEW2_PERMISSION_KIND_MICROPHONE
|| kind == COREWEBVIEW2_PERMISSION_KIND_CAMERA
|| kind == COREWEBVIEW2_PERMISSION_KIND_NOTIFICATIONS
{
unsafe {
args.SetState(COREWEBVIEW2_PERMISSION_STATE_ALLOW)
}?;
}
}
Ok(())
},
));
let mut token = Default::default();
let _ = unsafe { core.add_PermissionRequested(&handler, &mut token) };
}
})?;
Ok(())
})
.run(context)
+44
View File
@@ -0,0 +1,44 @@
use tauri::menu::{MenuBuilder, SubmenuBuilder};
use tauri::AppHandle;
pub fn menu() -> tauri::menu::Menu {
let app_menu = SubmenuBuilder::new(app, "Cinny")
.about(Some(Default::default()))
.separator()
.hide()
.hide_others()
.show_all()
.separator()
.quit()
.build()
.unwrap();
let edit_menu = SubmenuBuilder::new(app, "Edit")
.undo()
.redo()
.separator()
.cut()
.copy()
.paste()
.select_all()
.build()
.unwrap();
let view_menu = SubmenuBuilder::new(app, "View")
.fullscreen() // `.fullscreen()` works instead of `.enter_fullscreen()`
.build()
.unwrap();
let window_menu = SubmenuBuilder::new(app, "Window")
.minimize()
.build() // no `.zoom()` method directly available
.unwrap();
MenuBuilder::new(app)
.item(&app_menu)
.item(&edit_menu)
.item(&view_menu)
.item(&window_menu)
.build()
.unwrap()
}
+7 -18
View File
@@ -6,12 +6,6 @@
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": "",
"webviewInstallMode": {
"type": "downloadBootstrapper"
},
"nsis": {
"installMode": "currentUser"
},
"wix": {
"bannerPath": "wix/banner.bmp",
"dialogImagePath": "wix/dialogImage.bmp"
@@ -51,26 +45,21 @@
"beforeDevCommand": "cd cinny && npm start",
"devUrl": "http://localhost:8080"
},
"productName": "Lotus Chat",
"productName": "Cinny",
"mainBinaryName": "cinny",
"version": "4.12.2",
"identifier": "org.lotusguild.lotus-chat",
"version": "4.11.2",
"identifier": "in.cinny.app",
"plugins": {
"updater": {
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDM1N0Y0RThCQTJEQzY1NTkKUldSWlpkeWlpMDUvTlVjejMzN0E1U0FiaVpLK05QVkRXdWlMMm1NNUprMXAvTGZSbU5maVovNmwK",
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDE2NDc3NDBGMTAzNTk1NUYKUldSZmxUVVFEM1JIRnRuMjVRTkFOQ21lUFI5KzRMU0s4OWtBS1RNRUVCNE9LcE9GcExNZ2M2NHoK",
"endpoints": [
"https://code.lotusguild.org/LotusGuild/cinny-desktop/releases/download/latest/release.json"
"https://github.com/cinnyapp/cinny-desktop/releases/download/tauri/release.json"
]
},
"deep-link": {
"desktop": {
"schemes": ["matrix"]
}
}
},
"app": {
"security": {
"csp": "default-src 'self' blob: data: filesystem: ws: wss: http: https: tauri:; script-src 'self' 'unsafe-eval' 'unsafe-inline' blob: data: filesystem: ws: wss: http: https: tauri:; style-src 'self' 'unsafe-inline' blob: data: filesystem: http: https:; img-src 'self' data: blob: filesystem: http: https:; media-src 'self' blob: data: mediastream:; connect-src 'self' blob: ipc: ws: wss: http: https: http://ipc.localhost"
"csp": "default-src 'self' blob: data: filesystem: ws: wss: http: https: tauri:; script-src 'self' 'unsafe-eval' 'unsafe-inline' blob: data: filesystem: ws: wss: http: https: tauri:; img-src 'self' data: blob: filesystem: http: https:; connect-src 'self' blob: ipc: ws: wss: http: https: http://ipc.localhost"
}
}
}
}
Binary file not shown.
-53
View File
@@ -1,53 +0,0 @@
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
int main(int argc, char **argv) {
const char *appdir = "/root/linuxdeploy-root";
char apprun[256], ldbin[256], new_path[4096], new_ldpath[4096];
struct stat st;
snprintf(apprun, sizeof(apprun), "%s/AppRun", appdir);
snprintf(ldbin, sizeof(ldbin), "%s/usr/bin/linuxdeploy", appdir);
/* Strip --appimage-extract-and-run: AppImage runtime flag, not a linuxdeploy flag */
char **new_argv = malloc((argc + 1) * sizeof(char *));
int new_argc = 0;
new_argv[new_argc++] = argv[0];
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--appimage-extract-and-run") != 0)
new_argv[new_argc++] = argv[i];
}
new_argv[new_argc] = NULL;
setenv("APPDIR", appdir, 1);
char *old_path = getenv("PATH");
snprintf(new_path, sizeof(new_path), "%s/usr/bin:%s", appdir, old_path ? old_path : "");
setenv("PATH", new_path, 1);
char *old_ldpath = getenv("LD_LIBRARY_PATH");
snprintf(new_ldpath, sizeof(new_ldpath), "%s/usr/lib:%s/usr/lib/x86_64-linux-gnu:%s",
appdir, appdir, old_ldpath ? old_ldpath : "");
setenv("LD_LIBRARY_PATH", new_ldpath, 1);
/* Write diagnostic log visible in the always() post-step */
FILE *log = fopen("/tmp/ld-wrapper.log", "w");
if (log) {
fprintf(log, "APPDIR=%s\n", appdir);
fprintf(log, "AppRun exists: %s\n", stat(apprun, &st) == 0 ? "yes" : "NO");
fprintf(log, "linuxdeploy exists: %s\n", stat(ldbin, &st) == 0 ? "yes" : "NO");
fprintf(log, "argc=%d new_argc=%d\n", argc, new_argc);
fprintf(log, "args:");
for (int i = 0; i < new_argc; i++) fprintf(log, " [%s]", new_argv[i]);
fprintf(log, "\nPATH=%s\n", getenv("PATH") ? getenv("PATH") : "(null)");
fclose(log);
}
if (stat(ldbin, &st) == 0)
execv(ldbin, new_argv);
execv(apprun, new_argv);
return 1;
}