UI Update
Some checks failed
Build and Release / Test (push) Has been cancelled
Build and Release / Build Windows (push) Has been cancelled
Build and Release / Build Linux (push) Has been cancelled
Build and Release / Build macOS (push) Has been cancelled
Build and Release / Create Release (push) Has been cancelled
Build and Release / Development Build (push) Has been cancelled
293
.gitea/workflows/build.yml
Normal file
@@ -0,0 +1,293 @@
|
||||
name: Build and Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- 'v*'
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Bun
|
||||
uses: https://github.com/oven-sh/setup-bun@v1
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- name: Install Node dependencies
|
||||
run: bun install
|
||||
|
||||
- name: Run type check
|
||||
run: bun run check
|
||||
|
||||
- name: Install Rust
|
||||
uses: https://github.com/dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Cache Rust dependencies
|
||||
uses: https://github.com/Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: src-tauri
|
||||
|
||||
- name: Check Rust formatting
|
||||
run: cargo fmt --check --manifest-path src-tauri/Cargo.toml
|
||||
|
||||
- name: Run Rust clippy
|
||||
run: cargo clippy --manifest-path src-tauri/Cargo.toml -- -D warnings
|
||||
|
||||
build-windows:
|
||||
name: Build Windows
|
||||
runs-on: windows-latest
|
||||
needs: test
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Bun
|
||||
uses: https://github.com/oven-sh/setup-bun@v1
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
|
||||
- name: Install Rust
|
||||
uses: https://github.com/dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Cache Rust dependencies
|
||||
uses: https://github.com/Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: src-tauri
|
||||
|
||||
- name: Build Tauri app
|
||||
run: bun tauri build
|
||||
env:
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||
|
||||
- name: Upload Windows artifacts
|
||||
uses: https://github.com/actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows-artifacts
|
||||
path: |
|
||||
src-tauri/target/release/bundle/msi/*.msi
|
||||
src-tauri/target/release/bundle/msi/*.msi.zip
|
||||
src-tauri/target/release/bundle/msi/*.msi.zip.sig
|
||||
src-tauri/target/release/bundle/nsis/*.exe
|
||||
|
||||
build-linux:
|
||||
name: Build Linux
|
||||
runs-on: ubuntu-latest
|
||||
needs: test
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Bun
|
||||
uses: https://github.com/oven-sh/setup-bun@v1
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
|
||||
- name: Install Rust
|
||||
uses: https://github.com/dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Cache Rust dependencies
|
||||
uses: https://github.com/Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: src-tauri
|
||||
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libwebkit2gtk-4.1-dev \
|
||||
build-essential \
|
||||
curl \
|
||||
wget \
|
||||
file \
|
||||
libxdo-dev \
|
||||
libssl-dev \
|
||||
libayatana-appindicator3-dev \
|
||||
librsvg2-dev
|
||||
|
||||
- name: Build Tauri app
|
||||
run: bun tauri build
|
||||
env:
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||
|
||||
- name: Upload Linux artifacts
|
||||
uses: https://github.com/actions/upload-artifact@v4
|
||||
with:
|
||||
name: linux-artifacts
|
||||
path: |
|
||||
src-tauri/target/release/bundle/deb/*.deb
|
||||
src-tauri/target/release/bundle/appimage/*.AppImage
|
||||
src-tauri/target/release/bundle/appimage/*.AppImage.tar.gz
|
||||
src-tauri/target/release/bundle/appimage/*.AppImage.tar.gz.sig
|
||||
|
||||
build-macos:
|
||||
name: Build macOS
|
||||
runs-on: macos-latest
|
||||
needs: test
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Bun
|
||||
uses: https://github.com/oven-sh/setup-bun@v1
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
|
||||
- name: Install Rust
|
||||
uses: https://github.com/dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: aarch64-apple-darwin,x86_64-apple-darwin
|
||||
|
||||
- name: Cache Rust dependencies
|
||||
uses: https://github.com/Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: src-tauri
|
||||
|
||||
- name: Build Tauri app (Universal)
|
||||
run: bun tauri build --target universal-apple-darwin
|
||||
env:
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
|
||||
|
||||
- name: Upload macOS artifacts
|
||||
uses: https://github.com/actions/upload-artifact@v4
|
||||
with:
|
||||
name: macos-artifacts
|
||||
path: |
|
||||
src-tauri/target/universal-apple-darwin/release/bundle/dmg/*.dmg
|
||||
src-tauri/target/universal-apple-darwin/release/bundle/macos/*.app.tar.gz
|
||||
src-tauri/target/universal-apple-darwin/release/bundle/macos/*.app.tar.gz.sig
|
||||
|
||||
release:
|
||||
name: Create Release
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-windows, build-linux, build-macos]
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: https://github.com/actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
|
||||
- name: Get version from tag
|
||||
id: get_version
|
||||
run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Generate release notes
|
||||
id: release_notes
|
||||
run: |
|
||||
echo "NOTES<<EOF" >> $GITHUB_OUTPUT
|
||||
git log $(git describe --tags --abbrev=0 HEAD^)..HEAD --pretty=format:"- %s" >> $GITHUB_OUTPUT
|
||||
echo "EOF" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Create latest.json for updater
|
||||
run: |
|
||||
cat > latest.json << EOF
|
||||
{
|
||||
"version": "${{ steps.get_version.outputs.VERSION }}",
|
||||
"notes": "${{ steps.release_notes.outputs.NOTES }}",
|
||||
"pub_date": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
||||
"platforms": {
|
||||
"windows-x86_64": {
|
||||
"signature": "$(cat artifacts/windows-artifacts/*.msi.zip.sig)",
|
||||
"url": "https://git.sirblob.co/${{ github.repository }}/releases/download/v${{ steps.get_version.outputs.VERSION }}/gitea-desktop_${{ steps.get_version.outputs.VERSION }}_x64_en-US.msi.zip"
|
||||
},
|
||||
"linux-x86_64": {
|
||||
"signature": "$(cat artifacts/linux-artifacts/*.AppImage.tar.gz.sig)",
|
||||
"url": "https://git.sirblob.co/${{ github.repository }}/releases/download/v${{ steps.get_version.outputs.VERSION }}/gitea-desktop_${{ steps.get_version.outputs.VERSION }}_amd64.AppImage.tar.gz"
|
||||
},
|
||||
"darwin-universal": {
|
||||
"signature": "$(cat artifacts/macos-artifacts/*.app.tar.gz.sig)",
|
||||
"url": "https://git.sirblob.co/${{ github.repository }}/releases/download/v${{ steps.get_version.outputs.VERSION }}/gitea-desktop_${{ steps.get_version.outputs.VERSION }}_universal.app.tar.gz"
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
- name: Create Release
|
||||
uses: https://github.com/softprops/action-gh-release@v1
|
||||
with:
|
||||
name: Release v${{ steps.get_version.outputs.VERSION }}
|
||||
body: |
|
||||
## What's Changed
|
||||
${{ steps.release_notes.outputs.NOTES }}
|
||||
|
||||
## Downloads
|
||||
- **Windows**: `.msi` installer
|
||||
- **Linux**: `.deb` or `.AppImage`
|
||||
- **macOS**: `.dmg` or `.app`
|
||||
|
||||
## Installation
|
||||
Download the appropriate file for your platform and install it.
|
||||
files: |
|
||||
artifacts/windows-artifacts/*
|
||||
artifacts/linux-artifacts/*
|
||||
artifacts/macos-artifacts/*
|
||||
latest.json
|
||||
draft: false
|
||||
prerelease: false
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
dev-build:
|
||||
name: Development Build
|
||||
runs-on: windows-latest
|
||||
if: github.ref == 'refs/heads/main' && !startsWith(github.ref, 'refs/tags/')
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Bun
|
||||
uses: https://github.com/oven-sh/setup-bun@v1
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install
|
||||
|
||||
- name: Install Rust
|
||||
uses: https://github.com/dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Cache Rust dependencies
|
||||
uses: https://github.com/Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: src-tauri
|
||||
|
||||
- name: Build Tauri app (dev)
|
||||
run: bun tauri build --debug
|
||||
|
||||
- name: Upload dev build
|
||||
uses: https://github.com/actions/upload-artifact@v4
|
||||
with:
|
||||
name: dev-build-windows
|
||||
path: |
|
||||
src-tauri/target/debug/bundle/**/*
|
||||
retention-days: 7
|
||||
2
.gitignore
vendored
@@ -8,3 +8,5 @@ node_modules
|
||||
!.env.example
|
||||
vite.config.js.timestamp-*
|
||||
vite.config.ts.timestamp-*
|
||||
bun.lock
|
||||
cargo.lock
|
||||
7
.vscode/extensions.json
vendored
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"svelte.svelte-vscode",
|
||||
"tauri-apps.tauri-vscode",
|
||||
"rust-lang.rust-analyzer"
|
||||
]
|
||||
}
|
||||
3
.vscode/settings.json
vendored
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"svelte.enable-ts-plugin": true
|
||||
}
|
||||
253
bun.lock
@@ -1,253 +0,0 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "gitea-desktop",
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2",
|
||||
"@tauri-apps/plugin-opener": "^2",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-static": "^3.0.6",
|
||||
"@sveltejs/kit": "^2.9.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
||||
"@tauri-apps/cli": "^2",
|
||||
"svelte": "^5.0.0",
|
||||
"svelte-check": "^4.0.0",
|
||||
"typescript": "~5.6.2",
|
||||
"vite": "^6.0.3",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="],
|
||||
|
||||
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="],
|
||||
|
||||
"@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="],
|
||||
|
||||
"@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="],
|
||||
|
||||
"@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="],
|
||||
|
||||
"@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="],
|
||||
|
||||
"@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="],
|
||||
|
||||
"@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="],
|
||||
|
||||
"@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="],
|
||||
|
||||
"@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="],
|
||||
|
||||
"@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="],
|
||||
|
||||
"@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="],
|
||||
|
||||
"@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="],
|
||||
|
||||
"@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="],
|
||||
|
||||
"@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="],
|
||||
|
||||
"@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="],
|
||||
|
||||
"@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="],
|
||||
|
||||
"@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="],
|
||||
|
||||
"@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="],
|
||||
|
||||
"@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="],
|
||||
|
||||
"@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="],
|
||||
|
||||
"@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="],
|
||||
|
||||
"@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="],
|
||||
|
||||
"@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="],
|
||||
|
||||
"@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="],
|
||||
|
||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="],
|
||||
|
||||
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
|
||||
|
||||
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
|
||||
|
||||
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
||||
|
||||
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
||||
|
||||
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||
|
||||
"@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="],
|
||||
|
||||
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.52.5", "", { "os": "android", "cpu": "arm" }, "sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ=="],
|
||||
|
||||
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.52.5", "", { "os": "android", "cpu": "arm64" }, "sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA=="],
|
||||
|
||||
"@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.52.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA=="],
|
||||
|
||||
"@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.52.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA=="],
|
||||
|
||||
"@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.52.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA=="],
|
||||
|
||||
"@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.52.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ=="],
|
||||
|
||||
"@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.52.5", "", { "os": "linux", "cpu": "arm" }, "sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ=="],
|
||||
|
||||
"@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.52.5", "", { "os": "linux", "cpu": "arm" }, "sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ=="],
|
||||
|
||||
"@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.52.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg=="],
|
||||
|
||||
"@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.52.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q=="],
|
||||
|
||||
"@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA=="],
|
||||
|
||||
"@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.52.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw=="],
|
||||
|
||||
"@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw=="],
|
||||
|
||||
"@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.52.5", "", { "os": "linux", "cpu": "none" }, "sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg=="],
|
||||
|
||||
"@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.52.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ=="],
|
||||
|
||||
"@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.52.5", "", { "os": "linux", "cpu": "x64" }, "sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q=="],
|
||||
|
||||
"@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.52.5", "", { "os": "linux", "cpu": "x64" }, "sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg=="],
|
||||
|
||||
"@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.52.5", "", { "os": "none", "cpu": "arm64" }, "sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw=="],
|
||||
|
||||
"@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.52.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w=="],
|
||||
|
||||
"@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.52.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg=="],
|
||||
|
||||
"@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ=="],
|
||||
|
||||
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg=="],
|
||||
|
||||
"@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="],
|
||||
|
||||
"@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.6", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-4awhxtMh4cx9blePWl10HRHj8Iivtqj+2QdDCSMDzxG+XKa9+VCNupQuCuvzEhYPzZSrX+0gC+0lHA/0fFKKQQ=="],
|
||||
|
||||
"@sveltejs/adapter-static": ["@sveltejs/adapter-static@3.0.10", "", { "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-7D9lYFWJmB7zxZyTE/qxjksvMqzMuYrrsyh1f4AlZqeZeACPRySjbC3aFiY55wb1tWUaKOQG9PVbm74JcN2Iew=="],
|
||||
|
||||
"@sveltejs/kit": ["@sveltejs/kit@2.48.4", "", { "dependencies": { "@standard-schema/spec": "^1.0.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.3.2", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", "sirv": "^3.0.0" }, "peerDependencies": { "@opentelemetry/api": "^1.0.0", "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["@opentelemetry/api"], "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-TGFX1pZUt9qqY20Cv5NyYvy0iLWHf2jXi8s+eCGsig7jQMdwZWKUFMR6TbvFNhfDSUpc1sH/Y5EHv20g3HHA3g=="],
|
||||
|
||||
"@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@5.1.1", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^4.0.1", "debug": "^4.4.1", "deepmerge": "^4.3.1", "kleur": "^4.1.5", "magic-string": "^0.30.17", "vitefu": "^1.0.6" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.0.0" } }, "sha512-Y1Cs7hhTc+a5E9Va/xwKlAJoariQyHY+5zBgCZg4PFWNYQ1nMN9sjK1zhw1gK69DuqVP++sht/1GZg1aRwmAXQ=="],
|
||||
|
||||
"@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@4.0.1", "", { "dependencies": { "debug": "^4.3.7" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^5.0.0", "svelte": "^5.0.0", "vite": "^6.0.0" } }, "sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw=="],
|
||||
|
||||
"@tauri-apps/api": ["@tauri-apps/api@2.9.0", "", {}, "sha512-qD5tMjh7utwBk9/5PrTA/aGr3i5QaJ/Mlt7p8NilQ45WgbifUNPyKWsA63iQ8YfQq6R8ajMapU+/Q8nMcPRLNw=="],
|
||||
|
||||
"@tauri-apps/cli": ["@tauri-apps/cli@2.9.3", "", { "optionalDependencies": { "@tauri-apps/cli-darwin-arm64": "2.9.3", "@tauri-apps/cli-darwin-x64": "2.9.3", "@tauri-apps/cli-linux-arm-gnueabihf": "2.9.3", "@tauri-apps/cli-linux-arm64-gnu": "2.9.3", "@tauri-apps/cli-linux-arm64-musl": "2.9.3", "@tauri-apps/cli-linux-riscv64-gnu": "2.9.3", "@tauri-apps/cli-linux-x64-gnu": "2.9.3", "@tauri-apps/cli-linux-x64-musl": "2.9.3", "@tauri-apps/cli-win32-arm64-msvc": "2.9.3", "@tauri-apps/cli-win32-ia32-msvc": "2.9.3", "@tauri-apps/cli-win32-x64-msvc": "2.9.3" }, "bin": { "tauri": "tauri.js" } }, "sha512-BQ7iLUXTQcyG1PpzLWeVSmBCedYDpnA/6Cm/kRFGtqjTf/eVUlyYO5S2ee07tLum3nWwDBWTGFZeruO8yEukfA=="],
|
||||
|
||||
"@tauri-apps/cli-darwin-arm64": ["@tauri-apps/cli-darwin-arm64@2.9.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-W8FQXZXQmQ0Fmj9UJXNrm2mLdIaLLriKVY7o/FzmizyIKTPIvHjfZALTNybbpTQRbJvKoGHLrW1DNzAWVDWJYg=="],
|
||||
|
||||
"@tauri-apps/cli-darwin-x64": ["@tauri-apps/cli-darwin-x64@2.9.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-zDwu40rlshijt3TU6aRvzPUyVpapsx1sNfOlreDMTaMelQLHl6YoQzSRpLHYwrHrhimxyX2uDqnKIiuGel0Lhg=="],
|
||||
|
||||
"@tauri-apps/cli-linux-arm-gnueabihf": ["@tauri-apps/cli-linux-arm-gnueabihf@2.9.3", "", { "os": "linux", "cpu": "arm" }, "sha512-+Oc2OfcTRwYtW93VJqd/HOk77buORwC9IToj/qsEvM7bTMq6Kda4alpZprzwrCHYANSw+zD8PgjJdljTpe4p+g=="],
|
||||
|
||||
"@tauri-apps/cli-linux-arm64-gnu": ["@tauri-apps/cli-linux-arm64-gnu@2.9.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-59GqU/J1n9wFyAtleoQOaU0oVIo+kwQynEw4meFDoKRXszKGor6lTsbsS3r0QKLSPbc0o/yYGJhqqCtkYjb/eg=="],
|
||||
|
||||
"@tauri-apps/cli-linux-arm64-musl": ["@tauri-apps/cli-linux-arm64-musl@2.9.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-fzvG+jEn5/iYGNH6Z2IRMheYFC4pJdXa19BR9fFm6Bdn2cuajRLDKdUcEME/DCtwqclphXtFZTrT4oezY5vI/A=="],
|
||||
|
||||
"@tauri-apps/cli-linux-riscv64-gnu": ["@tauri-apps/cli-linux-riscv64-gnu@2.9.3", "", { "os": "linux", "cpu": "none" }, "sha512-qV8DZXI/fZwawk6T3Th1g6smiNC2KeQTk7XFgKvqZ6btC01z3UTsQmNGvI602zwm3Ld1TBZb4+rEWu2QmQimmw=="],
|
||||
|
||||
"@tauri-apps/cli-linux-x64-gnu": ["@tauri-apps/cli-linux-x64-gnu@2.9.3", "", { "os": "linux", "cpu": "x64" }, "sha512-tquyEONCNRfqEBWEe4eAHnxFN5yY5lFkCuD4w79XLIovUxVftQ684+xLp7zkhntkt4y20SMj2AgJa/+MOlx4Kg=="],
|
||||
|
||||
"@tauri-apps/cli-linux-x64-musl": ["@tauri-apps/cli-linux-x64-musl@2.9.3", "", { "os": "linux", "cpu": "x64" }, "sha512-v2cBIB/6ji8DL+aiL5QUykU3ZO8OoJGyx50/qv2HQVzkf85KdaYSis3D/oVRemN/pcDz+vyCnnL3XnzFnDl4JQ=="],
|
||||
|
||||
"@tauri-apps/cli-win32-arm64-msvc": ["@tauri-apps/cli-win32-arm64-msvc@2.9.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-ZGvBy7nvrHPbE0HeKp/ioaiw8bNgAHxWnb7JRZ4/G0A+oFj0SeSFxl9k5uU6FKnM7bHM23Gd1oeaDex9g5Fceg=="],
|
||||
|
||||
"@tauri-apps/cli-win32-ia32-msvc": ["@tauri-apps/cli-win32-ia32-msvc@2.9.3", "", { "os": "win32", "cpu": "ia32" }, "sha512-UsgIwOnpCoY9NK9/65QiwgmWVIE80LE7SwRYVblGtmlY9RYfsYvpbItwsovA/AcHMTiO+OCvS/q9yLeqS3m6Sg=="],
|
||||
|
||||
"@tauri-apps/cli-win32-x64-msvc": ["@tauri-apps/cli-win32-x64-msvc@2.9.3", "", { "os": "win32", "cpu": "x64" }, "sha512-fmw7NrrHE5m49idCvJAx9T9bsupjdJ0a3p3DPCNCZRGANU6R1tA1L+KTlVuUtdAldX2NqU/9UPo2SCslYKgJHQ=="],
|
||||
|
||||
"@tauri-apps/plugin-opener": ["@tauri-apps/plugin-opener@2.5.2", "", { "dependencies": { "@tauri-apps/api": "^2.8.0" } }, "sha512-ei/yRRoCklWHImwpCcDK3VhNXx+QXM9793aQ64YxpqVF0BDuuIlXhZgiAkc15wnPVav+IbkYhmDJIv5R326Mew=="],
|
||||
|
||||
"@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="],
|
||||
|
||||
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||
|
||||
"acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
||||
|
||||
"aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="],
|
||||
|
||||
"axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="],
|
||||
|
||||
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
|
||||
|
||||
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
|
||||
|
||||
"cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="],
|
||||
|
||||
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||
|
||||
"deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="],
|
||||
|
||||
"devalue": ["devalue@5.4.2", "", {}, "sha512-MwPZTKEPK2k8Qgfmqrd48ZKVvzSQjgW0lXLxiIBA8dQjtf/6mw6pggHNLcyDKyf+fI6eXxlQwPsfaCMTU5U+Bw=="],
|
||||
|
||||
"esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="],
|
||||
|
||||
"esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="],
|
||||
|
||||
"esrap": ["esrap@2.1.2", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-DgvlIQeowRNyvLPWW4PT7Gu13WznY288Du086E751mwwbsgr29ytBiYeLzAGIo0qk3Ujob0SDk8TiSaM5WQzNg=="],
|
||||
|
||||
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||
|
||||
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||
|
||||
"is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="],
|
||||
|
||||
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
|
||||
|
||||
"locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="],
|
||||
|
||||
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
||||
|
||||
"mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="],
|
||||
|
||||
"mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="],
|
||||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||
|
||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="],
|
||||
|
||||
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
||||
|
||||
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
||||
|
||||
"rollup": ["rollup@4.52.5", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.52.5", "@rollup/rollup-android-arm64": "4.52.5", "@rollup/rollup-darwin-arm64": "4.52.5", "@rollup/rollup-darwin-x64": "4.52.5", "@rollup/rollup-freebsd-arm64": "4.52.5", "@rollup/rollup-freebsd-x64": "4.52.5", "@rollup/rollup-linux-arm-gnueabihf": "4.52.5", "@rollup/rollup-linux-arm-musleabihf": "4.52.5", "@rollup/rollup-linux-arm64-gnu": "4.52.5", "@rollup/rollup-linux-arm64-musl": "4.52.5", "@rollup/rollup-linux-loong64-gnu": "4.52.5", "@rollup/rollup-linux-ppc64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-gnu": "4.52.5", "@rollup/rollup-linux-riscv64-musl": "4.52.5", "@rollup/rollup-linux-s390x-gnu": "4.52.5", "@rollup/rollup-linux-x64-gnu": "4.52.5", "@rollup/rollup-linux-x64-musl": "4.52.5", "@rollup/rollup-openharmony-arm64": "4.52.5", "@rollup/rollup-win32-arm64-msvc": "4.52.5", "@rollup/rollup-win32-ia32-msvc": "4.52.5", "@rollup/rollup-win32-x64-gnu": "4.52.5", "@rollup/rollup-win32-x64-msvc": "4.52.5", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw=="],
|
||||
|
||||
"sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="],
|
||||
|
||||
"set-cookie-parser": ["set-cookie-parser@2.7.2", "", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="],
|
||||
|
||||
"sirv": ["sirv@3.0.2", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g=="],
|
||||
|
||||
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||
|
||||
"svelte": ["svelte@5.43.4", "", { "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^2.1.0", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-tPNp21nDWB0PSHE+VrTvEy9cFtDp2Q+ATxQoFomISEVdikZ1QZ69UqBPz/LlT+Oc8/LYS/COYwDQZrmZEUr+JQ=="],
|
||||
|
||||
"svelte-check": ["svelte-check@4.3.3", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-RYP0bEwenDXzfv0P1sKAwjZSlaRyqBn0Fz1TVni58lqyEiqgwztTpmodJrGzP6ZT2aHl4MbTvWP6gbmQ3FOnBg=="],
|
||||
|
||||
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
||||
|
||||
"totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="],
|
||||
|
||||
"typescript": ["typescript@5.6.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw=="],
|
||||
|
||||
"vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="],
|
||||
|
||||
"vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="],
|
||||
|
||||
"zimmerframe": ["zimmerframe@1.1.4", "", {}, "sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ=="],
|
||||
}
|
||||
}
|
||||
31
package.json
@@ -13,17 +13,28 @@
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2",
|
||||
"@tauri-apps/plugin-opener": "^2"
|
||||
"@iconify/json": "^2.2.404",
|
||||
"@iconify/svelte": "^5.1.0",
|
||||
"@iconify/tailwind": "^1.2.0",
|
||||
"@tailwindcss/vite": "^4.1.17",
|
||||
"@tauri-apps/api": "^2.9.0",
|
||||
"@tauri-apps/plugin-http": "^2.5.4",
|
||||
"@tauri-apps/plugin-opener": "^2.5.2",
|
||||
"@tauri-apps/plugin-process": "^2.3.1",
|
||||
"@tauri-apps/plugin-updater": "^2.9.0",
|
||||
"axios": "^1.13.2",
|
||||
"tailwindcss": "^4.1.17"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@sveltejs/adapter-static": "^3.0.6",
|
||||
"@sveltejs/kit": "^2.9.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
||||
"svelte": "^5.0.0",
|
||||
"svelte-check": "^4.0.0",
|
||||
"typescript": "~5.6.2",
|
||||
"vite": "^6.0.3",
|
||||
"@tauri-apps/cli": "^2"
|
||||
"@skeletonlabs/skeleton": "^4.2.4",
|
||||
"@skeletonlabs/skeleton-svelte": "^4.2.4",
|
||||
"@sveltejs/adapter-static": "^3.0.10",
|
||||
"@sveltejs/kit": "^2.48.4",
|
||||
"@sveltejs/vite-plugin-svelte": "^5.1.1",
|
||||
"@tauri-apps/cli": "^2.9.3",
|
||||
"svelte": "^5.43.4",
|
||||
"svelte-check": "^4.3.3",
|
||||
"typescript": "~5.6.3",
|
||||
"vite": "^6.4.1"
|
||||
}
|
||||
}
|
||||
|
||||
627
src-tauri/Cargo.lock
generated
@@ -47,6 +47,15 @@ version = "1.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
|
||||
dependencies = [
|
||||
"derive_arbitrary",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "async-broadcast"
|
||||
version = "0.7.2"
|
||||
@@ -397,9 +406,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.44"
|
||||
version = "1.2.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37521ac7aabe3d13122dc382493e20c9416f299d2ccd5b3a5340a2570cdeb0f3"
|
||||
checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"shlex",
|
||||
@@ -487,10 +496,39 @@ version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
|
||||
dependencies = [
|
||||
"percent-encoding",
|
||||
"time",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cookie_store"
|
||||
version = "0.21.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2eac901828f88a5241ee0600950ab981148a18f2f756900ffba1b125ca6a3ef9"
|
||||
dependencies = [
|
||||
"cookie",
|
||||
"document-features",
|
||||
"idna",
|
||||
"log",
|
||||
"publicsuffix",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"time",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.10.1"
|
||||
@@ -514,7 +552,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"core-foundation",
|
||||
"core-foundation 0.10.1",
|
||||
"core-graphics-types",
|
||||
"foreign-types",
|
||||
"libc",
|
||||
@@ -527,7 +565,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"core-foundation",
|
||||
"core-foundation 0.10.1",
|
||||
"libc",
|
||||
]
|
||||
|
||||
@@ -646,6 +684,12 @@ dependencies = [
|
||||
"syn 2.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data-url"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376"
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.5.5"
|
||||
@@ -656,6 +700,17 @@ dependencies = [
|
||||
"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.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.99.20"
|
||||
@@ -688,6 +743,16 @@ dependencies = [
|
||||
"dirs-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-next"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"dirs-sys-next",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.5.0"
|
||||
@@ -696,10 +761,21 @@ checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users",
|
||||
"redox_users 0.5.2",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys-next"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users 0.4.6",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dispatch"
|
||||
version = "0.2.0"
|
||||
@@ -750,6 +826,15 @@ dependencies = [
|
||||
"syn 2.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "document-features"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
|
||||
dependencies = [
|
||||
"litrs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dpi"
|
||||
version = "0.1.2"
|
||||
@@ -806,6 +891,15 @@ version = "1.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7"
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "endi"
|
||||
version = "1.1.0"
|
||||
@@ -841,9 +935,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "erased-serde"
|
||||
version = "0.4.8"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "259d404d09818dec19332e31d94558aeb442fea04c817006456c24b5460bbd4b"
|
||||
checksum = "89e8918065695684b2b0702da20382d5ae6065cf3327bc2d6436bd49a71ce9f3"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_core",
|
||||
@@ -906,6 +1000,18 @@ dependencies = [
|
||||
"rustc_version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"libredox",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.4"
|
||||
@@ -1195,8 +1301,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1206,9 +1314,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasip2",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1247,11 +1357,15 @@ dependencies = [
|
||||
name = "gitea-desktop"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"dirs-next",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-build",
|
||||
"tauri-plugin-http",
|
||||
"tauri-plugin-opener",
|
||||
"tauri-plugin-process",
|
||||
"tauri-plugin-updater",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1370,6 +1484,25 @@ dependencies = [
|
||||
"syn 2.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "h2"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bytes",
|
||||
"fnv",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"http",
|
||||
"indexmap 2.12.0",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
@@ -1468,6 +1601,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"h2",
|
||||
"http",
|
||||
"http-body",
|
||||
"httparse",
|
||||
@@ -1479,6 +1613,23 @@ dependencies = [
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-rustls"
|
||||
version = "0.27.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58"
|
||||
dependencies = [
|
||||
"http",
|
||||
"hyper",
|
||||
"hyper-util",
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tower-service",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-util"
|
||||
version = "0.1.17"
|
||||
@@ -1498,9 +1649,11 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"system-configuration",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
"windows-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1872,6 +2025,7 @@ checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1886,6 +2040,12 @@ version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
|
||||
|
||||
[[package]]
|
||||
name = "litrs"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.14"
|
||||
@@ -1901,6 +2061,12 @@ version = "0.4.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
|
||||
|
||||
[[package]]
|
||||
name = "lru-slab"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
|
||||
|
||||
[[package]]
|
||||
name = "mac"
|
||||
version = "0.1.1"
|
||||
@@ -1959,6 +2125,12 @@ version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "minisign-verify"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e856fdd13623a2f5f2f54676a4ee49502a96a80ef4a62bcedd23d52427c44d43"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.9"
|
||||
@@ -2294,6 +2466,18 @@ dependencies = [
|
||||
"objc2-foundation 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-osa-kit"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f112d1746737b0da274ef79a23aac283376f335f4095a083a267a082f21db0c0"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"objc2 0.6.3",
|
||||
"objc2-app-kit",
|
||||
"objc2-foundation 0.3.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc2-quartz-core"
|
||||
version = "0.2.2"
|
||||
@@ -2391,6 +2575,20 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "osakit"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "732c71caeaa72c065bb69d7ea08717bd3f4863a4f451402fc9513e29dbd5261b"
|
||||
dependencies = [
|
||||
"objc2 0.6.3",
|
||||
"objc2-foundation 0.3.2",
|
||||
"objc2-osa-kit",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pango"
|
||||
version = "0.18.3"
|
||||
@@ -2758,6 +2956,22 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "psl-types"
|
||||
version = "2.0.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac"
|
||||
|
||||
[[package]]
|
||||
name = "publicsuffix"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf"
|
||||
dependencies = [
|
||||
"idna",
|
||||
"psl-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.38.3"
|
||||
@@ -2767,6 +2981,61 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quinn"
|
||||
version = "0.11.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"cfg_aliases",
|
||||
"pin-project-lite",
|
||||
"quinn-proto",
|
||||
"quinn-udp",
|
||||
"rustc-hash",
|
||||
"rustls",
|
||||
"socket2",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quinn-proto"
|
||||
version = "0.11.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"getrandom 0.3.4",
|
||||
"lru-slab",
|
||||
"rand 0.9.2",
|
||||
"ring",
|
||||
"rustc-hash",
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"slab",
|
||||
"thiserror 2.0.17",
|
||||
"tinyvec",
|
||||
"tracing",
|
||||
"web-time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quinn-udp"
|
||||
version = "0.5.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd"
|
||||
dependencies = [
|
||||
"cfg_aliases",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"socket2",
|
||||
"tracing",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.42"
|
||||
@@ -2807,6 +3076,16 @@ dependencies = [
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||
dependencies = [
|
||||
"rand_chacha 0.9.0",
|
||||
"rand_core 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.2.2"
|
||||
@@ -2827,6 +3106,16 @@ dependencies = [
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.5.1"
|
||||
@@ -2845,6 +3134,15 @@ dependencies = [
|
||||
"getrandom 0.2.16",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
||||
dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.2.0"
|
||||
@@ -2878,6 +3176,17 @@ dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
|
||||
dependencies = [
|
||||
"getrandom 0.2.16",
|
||||
"libredox",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.5.2"
|
||||
@@ -2946,22 +3255,32 @@ checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"cookie",
|
||||
"cookie_store",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
"http",
|
||||
"http-body",
|
||||
"http-body-util",
|
||||
"hyper",
|
||||
"hyper-rustls",
|
||||
"hyper-util",
|
||||
"js-sys",
|
||||
"log",
|
||||
"mime",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"quinn",
|
||||
"rustls",
|
||||
"rustls-pki-types",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"tokio-util",
|
||||
"tower",
|
||||
"tower-http",
|
||||
@@ -2971,8 +3290,29 @@ dependencies = [
|
||||
"wasm-bindgen-futures",
|
||||
"wasm-streams",
|
||||
"web-sys",
|
||||
"webpki-roots",
|
||||
]
|
||||
|
||||
[[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.16",
|
||||
"libc",
|
||||
"untrusted",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "2.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
@@ -2995,6 +3335,41 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.23.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f"
|
||||
dependencies = [
|
||||
"once_cell",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a"
|
||||
dependencies = [
|
||||
"web-time",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustls-webpki"
|
||||
version = "0.103.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52"
|
||||
dependencies = [
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"untrusted",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.22"
|
||||
@@ -3428,6 +3803,12 @@ version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "swift-rs"
|
||||
version = "1.0.7"
|
||||
@@ -3481,6 +3862,27 @@ dependencies = [
|
||||
"syn 2.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"core-foundation 0.9.4",
|
||||
"system-configuration-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-configuration-sys"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "system-deps"
|
||||
version = "6.2.2"
|
||||
@@ -3502,7 +3904,7 @@ checksum = "f3a753bdc39c07b192151523a3f77cd0394aa75413802c883a0f6f6a0e5ee2e7"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"block2 0.6.2",
|
||||
"core-foundation",
|
||||
"core-foundation 0.10.1",
|
||||
"core-graphics",
|
||||
"crossbeam-channel",
|
||||
"dispatch",
|
||||
@@ -3545,6 +3947,17 @@ dependencies = [
|
||||
"syn 2.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tar"
|
||||
version = "0.4.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a"
|
||||
dependencies = [
|
||||
"filetime",
|
||||
"libc",
|
||||
"xattr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "target-lexicon"
|
||||
version = "0.12.16"
|
||||
@@ -3682,6 +4095,52 @@ dependencies = [
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-fs"
|
||||
version = "2.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47df422695255ecbe7bac7012440eddaeefd026656171eac9559f5243d3230d9"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dunce",
|
||||
"glob",
|
||||
"percent-encoding",
|
||||
"schemars 0.8.22",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"tauri-utils",
|
||||
"thiserror 2.0.17",
|
||||
"toml 0.9.8",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-http"
|
||||
version = "2.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c00685aceab12643cf024f712ab0448ba8fcadf86f2391d49d2e5aa732aacc70"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"cookie_store",
|
||||
"data-url",
|
||||
"http",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"schemars 0.8.22",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"tauri-plugin-fs",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"url",
|
||||
"urlpattern",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-opener"
|
||||
version = "2.5.2"
|
||||
@@ -3704,6 +4163,48 @@ dependencies = [
|
||||
"zbus",
|
||||
]
|
||||
|
||||
[[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.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27cbc31740f4d507712550694749572ec0e43bdd66992db7599b89fbfd6b167b"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"dirs",
|
||||
"flate2",
|
||||
"futures-util",
|
||||
"http",
|
||||
"infer",
|
||||
"log",
|
||||
"minisign-verify",
|
||||
"osakit",
|
||||
"percent-encoding",
|
||||
"reqwest",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tar",
|
||||
"tauri",
|
||||
"tauri-plugin",
|
||||
"tempfile",
|
||||
"thiserror 2.0.17",
|
||||
"time",
|
||||
"tokio",
|
||||
"url",
|
||||
"windows-sys 0.60.2",
|
||||
"zip",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime"
|
||||
version = "2.9.1"
|
||||
@@ -3909,6 +4410,21 @@ dependencies = [
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec_macros"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.48.0"
|
||||
@@ -3920,9 +4436,31 @@ dependencies = [
|
||||
"mio",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio-macros",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.26.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61"
|
||||
dependencies = [
|
||||
"rustls",
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.17"
|
||||
@@ -4212,6 +4750,12 @@ version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.7"
|
||||
@@ -4413,6 +4957,16 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "web-time"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webkit2gtk"
|
||||
version = "2.0.1"
|
||||
@@ -4457,6 +5011,15 @@ dependencies = [
|
||||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webview2-com"
|
||||
version = "0.38.0"
|
||||
@@ -4642,6 +5205,17 @@ dependencies = [
|
||||
"windows-link 0.1.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-registry"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e"
|
||||
dependencies = [
|
||||
"windows-link 0.1.3",
|
||||
"windows-result 0.3.4",
|
||||
"windows-strings 0.4.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-result"
|
||||
version = "0.3.4"
|
||||
@@ -4687,6 +5261,15 @@ dependencies = [
|
||||
"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]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
@@ -5024,6 +5607,16 @@ dependencies = [
|
||||
"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]]
|
||||
name = "yoke"
|
||||
version = "0.8.1"
|
||||
@@ -5149,6 +5742,12 @@ dependencies = [
|
||||
"synstructure",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
|
||||
|
||||
[[package]]
|
||||
name = "zerotrie"
|
||||
version = "0.2.3"
|
||||
@@ -5182,6 +5781,18 @@ dependencies = [
|
||||
"syn 2.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "4.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "caa8cd6af31c3b31c6631b8f483848b91589021b28fffe50adada48d4f4d2ed1"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"crc32fast",
|
||||
"indexmap 2.12.0",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zvariant"
|
||||
version = "5.8.0"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
[package]
|
||||
name = "gitea-desktop"
|
||||
version = "0.1.0"
|
||||
description = "A Tauri App"
|
||||
authors = ["you"]
|
||||
description = "Gitea Desktop Application"
|
||||
authors = ["SirBlob"]
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
@@ -20,6 +20,10 @@ tauri-build = { version = "2", features = [] }
|
||||
[dependencies]
|
||||
tauri = { version = "2", features = [] }
|
||||
tauri-plugin-opener = "2"
|
||||
tauri-plugin-http = "2"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
dirs-next = "2"
|
||||
tauri-plugin-updater = "2"
|
||||
tauri-plugin-process = "2"
|
||||
|
||||
|
||||
@@ -5,6 +5,29 @@
|
||||
"windows": ["main"],
|
||||
"permissions": [
|
||||
"core:default",
|
||||
"opener:default"
|
||||
"core:window:allow-minimize",
|
||||
"core:window:allow-maximize",
|
||||
"core:window:allow-unmaximize",
|
||||
"core:window:allow-close",
|
||||
"core:window:allow-toggle-maximize",
|
||||
"core:window:allow-is-maximized",
|
||||
"opener:default",
|
||||
"http:default",
|
||||
"updater:default",
|
||||
"updater:allow-check",
|
||||
"updater:allow-download-and-install",
|
||||
"process:default",
|
||||
"process:allow-restart",
|
||||
{
|
||||
"identifier": "http:allow-fetch",
|
||||
"allow": [
|
||||
{
|
||||
"url": "https://**"
|
||||
},
|
||||
{
|
||||
"url": "http://**"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 974 B |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 903 B |
|
Before Width: | Height: | Size: 8.4 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
BIN
src-tauri/icons/favicon.ico
Normal file
|
After Width: | Height: | Size: 166 KiB |
BIN
src-tauri/icons/favicon.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 14 KiB |
23
src-tauri/src/git/mod.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
|
||||
|
||||
pub fn git_check_installed() -> bool {
|
||||
match std::process::Command::new("git").arg("--version").output() {
|
||||
Ok(output) => output.status.success(),
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn git_get_version() -> Result<String, String> {
|
||||
match std::process::Command::new("git").arg("--version").output() {
|
||||
Ok(output) => {
|
||||
if output.status.success() {
|
||||
let version = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
||||
Ok(version)
|
||||
} else {
|
||||
Err("Failed to get git version".to_string())
|
||||
}
|
||||
}
|
||||
Err(e) => Err(e.to_string()),
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,74 @@
|
||||
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
||||
|
||||
pub mod git;
|
||||
|
||||
#[tauri::command]
|
||||
fn greet(name: &str) -> String {
|
||||
git::git_check_installed();
|
||||
format!("Hello, {}! You've been greeted from Rust!", name)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn store_token(token: String) -> Result<(), String> {
|
||||
// Persist token to the app config directory. For MVP this is plaintext storage.
|
||||
// Replace with OS keychain plugin (tauri-plugin-keychain) for production.
|
||||
let dir = dirs_next::config_dir().ok_or_else(|| "could not determine config directory".to_string())?;
|
||||
let app_dir = dir.join("gitea-desktop");
|
||||
std::fs::create_dir_all(&app_dir).map_err(|e| e.to_string())?;
|
||||
let token_file = app_dir.join("token");
|
||||
std::fs::write(token_file, token).map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn get_token() -> Result<String, String> {
|
||||
let dir = dirs_next::config_dir().ok_or_else(|| "could not determine config directory".to_string())?;
|
||||
let token_file = dir.join("gitea-desktop").join("token");
|
||||
match std::fs::read_to_string(token_file) {
|
||||
Ok(s) => Ok(s),
|
||||
Err(e) => {
|
||||
if e.kind() == std::io::ErrorKind::NotFound {
|
||||
Ok(String::new())
|
||||
} else {
|
||||
Err(e.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn store_base_url(base_url: String) -> Result<(), String> {
|
||||
let dir = dirs_next::config_dir().ok_or_else(|| "could not determine config directory".to_string())?;
|
||||
let app_dir = dir.join("gitea-desktop");
|
||||
std::fs::create_dir_all(&app_dir).map_err(|e| e.to_string())?;
|
||||
let url_file = app_dir.join("base_url");
|
||||
std::fs::write(url_file, base_url).map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn get_base_url() -> Result<String, String> {
|
||||
let dir = dirs_next::config_dir().ok_or_else(|| "could not determine config directory".to_string())?;
|
||||
let url_file = dir.join("gitea-desktop").join("base_url");
|
||||
match std::fs::read_to_string(url_file) {
|
||||
Ok(s) => Ok(s),
|
||||
Err(e) => {
|
||||
if e.kind() == std::io::ErrorKind::NotFound {
|
||||
Ok(String::new())
|
||||
} else {
|
||||
Err(e.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.invoke_handler(tauri::generate_handler![greet])
|
||||
.plugin(tauri_plugin_http::init())
|
||||
.plugin(tauri_plugin_updater::Builder::new().build())
|
||||
.plugin(tauri_plugin_process::init())
|
||||
.invoke_handler(tauri::generate_handler![greet, store_token, get_token, store_base_url, get_base_url])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
|
||||
@@ -1,35 +1,41 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "gitea-desktop",
|
||||
"version": "0.1.0",
|
||||
"identifier": "co.sirblob.gitea-desktop",
|
||||
"build": {
|
||||
"beforeDevCommand": "bun run dev",
|
||||
"devUrl": "http://localhost:1420",
|
||||
"beforeBuildCommand": "bun run build",
|
||||
"frontendDist": "../build"
|
||||
},
|
||||
"app": {
|
||||
"windows": [
|
||||
{
|
||||
"title": "gitea-desktop",
|
||||
"width": 800,
|
||||
"height": 600
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": null
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/128x128@2x.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
]
|
||||
}
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "gitea-desktop",
|
||||
"version": "0.1.0",
|
||||
"identifier": "co.sirblob.gitea-desktop",
|
||||
"build": {
|
||||
"beforeDevCommand": "bun run dev",
|
||||
"devUrl": "http://localhost:1420",
|
||||
"beforeBuildCommand": "bun run build",
|
||||
"frontendDist": "../build"
|
||||
},
|
||||
"app": {
|
||||
"windows": [
|
||||
{
|
||||
"title": "gitea-desktop",
|
||||
"width": 1200,
|
||||
"height": 800,
|
||||
"decorations": false,
|
||||
"transparent": false
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": null
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"updater": {
|
||||
"active": true,
|
||||
"endpoints": [
|
||||
"https://git.sirblob.co/SirBlob/Gitea-Desktop/releases/latest/download/latest.json"
|
||||
],
|
||||
"dialog": true,
|
||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDQzNkEzQURGNEE5QUFGQ0QKUldUTnI1cEszenBxUXlWUE1INjR6RGdtSlUvSUMyaTNxZjBteDI4T0d5WWRaNm5HODFNbDN0SG4K"
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": "all",
|
||||
"icon": ["icons/favicon.png", "icons/favicon.ico"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<html lang="en" data-theme="cerberus">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Tauri + SvelteKit + Typescript App</title>
|
||||
<title>Gitea Desktop</title>
|
||||
%sveltekit.head%
|
||||
</head>
|
||||
<body data-sveltekit-preload-data="hover">
|
||||
<body data-sveltekit-preload-data="hover" >
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
37
src/lib/components/EmptyState.svelte
Normal file
@@ -0,0 +1,37 @@
|
||||
<script lang="ts">
|
||||
import Icon from "@iconify/svelte";
|
||||
</script>
|
||||
|
||||
<div class="empty-state">
|
||||
<div class="text-center max-w-md px-8">
|
||||
<div class="mb-6 flex justify-center">
|
||||
<Icon icon="simple-icons:gitea" class="text-[128px] text-[#3e4451]" />
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold mb-2 text-white">Get started with Gitea Desktop</h3>
|
||||
<p class="text-[#9399a8] mb-6">
|
||||
Select a repository from the list or clone a new one to get started.
|
||||
</p>
|
||||
<div class="space-y-2">
|
||||
<button class="w-full px-4 py-2.5 rounded bg-[#4c9ac9] hover:bg-[#5da9d6] text-white font-medium text-sm transition-colors flex items-center justify-center gap-2">
|
||||
<Icon icon="mdi:download" class="text-base" />
|
||||
Clone a Repository
|
||||
</button>
|
||||
<button class="w-full px-4 py-2.5 rounded bg-[#3e4451] hover:bg-[#4a5064] text-[#c0c6d4] font-medium text-sm transition-colors flex items-center justify-center gap-2">
|
||||
<Icon icon="mdi:plus" class="text-base" />
|
||||
Create New Repository
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.empty-state {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #1c2027;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
157
src/lib/components/RepositoryList.svelte
Normal file
@@ -0,0 +1,157 @@
|
||||
<script lang="ts">
|
||||
import Icon from "@iconify/svelte";
|
||||
import type { Repository } from "$lib/gitea/types";
|
||||
|
||||
interface Props {
|
||||
repos: Repository[];
|
||||
selectedRepo: Repository | null;
|
||||
loading: boolean;
|
||||
error: string;
|
||||
onSelectRepo: (repo: Repository) => void;
|
||||
}
|
||||
|
||||
let { repos, selectedRepo, loading, error, onSelectRepo }: Props = $props();
|
||||
|
||||
let searchQuery = $state("");
|
||||
|
||||
let filteredRepos = $derived(
|
||||
searchQuery.trim()
|
||||
? repos.filter(
|
||||
(repo) =>
|
||||
repo.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
|
||||
repo.owner.login
|
||||
.toLowerCase()
|
||||
.includes(searchQuery.toLowerCase()) ||
|
||||
repo.description?.toLowerCase().includes(searchQuery.toLowerCase())
|
||||
)
|
||||
: repos
|
||||
);
|
||||
</script>
|
||||
|
||||
<div class="flex-1 flex flex-col min-h-0">
|
||||
|
||||
<div class="p-3 border-b border-[#3e4451] shrink-0">
|
||||
<div class="relative">
|
||||
<Icon
|
||||
icon="mdi:magnify"
|
||||
class="absolute left-3 top-1/2 -translate-y-1/2 text-[#6b7280] text-base"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Find a repository..."
|
||||
bind:value={searchQuery}
|
||||
class="w-full pl-9 pr-3 py-2 bg-[#1c2027] border border-[#3e4451] rounded-lg text-sm text-white placeholder-[#6b7280] focus:outline-none focus:border-[#4c9ac9] focus:ring-1 focus:ring-[#4c9ac9]/50 transition-all"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex-1 overflow-y-auto p-2 min-h-0">
|
||||
{#if error}
|
||||
<div class="m-2">
|
||||
<div
|
||||
class="px-3 py-2 rounded bg-[#7d2f2f] border border-[#a04141] text-white text-xs"
|
||||
>
|
||||
{error}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
{#if loading && repos.length === 0}
|
||||
<div class="flex flex-col items-center justify-center py-12 text-center">
|
||||
<Icon
|
||||
icon="mdi:loading"
|
||||
class="text-2xl mb-2 animate-spin text-[#84c5fb]"
|
||||
/>
|
||||
<p class="text-sm text-[#9399a8]">Loading repositories...</p>
|
||||
</div>
|
||||
{:else if repos.length === 0}
|
||||
<div
|
||||
class="flex flex-col items-center justify-center py-12 text-center px-4"
|
||||
>
|
||||
<p class="text-sm text-[#9399a8] mb-2">No repositories found</p>
|
||||
<p class="text-xs text-[#6b7280]">
|
||||
Create a repository on your Gitea instance to get started
|
||||
</p>
|
||||
</div>
|
||||
{:else if filteredRepos.length === 0}
|
||||
<div
|
||||
class="flex flex-col items-center justify-center py-12 text-center px-4"
|
||||
>
|
||||
<Icon
|
||||
icon="mdi:file-search-outline"
|
||||
class="text-4xl text-[#3e4451] mb-2"
|
||||
/>
|
||||
<p class="text-sm text-[#9399a8] mb-1">
|
||||
No repositories match your search
|
||||
</p>
|
||||
<p class="text-xs text-[#6b7280]">Try a different search term</p>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="space-y-1">
|
||||
{#each filteredRepos as repo}
|
||||
<button
|
||||
class={"w-full px-3 py-2.5 rounded text-left transition-colors " +
|
||||
(selectedRepo?.id === repo.id
|
||||
? "bg-[#4c9ac9] text-white"
|
||||
: "hover:bg-[#3e4451] text-[#c0c6d4]")}
|
||||
onclick={() => onSelectRepo(repo)}
|
||||
>
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex items-center gap-2 mb-1">
|
||||
<span class="font-semibold truncate text-sm">{repo.name}</span>
|
||||
{#if repo.private}
|
||||
<span
|
||||
class="px-1.5 py-0.5 rounded text-[10px] font-medium bg-[#7d2f2f] text-white flex items-center gap-1"
|
||||
>
|
||||
<Icon icon="mdi:lock" class="text-[10px]" />
|
||||
Private
|
||||
</span>
|
||||
{:else}
|
||||
<span
|
||||
class="px-1.5 py-0.5 rounded text-[10px] font-medium bg-[#2d6a3d] text-white flex items-center gap-1"
|
||||
>
|
||||
<Icon icon="mdi:earth" class="text-[10px]" />
|
||||
Public
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="text-xs {
|
||||
selectedRepo?.id === repo.id
|
||||
? "text-[#d1d5db]"
|
||||
: "text-[#6b7280]"
|
||||
} truncate">
|
||||
{repo.owner.login}
|
||||
</div>
|
||||
{#if repo.description}
|
||||
<div class="text-xs {
|
||||
selectedRepo?.id === repo.id
|
||||
? "text-[#d1d5db]"
|
||||
: "text-[#6b7280]"
|
||||
} truncate mt-1">
|
||||
{repo.description}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="p-3 border-t border-[#3e4451] space-y-2 bg-[#23272f] shrink-0">
|
||||
<button
|
||||
class="btn w-full px-4 py-2 rounded bg-[#4c9ac9] hover:bg-[#5da9d6] text-white font-medium text-sm transition-colors flex items-center justify-center gap-2"
|
||||
>
|
||||
<Icon icon="mdi:download" class="text-base" />
|
||||
Clone Repository
|
||||
</button>
|
||||
<button
|
||||
class="btn w-full px-4 py-2 rounded bg-[#3e4451] hover:bg-[#4a5064] text-[#c0c6d4] font-medium text-sm transition-colors flex items-center justify-center gap-2"
|
||||
>
|
||||
<Icon icon="mdi:plus" class="text-base" />
|
||||
Create New Repository
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
47
src/lib/components/RepositorySelector.svelte
Normal file
@@ -0,0 +1,47 @@
|
||||
<script lang="ts">
|
||||
import Icon from "@iconify/svelte";
|
||||
import type { Repository } from "$lib/gitea/types";
|
||||
|
||||
interface Props {
|
||||
selectedRepo: Repository | null;
|
||||
loading: boolean;
|
||||
onRefresh: () => void;
|
||||
}
|
||||
|
||||
let { selectedRepo, loading, onRefresh }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="p-3 border-b border-[#3e4451]">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<span class="text-xs font-semibold text-[#9399a8] uppercase tracking-wide">Current Repository</span>
|
||||
<button
|
||||
class="p-1.5 rounded hover:bg-[#3e4451] text-[#9399a8] hover:text-white transition-colors disabled:opacity-50"
|
||||
onclick={onRefresh}
|
||||
disabled={loading}
|
||||
title="Refresh repositories"
|
||||
>
|
||||
{#if loading}
|
||||
<Icon icon="mdi:loading" class="text-sm animate-spin" />
|
||||
{:else}
|
||||
<Icon icon="mdi:refresh" class="text-sm" />
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{#if selectedRepo}
|
||||
<button class="w-full px-3 py-2 rounded bg-[#3e4451] hover:bg-[#4a5064] text-left transition-colors flex items-center justify-between group">
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="font-semibold truncate text-white text-sm">{selectedRepo.name}</div>
|
||||
<div class="text-xs text-[#9399a8] truncate">
|
||||
{selectedRepo.owner.login}
|
||||
</div>
|
||||
</div>
|
||||
<Icon icon="mdi:chevron-down" class="text-[#9399a8] ml-2 shrink-0" />
|
||||
</button>
|
||||
{:else}
|
||||
<button class="w-full px-3 py-2 rounded bg-[#3e4451] hover:bg-[#4a5064] text-left transition-colors flex items-center justify-between">
|
||||
<span class="text-[#9399a8] text-sm">Select a repository...</span>
|
||||
<Icon icon="mdi:chevron-down" class="text-[#9399a8] ml-2" />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
167
src/lib/components/RepositoryView.svelte
Normal file
@@ -0,0 +1,167 @@
|
||||
<script lang="ts">
|
||||
import Icon from "@iconify/svelte";
|
||||
import type { Repository } from "$lib/gitea/types";
|
||||
|
||||
interface Props {
|
||||
repo: Repository;
|
||||
baseUrl: string;
|
||||
}
|
||||
|
||||
let { repo, baseUrl }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="repository-view">
|
||||
|
||||
<div class="bg-[#2a2e3a] border-b border-[#3e4451] p-6 shrink-0">
|
||||
<div class="flex items-start justify-between mb-4">
|
||||
<div>
|
||||
<h2 class="text-xl font-bold mb-2 text-white">
|
||||
<span class="text-[#84c5fb]">
|
||||
<a href="{baseUrl}\{repo.owner.login}" target="_blank">{repo.owner.login}</a>
|
||||
</span>
|
||||
<span class="text-[#6b7280] mx-2">/</span>
|
||||
{repo.name}
|
||||
</h2>
|
||||
{#if repo.description}
|
||||
<p class="text-[#9399a8] text-sm">{repo.description}</p>
|
||||
{/if}
|
||||
</div>
|
||||
{#if repo.html_url}
|
||||
<a
|
||||
href={repo.html_url}
|
||||
target="_blank"
|
||||
class="px-4 py-2 rounded bg-[#3e4451] hover:bg-[#4a5064] text-[#c0c6d4] text-sm font-medium transition-colors flex items-center gap-2"
|
||||
>
|
||||
<Icon icon="mdi:open-in-new" class="text-base" />
|
||||
View on Gitea
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex gap-6 text-sm">
|
||||
{#if repo.stars_count !== undefined}
|
||||
<div class="flex items-center gap-2 text-[#9399a8]">
|
||||
<Icon icon="mdi:star" class="text-[#fbbf24]" />
|
||||
<span class="font-semibold text-white">{repo.stars_count}</span>
|
||||
<span>stars</span>
|
||||
</div>
|
||||
{/if}
|
||||
{#if repo.forks_count !== undefined}
|
||||
<div class="flex items-center gap-2 text-[#9399a8]">
|
||||
<Icon icon="mdi:source-fork" class="text-[#84c5fb]" />
|
||||
<span class="font-semibold text-white">{repo.forks_count}</span>
|
||||
<span>forks</span>
|
||||
</div>
|
||||
{/if}
|
||||
{#if repo.open_issues_count !== undefined}
|
||||
<div class="flex items-center gap-2 text-[#9399a8]">
|
||||
<Icon icon="mdi:alert-circle" class="text-[#ef4444]" />
|
||||
<span class="font-semibold text-white">{repo.open_issues_count}</span>
|
||||
<span>issues</span>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex border-b border-[#3e4451] bg-[#23272f] shrink-0">
|
||||
<button class="px-6 py-3 border-b-2 border-[#4c9ac9] font-semibold text-white text-sm">
|
||||
Changes
|
||||
</button>
|
||||
<button class="px-6 py-3 text-[#9399a8] hover:text-white text-sm transition-colors">
|
||||
History
|
||||
</button>
|
||||
<button class="px-6 py-3 text-[#9399a8] hover:text-white text-sm transition-colors">
|
||||
Pull Requests
|
||||
</button>
|
||||
<button class="px-6 py-3 text-[#9399a8] hover:text-white text-sm transition-colors">
|
||||
Actions
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex-1 overflow-y-auto p-6 min-h-0">
|
||||
<div class="max-w-3xl mx-auto">
|
||||
|
||||
<div class="text-center py-16">
|
||||
<div class="mb-6 flex justify-center">
|
||||
<Icon icon="mdi:file-document-outline" class="text-[128px] text-[#3e4451]" />
|
||||
</div>
|
||||
<h3 class="text-xl font-semibold mb-2 text-white">No local changes</h3>
|
||||
<p class="text-[#9399a8] mb-8">
|
||||
There are no uncommitted changes in this repository. Here are some friendly suggestions for what to do next.
|
||||
</p>
|
||||
|
||||
<div class="space-y-3 text-left">
|
||||
<div class="rounded-lg bg-[#2a2e3a] border border-[#3e4451] p-4">
|
||||
<h4 class="font-semibold mb-2 text-white">Open the repository in your external editor</h4>
|
||||
<p class="text-sm text-[#9399a8] mb-3">
|
||||
Select your editor in Options
|
||||
</p>
|
||||
<button class="px-4 py-2 rounded bg-[#4c9ac9] hover:bg-[#5da9d6] text-white font-medium text-sm transition-colors flex items-center gap-2">
|
||||
<Icon icon="mdi:microsoft-visual-studio-code" class="text-base" />
|
||||
Open in Visual Studio Code
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg bg-[#2a2e3a] border border-[#3e4451] p-4">
|
||||
<h4 class="font-semibold mb-2 text-white">View the files of your repository in Explorer</h4>
|
||||
<p class="text-sm text-[#9399a8] mb-3">
|
||||
Repository menu or <kbd class="px-2 py-1 rounded bg-[#3e4451] text-xs text-[#c0c6d4] font-mono">Ctrl + Shift + F</kbd>
|
||||
</p>
|
||||
<button class="px-4 py-2 rounded bg-[#4c9ac9] hover:bg-[#5da9d6] text-white font-medium text-sm transition-colors flex items-center gap-2">
|
||||
<Icon icon="mdi:folder-open" class="text-base" />
|
||||
Show in Explorer
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="rounded-lg bg-[#2a2e3a] border border-[#3e4451] p-4">
|
||||
<h4 class="font-semibold mb-2 text-white">Open the repository page on Gitea in your browser</h4>
|
||||
<p class="text-sm text-[#9399a8] mb-3">
|
||||
Repository menu or <kbd class="px-2 py-1 rounded bg-[#3e4451] text-xs text-[#c0c6d4] font-mono">Ctrl + Shift + G</kbd>
|
||||
</p>
|
||||
{#if repo.html_url}
|
||||
<a href={repo.html_url} target="_blank" class="inline-flex px-4 py-2 rounded bg-[#4c9ac9] hover:bg-[#5da9d6] text-white font-medium text-sm transition-colors items-center gap-2">
|
||||
<Icon icon="mdi:web" class="text-base" />
|
||||
View on Gitea
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="bg-[#2a2e3a] border-t border-[#3e4451] px-6 py-3 flex items-center justify-between shrink-0">
|
||||
<div class="flex items-center gap-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-xs font-semibold text-[#9399a8]">Current branch</span>
|
||||
<button class="px-3 py-1.5 rounded bg-[#3e4451] hover:bg-[#4a5064] text-[#c0c6d4] text-sm transition-colors flex items-center gap-2">
|
||||
<Icon icon="mdi:source-branch" class="text-base" />
|
||||
main
|
||||
<Icon icon="mdi:chevron-down" class="text-sm ml-1" />
|
||||
</button>
|
||||
</div>
|
||||
<button class="px-3 py-1.5 rounded bg-[#3e4451] hover:bg-[#4a5064] text-[#c0c6d4] text-sm transition-colors flex items-center gap-2">
|
||||
<Icon icon="mdi:refresh" class="text-base" />
|
||||
Fetch origin
|
||||
</button>
|
||||
</div>
|
||||
<div class="text-xs text-[#6b7280]">
|
||||
Last fetched 9 months ago
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.repository-view {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
background-color: #1c2027;
|
||||
}
|
||||
</style>
|
||||
215
src/lib/components/WindowBar.svelte
Normal file
@@ -0,0 +1,215 @@
|
||||
<script lang="ts">
|
||||
import { getCurrentWindow } from "@tauri-apps/api/window";
|
||||
import { check } from "@tauri-apps/plugin-updater";
|
||||
import { relaunch } from "@tauri-apps/plugin-process";
|
||||
import Icon from "@iconify/svelte";
|
||||
|
||||
interface Props {
|
||||
baseUrl?: string;
|
||||
serverVersion?: string;
|
||||
onSignOut?: () => void;
|
||||
showMenus?: boolean;
|
||||
}
|
||||
|
||||
let {
|
||||
baseUrl = "",
|
||||
serverVersion = "",
|
||||
onSignOut,
|
||||
showMenus = false,
|
||||
}: Props = $props();
|
||||
|
||||
let isMaximized = $state(false);
|
||||
let updateAvailable = $state(false);
|
||||
let checkingUpdate = $state(false);
|
||||
|
||||
const appWindow = getCurrentWindow();
|
||||
|
||||
async function minimize() {
|
||||
await appWindow.minimize();
|
||||
}
|
||||
|
||||
async function toggleMaximize() {
|
||||
await appWindow.toggleMaximize();
|
||||
isMaximized = await appWindow.isMaximized();
|
||||
}
|
||||
|
||||
async function close() {
|
||||
await appWindow.close();
|
||||
}
|
||||
|
||||
async function checkForUpdates() {
|
||||
checkingUpdate = true;
|
||||
try {
|
||||
const update = await check();
|
||||
if (update?.available) {
|
||||
updateAvailable = true;
|
||||
const confirmed = confirm(
|
||||
`Update to ${update.version} is available!\n\nRelease notes: ${update.body}\n\nWould you like to install it now?`
|
||||
);
|
||||
if (confirmed) {
|
||||
await update.downloadAndInstall();
|
||||
await relaunch();
|
||||
}
|
||||
} else {
|
||||
alert("You are on the latest version!");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to check for updates:", error);
|
||||
alert(`Failed to check for updates: ${error}`);
|
||||
} finally {
|
||||
checkingUpdate = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Check maximized state on mount
|
||||
$effect(() => {
|
||||
appWindow.isMaximized().then((maximized) => {
|
||||
isMaximized = maximized;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="title-bar flex items-center justify-between bg-linear-to-r from-[#2a2e3a] to-[#23272f] border-b border-[#3e4451] select-none shadow-lg"
|
||||
data-tauri-drag-region
|
||||
>
|
||||
<div class="flex items-center gap-4 px-4 py-2 h-11">
|
||||
<div class="flex items-center gap-2.5">
|
||||
<div
|
||||
class="w-6 h-6 rounded-md flex items-center justify-center shadow-md"
|
||||
>
|
||||
<Icon icon="simple-icons:gitea" class="text-white text-base" />
|
||||
</div>
|
||||
<span class="font-bold text-base text-white tracking-tight"
|
||||
>Gitea Desktop</span
|
||||
>
|
||||
</div>
|
||||
|
||||
{#if showMenus}
|
||||
<div class="flex gap-1 text-xs ml-2">
|
||||
<button class="menu-item">File</button>
|
||||
<button class="menu-item">Edit</button>
|
||||
<button class="menu-item">View</button>
|
||||
<button class="menu-item">Repository</button>
|
||||
<button class="menu-item">Branch</button>
|
||||
<button class="menu-item">Help</button>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2 ml-4 text-xs">
|
||||
<div
|
||||
class="flex items-center gap-2 rounded-full bg-[#1c2027]/50 border border-[#3e4451]"
|
||||
>
|
||||
<Icon icon="mdi:server" class="text-[#84c5fb] mx-2.5 my-1 text-sm" />
|
||||
<span class="text-[#9399a8] truncate max-w-[200px] my-1">{baseUrl}</span>
|
||||
{#if serverVersion}
|
||||
<div
|
||||
class="px-2.5 py-1 rounded-full bg-linear-to-r from-[#4c9ac9]/20 to-[#84c5fb]/20 border border-[#4c9ac9]/30"
|
||||
>
|
||||
<span class="text-[#84c5fb] text-[10px] font-bold"
|
||||
>v{serverVersion}</span
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="flex items-center h-11">
|
||||
{#if showMenus && onSignOut}
|
||||
<button
|
||||
class="window-control hover:bg-[#3e4451] text-[#c0c6d4] hover:text-white"
|
||||
onclick={onSignOut}
|
||||
title="Sign Out"
|
||||
>
|
||||
<Icon icon="mdi:logout" class="text-base" />
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
<button
|
||||
class="window-control hover:bg-[#3e4451] text-[#c0c6d4] hover:text-white"
|
||||
onclick={checkForUpdates}
|
||||
disabled={checkingUpdate}
|
||||
title="Check for updates"
|
||||
>
|
||||
{#if checkingUpdate}
|
||||
<Icon icon="mdi:loading" class="animate-spin text-base" />
|
||||
{:else if updateAvailable}
|
||||
<Icon icon="mdi:download-circle" class="text-[#4c9ac9] text-base" />
|
||||
{:else}
|
||||
<Icon icon="mdi:update" class="text-base" />
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
<div class="flex items-center border-l border-[#3e4451]/50">
|
||||
<button
|
||||
class="window-control hover:bg-[#3e4451] text-[#c0c6d4] hover:text-white"
|
||||
onclick={minimize}
|
||||
title="Minimize"
|
||||
>
|
||||
<Icon icon="mdi:window-minimize" class="text-base" />
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="window-control hover:bg-[#3e4451] text-[#c0c6d4] hover:text-white"
|
||||
onclick={toggleMaximize}
|
||||
title={isMaximized ? "Restore" : "Maximize"}
|
||||
>
|
||||
{#if isMaximized}
|
||||
<Icon icon="mdi:window-restore" class="text-base" />
|
||||
{:else}
|
||||
<Icon icon="mdi:window-maximize" class="text-base" />
|
||||
{/if}
|
||||
</button>
|
||||
|
||||
<button
|
||||
class="window-control hover:bg-[#dc2626] hover:text-white text-[#c0c6d4]"
|
||||
onclick={close}
|
||||
title="Close"
|
||||
>
|
||||
<Icon icon="mdi:close" class="text-base" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.title-bar {
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
|
||||
.window-control {
|
||||
-webkit-app-region: no-drag;
|
||||
width: 46px;
|
||||
height: 44px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.window-control:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
-webkit-app-region: no-drag;
|
||||
padding: 0.375rem 0.75rem;
|
||||
border-radius: 0.375rem;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: #c0c6d4;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.menu-item:hover {
|
||||
background: #3e4451;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
6
src/lib/components/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export { default as WindowBar } from './WindowBar.svelte';
|
||||
export { default as TitleBar } from './WindowBar.svelte';
|
||||
export { default as RepositorySelector } from './RepositorySelector.svelte';
|
||||
export { default as RepositoryList } from './RepositoryList.svelte';
|
||||
export { default as RepositoryView } from './RepositoryView.svelte';
|
||||
export { default as EmptyState } from './EmptyState.svelte';
|
||||
5
src/lib/css/app.css
Normal file
@@ -0,0 +1,5 @@
|
||||
@import 'tailwindcss';
|
||||
|
||||
@import '@skeletonlabs/skeleton';
|
||||
@import '@skeletonlabs/skeleton-svelte';
|
||||
@import '@skeletonlabs/skeleton/themes/cerberus';
|
||||
34
src/lib/gitea/branch.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import type { AxiosInstance } from "axios";
|
||||
import type { Branch } from "./types";
|
||||
|
||||
export class BranchAPI {
|
||||
constructor(private client: AxiosInstance) {}
|
||||
|
||||
async listBranches(owner: string, repo: string): Promise<Branch[]> {
|
||||
const { data } = await this.client.get<Branch[]>(`/repos/${owner}/${repo}/branches`);
|
||||
return data;
|
||||
}
|
||||
|
||||
async getBranch(owner: string, repo: string, branch: string): Promise<Branch> {
|
||||
const { data } = await this.client.get<Branch>(
|
||||
`/repos/${owner}/${repo}/branches/${branch}`,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
async createBranch(
|
||||
owner: string,
|
||||
repo: string,
|
||||
payload: { new_branch_name: string; old_branch_name?: string; old_ref_name?: string },
|
||||
): Promise<Branch> {
|
||||
const { data } = await this.client.post<Branch>(
|
||||
`/repos/${owner}/${repo}/branches`,
|
||||
payload,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
async deleteBranch(owner: string, repo: string, branch: string): Promise<void> {
|
||||
await this.client.delete(`/repos/${owner}/${repo}/branches/${branch}`);
|
||||
}
|
||||
}
|
||||
199
src/lib/gitea/client.ts
Normal file
@@ -0,0 +1,199 @@
|
||||
import { fetch } from "@tauri-apps/plugin-http";
|
||||
|
||||
export type {
|
||||
User,
|
||||
Repository,
|
||||
Branch,
|
||||
Commit,
|
||||
Issue,
|
||||
PullRequest,
|
||||
Label,
|
||||
Milestone,
|
||||
Comment,
|
||||
Organization,
|
||||
Notification,
|
||||
Tag,
|
||||
Release,
|
||||
FileContent,
|
||||
} from "./types";
|
||||
|
||||
import type {
|
||||
User,
|
||||
Repository,
|
||||
Branch,
|
||||
Commit,
|
||||
Issue,
|
||||
PullRequest,
|
||||
Label,
|
||||
Milestone,
|
||||
Comment,
|
||||
Organization,
|
||||
Notification,
|
||||
Tag,
|
||||
Release,
|
||||
FileContent,
|
||||
} from "./types";
|
||||
|
||||
export class GiteaClient {
|
||||
private baseUrl: string;
|
||||
private token?: string;
|
||||
|
||||
constructor(baseUrl: string, token?: string) {
|
||||
this.baseUrl = baseUrl.replace(/\/$/, "");
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
private async request<T>(
|
||||
endpoint: string,
|
||||
options: RequestInit = {}
|
||||
): Promise<T> {
|
||||
const url = `${this.baseUrl}/api/v1${endpoint}`;
|
||||
const headers: Record<string, string> = {
|
||||
"Content-Type": "application/json",
|
||||
...((options.headers as Record<string, string>) || {}),
|
||||
};
|
||||
|
||||
if (this.token) {
|
||||
headers["Authorization"] = `token ${this.token}`;
|
||||
}
|
||||
|
||||
const response = await fetch(url, {
|
||||
...options,
|
||||
headers,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
throw new Error(
|
||||
`API Error (${response.status}): ${errorText || response.statusText}`
|
||||
);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
setToken(token: string) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
clearToken() {
|
||||
this.token = undefined;
|
||||
}
|
||||
|
||||
async getCurrentUser(): Promise<User> {
|
||||
return this.request<User>("/user");
|
||||
}
|
||||
|
||||
async searchUsers(query: string, limit = 10): Promise<User[]> {
|
||||
const data = await this.request<{ data: User[] }>(
|
||||
`/users/search?q=${encodeURIComponent(query)}&limit=${limit}`
|
||||
);
|
||||
return data.data || [];
|
||||
}
|
||||
|
||||
async getUser(username: string): Promise<User> {
|
||||
return this.request<User>(`/users/${username}`);
|
||||
}
|
||||
|
||||
async listUserRepos(): Promise<Repository[]> {
|
||||
return this.request<Repository[]>("/user/repos");
|
||||
}
|
||||
|
||||
async searchRepos(
|
||||
query: string,
|
||||
options?: { limit?: number; sort?: string; order?: string }
|
||||
): Promise<Repository[]> {
|
||||
const params = new URLSearchParams({ q: query });
|
||||
if (options?.limit) params.set("limit", String(options.limit));
|
||||
if (options?.sort) params.set("sort", options.sort);
|
||||
if (options?.order) params.set("order", options.order);
|
||||
|
||||
const data = await this.request<{ data: Repository[] }>(
|
||||
`/repos/search?${params}`
|
||||
);
|
||||
return data.data || [];
|
||||
}
|
||||
|
||||
async getRepo(owner: string, repo: string): Promise<Repository> {
|
||||
return this.request<Repository>(`/repos/${owner}/${repo}`);
|
||||
}
|
||||
|
||||
async createRepo(payload: {
|
||||
name: string;
|
||||
description?: string;
|
||||
private?: boolean;
|
||||
auto_init?: boolean;
|
||||
gitignores?: string;
|
||||
license?: string;
|
||||
readme?: string;
|
||||
default_branch?: string;
|
||||
}): Promise<Repository> {
|
||||
return this.request<Repository>("/user/repos", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
}
|
||||
|
||||
async getVersion(): Promise<{ version: string }> {
|
||||
return this.request<{ version: string }>("/version");
|
||||
}
|
||||
|
||||
async listBranches(owner: string, repo: string): Promise<Branch[]> {
|
||||
return this.request<Branch[]>(`/repos/${owner}/${repo}/branches`);
|
||||
}
|
||||
|
||||
async listCommits(
|
||||
owner: string,
|
||||
repo: string,
|
||||
options?: { sha?: string; path?: string; page?: number; limit?: number }
|
||||
): Promise<Commit[]> {
|
||||
const params = new URLSearchParams();
|
||||
if (options?.sha) params.set("sha", options.sha);
|
||||
if (options?.path) params.set("path", options.path);
|
||||
if (options?.page) params.set("page", String(options.page));
|
||||
if (options?.limit) params.set("limit", String(options.limit));
|
||||
|
||||
const query = params.toString();
|
||||
return this.request<Commit[]>(
|
||||
`/repos/${owner}/${repo}/commits${query ? `?${query}` : ""}`
|
||||
);
|
||||
}
|
||||
|
||||
async listIssues(
|
||||
owner: string,
|
||||
repo: string,
|
||||
options?: {
|
||||
state?: "open" | "closed" | "all";
|
||||
labels?: string;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}
|
||||
): Promise<Issue[]> {
|
||||
const params = new URLSearchParams();
|
||||
if (options?.state) params.set("state", options.state);
|
||||
if (options?.labels) params.set("labels", options.labels);
|
||||
if (options?.page) params.set("page", String(options.page));
|
||||
if (options?.limit) params.set("limit", String(options.limit));
|
||||
|
||||
const query = params.toString();
|
||||
return this.request<Issue[]>(
|
||||
`/repos/${owner}/${repo}/issues${query ? `?${query}` : ""}`
|
||||
);
|
||||
}
|
||||
|
||||
async listPullRequests(
|
||||
owner: string,
|
||||
repo: string,
|
||||
options?: { state?: "open" | "closed" | "all"; page?: number; limit?: number }
|
||||
): Promise<PullRequest[]> {
|
||||
const params = new URLSearchParams();
|
||||
if (options?.state) params.set("state", options.state);
|
||||
if (options?.page) params.set("page", String(options.page));
|
||||
if (options?.limit) params.set("limit", String(options.limit));
|
||||
|
||||
const query = params.toString();
|
||||
return this.request<PullRequest[]>(
|
||||
`/repos/${owner}/${repo}/pulls${query ? `?${query}` : ""}`
|
||||
);
|
||||
}
|
||||
}
|
||||
33
src/lib/gitea/commit.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import type { AxiosInstance } from "axios";
|
||||
import type { Commit } from "./types";
|
||||
|
||||
export class CommitAPI {
|
||||
constructor(private client: AxiosInstance) {}
|
||||
|
||||
async listCommits(
|
||||
owner: string,
|
||||
repo: string,
|
||||
options?: { sha?: string; path?: string; page?: number; limit?: number },
|
||||
): Promise<Commit[]> {
|
||||
const { data } = await this.client.get<Commit[]>(`/repos/${owner}/${repo}/commits`, {
|
||||
params: options,
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
async getCommit(owner: string, repo: string, sha: string): Promise<Commit> {
|
||||
const { data } = await this.client.get<Commit>(
|
||||
`/repos/${owner}/${repo}/git/commits/${sha}`,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
async compareCommits(
|
||||
owner: string,
|
||||
repo: string,
|
||||
basehead: string,
|
||||
): Promise<{ commits: Commit[] }> {
|
||||
const { data } = await this.client.get(`/repos/${owner}/${repo}/compare/${basehead}`);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
29
src/lib/gitea/file.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import type { AxiosInstance } from "axios";
|
||||
import type { FileContent } from "./types";
|
||||
|
||||
export class FileAPI {
|
||||
constructor(private client: AxiosInstance) {}
|
||||
|
||||
async getContents(
|
||||
owner: string,
|
||||
repo: string,
|
||||
filepath: string,
|
||||
ref?: string,
|
||||
): Promise<FileContent | FileContent[]> {
|
||||
const { data } = await this.client.get<FileContent | FileContent[]>(
|
||||
`/repos/${owner}/${repo}/contents/${filepath}`,
|
||||
{ params: { ref } },
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
async getRawFile(owner: string, repo: string, filepath: string): Promise<string> {
|
||||
const { data } = await this.client.get<string>(
|
||||
`/repos/${owner}/${repo}/raw/${filepath}`,
|
||||
{
|
||||
responseType: "text",
|
||||
},
|
||||
);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
78
src/lib/gitea/gitea.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import axios, { type AxiosInstance } from "axios";
|
||||
import { UserAPI } from "./user";
|
||||
import { RepositoryAPI } from "./repository";
|
||||
import { BranchAPI } from "./branch";
|
||||
import { CommitAPI } from "./commit";
|
||||
import { IssueAPI } from "./issue";
|
||||
import { PullRequestAPI } from "./pull-request";
|
||||
import { FileAPI } from "./file";
|
||||
import { ReleaseAPI } from "./release";
|
||||
import { OrganizationAPI } from "./organization";
|
||||
import { NotificationAPI } from "./notification";
|
||||
import { MiscAPI } from "./misc";
|
||||
|
||||
export * from "./types";
|
||||
|
||||
export class GiteaClient {
|
||||
private client: AxiosInstance;
|
||||
public baseUrl: string;
|
||||
|
||||
public user: UserAPI;
|
||||
public repository: RepositoryAPI;
|
||||
public branch: BranchAPI;
|
||||
public commit: CommitAPI;
|
||||
public issue: IssueAPI;
|
||||
public pullRequest: PullRequestAPI;
|
||||
public file: FileAPI;
|
||||
public release: ReleaseAPI;
|
||||
public organization: OrganizationAPI;
|
||||
public notification: NotificationAPI;
|
||||
public misc: MiscAPI;
|
||||
|
||||
constructor(baseUrl: string, token?: string) {
|
||||
this.baseUrl = baseUrl.replace(/\/$/, "");
|
||||
|
||||
this.client = axios.create({
|
||||
baseURL: `${this.baseUrl}/api/v1`,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(token ? { Authorization: `token ${token}` } : {}),
|
||||
},
|
||||
timeout: 30000,
|
||||
});
|
||||
|
||||
this.client.interceptors.response.use(
|
||||
(response) => response,
|
||||
(error) => {
|
||||
const message =
|
||||
error.response?.data?.message ||
|
||||
error.response?.statusText ||
|
||||
error.message ||
|
||||
"Unknown error";
|
||||
throw new Error(`API Error (${error.response?.status || "network"}): ${message}`);
|
||||
},
|
||||
);
|
||||
|
||||
this.user = new UserAPI(this.client);
|
||||
this.repository = new RepositoryAPI(this.client);
|
||||
this.branch = new BranchAPI(this.client);
|
||||
this.commit = new CommitAPI(this.client);
|
||||
this.issue = new IssueAPI(this.client);
|
||||
this.pullRequest = new PullRequestAPI(this.client);
|
||||
this.file = new FileAPI(this.client);
|
||||
this.release = new ReleaseAPI(this.client);
|
||||
this.organization = new OrganizationAPI(this.client);
|
||||
this.notification = new NotificationAPI(this.client);
|
||||
this.misc = new MiscAPI(this.client);
|
||||
}
|
||||
|
||||
setToken(token: string) {
|
||||
this.client.defaults.headers.common["Authorization"] = `token ${token}`;
|
||||
}
|
||||
|
||||
clearToken() {
|
||||
delete this.client.defaults.headers.common["Authorization"];
|
||||
}
|
||||
}
|
||||
|
||||
export default GiteaClient;
|
||||
102
src/lib/gitea/issue.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import type { AxiosInstance } from "axios";
|
||||
import type { Issue, Comment, Label, Milestone } from "./types";
|
||||
|
||||
export class IssueAPI {
|
||||
constructor(private client: AxiosInstance) {}
|
||||
|
||||
async listIssues(
|
||||
owner: string,
|
||||
repo: string,
|
||||
options?: {
|
||||
state?: "open" | "closed" | "all";
|
||||
labels?: string;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
},
|
||||
): Promise<Issue[]> {
|
||||
const { data } = await this.client.get<Issue[]>(`/repos/${owner}/${repo}/issues`, {
|
||||
params: options,
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
async getIssue(owner: string, repo: string, index: number): Promise<Issue> {
|
||||
const { data } = await this.client.get<Issue>(`/repos/${owner}/${repo}/issues/${index}`);
|
||||
return data;
|
||||
}
|
||||
|
||||
async createIssue(
|
||||
owner: string,
|
||||
repo: string,
|
||||
payload: {
|
||||
title: string;
|
||||
body?: string;
|
||||
assignees?: string[];
|
||||
labels?: number[];
|
||||
milestone?: number;
|
||||
},
|
||||
): Promise<Issue> {
|
||||
const { data } = await this.client.post<Issue>(
|
||||
`/repos/${owner}/${repo}/issues`,
|
||||
payload,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
async editIssue(
|
||||
owner: string,
|
||||
repo: string,
|
||||
index: number,
|
||||
payload: Partial<{
|
||||
title: string;
|
||||
body: string;
|
||||
state: "open" | "closed";
|
||||
assignees: string[];
|
||||
labels: number[];
|
||||
milestone: number;
|
||||
}>,
|
||||
): Promise<Issue> {
|
||||
const { data } = await this.client.patch<Issue>(
|
||||
`/repos/${owner}/${repo}/issues/${index}`,
|
||||
payload,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
async listIssueComments(owner: string, repo: string, index: number): Promise<Comment[]> {
|
||||
const { data } = await this.client.get<Comment[]>(
|
||||
`/repos/${owner}/${repo}/issues/${index}/comments`,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
async addIssueComment(
|
||||
owner: string,
|
||||
repo: string,
|
||||
index: number,
|
||||
body: string,
|
||||
): Promise<Comment> {
|
||||
const { data } = await this.client.post<Comment>(
|
||||
`/repos/${owner}/${repo}/issues/${index}/comments`,
|
||||
{ body },
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
async listLabels(owner: string, repo: string): Promise<Label[]> {
|
||||
const { data } = await this.client.get<Label[]>(`/repos/${owner}/${repo}/labels`);
|
||||
return data;
|
||||
}
|
||||
|
||||
async listMilestones(
|
||||
owner: string,
|
||||
repo: string,
|
||||
state?: "open" | "closed" | "all",
|
||||
): Promise<Milestone[]> {
|
||||
const { data } = await this.client.get<Milestone[]>(
|
||||
`/repos/${owner}/${repo}/milestones`,
|
||||
{ params: { state } },
|
||||
);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
19
src/lib/gitea/misc.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import type { AxiosInstance } from "axios";
|
||||
|
||||
export class MiscAPI {
|
||||
constructor(private client: AxiosInstance) {}
|
||||
|
||||
async getVersion(): Promise<{ version: string }> {
|
||||
const { data } = await this.client.get<{ version: string }>("/version");
|
||||
return data;
|
||||
}
|
||||
|
||||
async renderMarkdown(text: string, mode?: "gfm" | "markdown"): Promise<string> {
|
||||
const { data } = await this.client.post<string>(
|
||||
"/markdown",
|
||||
{ Text: text, Mode: mode || "gfm" },
|
||||
{ responseType: "text" },
|
||||
);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
32
src/lib/gitea/notification.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import type { AxiosInstance } from "axios";
|
||||
import type { Notification } from "./types";
|
||||
|
||||
export class NotificationAPI {
|
||||
constructor(private client: AxiosInstance) {}
|
||||
|
||||
async listNotifications(options?: {
|
||||
all?: boolean;
|
||||
page?: number;
|
||||
limit?: number;
|
||||
}): Promise<Notification[]> {
|
||||
const { data } = await this.client.get<Notification[]>("/notifications", {
|
||||
params: options,
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
async markNotificationsRead(lastReadAt?: string): Promise<void> {
|
||||
await this.client.put("/notifications", {
|
||||
last_read_at: lastReadAt || new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
async getNotificationThread(id: string): Promise<Notification> {
|
||||
const { data } = await this.client.get<Notification>(`/notifications/threads/${id}`);
|
||||
return data;
|
||||
}
|
||||
|
||||
async markNotificationThreadRead(id: string): Promise<void> {
|
||||
await this.client.patch(`/notifications/threads/${id}`);
|
||||
}
|
||||
}
|
||||
26
src/lib/gitea/organization.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import type { AxiosInstance } from "axios";
|
||||
import type { Organization, Repository, User } from "./types";
|
||||
|
||||
export class OrganizationAPI {
|
||||
constructor(private client: AxiosInstance) {}
|
||||
|
||||
async listUserOrgs(): Promise<Organization[]> {
|
||||
const { data } = await this.client.get<Organization[]>("/user/orgs");
|
||||
return data;
|
||||
}
|
||||
|
||||
async getOrg(org: string): Promise<Organization> {
|
||||
const { data } = await this.client.get<Organization>(`/orgs/${org}`);
|
||||
return data;
|
||||
}
|
||||
|
||||
async listOrgRepos(org: string): Promise<Repository[]> {
|
||||
const { data } = await this.client.get<Repository[]>(`/orgs/${org}/repos`);
|
||||
return data;
|
||||
}
|
||||
|
||||
async listOrgMembers(org: string): Promise<User[]> {
|
||||
const { data } = await this.client.get<User[]>(`/orgs/${org}/members`);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
108
src/lib/gitea/pull-request.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import type { AxiosInstance } from "axios";
|
||||
import type { PullRequest, Commit, FileContent } from "./types";
|
||||
|
||||
export class PullRequestAPI {
|
||||
constructor(private client: AxiosInstance) {}
|
||||
|
||||
async listPullRequests(
|
||||
owner: string,
|
||||
repo: string,
|
||||
options?: { state?: "open" | "closed" | "all"; page?: number; limit?: number },
|
||||
): Promise<PullRequest[]> {
|
||||
const { data } = await this.client.get<PullRequest[]>(
|
||||
`/repos/${owner}/${repo}/pulls`,
|
||||
{ params: options },
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
async getPullRequest(owner: string, repo: string, index: number): Promise<PullRequest> {
|
||||
const { data } = await this.client.get<PullRequest>(
|
||||
`/repos/${owner}/${repo}/pulls/${index}`,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
async createPullRequest(
|
||||
owner: string,
|
||||
repo: string,
|
||||
payload: {
|
||||
title: string;
|
||||
body?: string;
|
||||
head: string;
|
||||
base: string;
|
||||
assignees?: string[];
|
||||
labels?: number[];
|
||||
milestone?: number;
|
||||
},
|
||||
): Promise<PullRequest> {
|
||||
const { data } = await this.client.post<PullRequest>(
|
||||
`/repos/${owner}/${repo}/pulls`,
|
||||
payload,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
async editPullRequest(
|
||||
owner: string,
|
||||
repo: string,
|
||||
index: number,
|
||||
payload: Partial<{
|
||||
title: string;
|
||||
body: string;
|
||||
state: "open" | "closed";
|
||||
base: string;
|
||||
assignees: string[];
|
||||
labels: number[];
|
||||
milestone: number;
|
||||
}>,
|
||||
): Promise<PullRequest> {
|
||||
const { data } = await this.client.patch<PullRequest>(
|
||||
`/repos/${owner}/${repo}/pulls/${index}`,
|
||||
payload,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
async mergePullRequest(
|
||||
owner: string,
|
||||
repo: string,
|
||||
index: number,
|
||||
method?: "merge" | "rebase" | "squash",
|
||||
): Promise<void> {
|
||||
await this.client.post(`/repos/${owner}/${repo}/pulls/${index}/merge`, {
|
||||
Do: method || "merge",
|
||||
});
|
||||
}
|
||||
|
||||
async isPullRequestMerged(owner: string, repo: string, index: number): Promise<boolean> {
|
||||
try {
|
||||
await this.client.get(`/repos/${owner}/${repo}/pulls/${index}/merge`);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async listPullRequestCommits(
|
||||
owner: string,
|
||||
repo: string,
|
||||
index: number,
|
||||
): Promise<Commit[]> {
|
||||
const { data } = await this.client.get<Commit[]>(
|
||||
`/repos/${owner}/${repo}/pulls/${index}/commits`,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
async listPullRequestFiles(
|
||||
owner: string,
|
||||
repo: string,
|
||||
index: number,
|
||||
): Promise<FileContent[]> {
|
||||
const { data } = await this.client.get<FileContent[]>(
|
||||
`/repos/${owner}/${repo}/pulls/${index}/files`,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
49
src/lib/gitea/release.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import type { AxiosInstance } from "axios";
|
||||
import type { Tag, Release } from "./types";
|
||||
|
||||
export class ReleaseAPI {
|
||||
constructor(private client: AxiosInstance) {}
|
||||
|
||||
async listTags(owner: string, repo: string): Promise<Tag[]> {
|
||||
const { data } = await this.client.get<Tag[]>(`/repos/${owner}/${repo}/tags`);
|
||||
return data;
|
||||
}
|
||||
|
||||
async listReleases(owner: string, repo: string): Promise<Release[]> {
|
||||
const { data } = await this.client.get<Release[]>(`/repos/${owner}/${repo}/releases`);
|
||||
return data;
|
||||
}
|
||||
|
||||
async getLatestRelease(owner: string, repo: string): Promise<Release> {
|
||||
const { data } = await this.client.get<Release>(
|
||||
`/repos/${owner}/${repo}/releases/latest`,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
async getReleaseByTag(owner: string, repo: string, tag: string): Promise<Release> {
|
||||
const { data } = await this.client.get<Release>(
|
||||
`/repos/${owner}/${repo}/releases/tags/${tag}`,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
async createRelease(
|
||||
owner: string,
|
||||
repo: string,
|
||||
payload: {
|
||||
tag_name: string;
|
||||
target_commitish?: string;
|
||||
name?: string;
|
||||
body?: string;
|
||||
draft?: boolean;
|
||||
prerelease?: boolean;
|
||||
},
|
||||
): Promise<Release> {
|
||||
const { data } = await this.client.post<Release>(
|
||||
`/repos/${owner}/${repo}/releases`,
|
||||
payload,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
102
src/lib/gitea/repository.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
import type { AxiosInstance } from "axios";
|
||||
import type { Repository } from "./types";
|
||||
|
||||
export class RepositoryAPI {
|
||||
constructor(private client: AxiosInstance) {}
|
||||
|
||||
async listUserRepos(): Promise<Repository[]> {
|
||||
const { data } = await this.client.get<Repository[]>("/user/repos");
|
||||
return data;
|
||||
}
|
||||
|
||||
async searchRepos(
|
||||
query: string,
|
||||
options?: { limit?: number; sort?: string; order?: string },
|
||||
): Promise<Repository[]> {
|
||||
const { data } = await this.client.get<{ data: Repository[] }>("/repos/search", {
|
||||
params: { q: query, ...options },
|
||||
});
|
||||
return data.data || [];
|
||||
}
|
||||
|
||||
async getRepo(owner: string, repo: string): Promise<Repository> {
|
||||
const { data } = await this.client.get<Repository>(`/repos/${owner}/${repo}`);
|
||||
return data;
|
||||
}
|
||||
|
||||
async createRepo(payload: {
|
||||
name: string;
|
||||
description?: string;
|
||||
private?: boolean;
|
||||
auto_init?: boolean;
|
||||
gitignores?: string;
|
||||
license?: string;
|
||||
readme?: string;
|
||||
default_branch?: string;
|
||||
}): Promise<Repository> {
|
||||
const { data } = await this.client.post<Repository>("/user/repos", payload);
|
||||
return data;
|
||||
}
|
||||
|
||||
async editRepo(
|
||||
owner: string,
|
||||
repo: string,
|
||||
payload: Partial<{
|
||||
name: string;
|
||||
description: string;
|
||||
private: boolean;
|
||||
default_branch: string;
|
||||
has_issues: boolean;
|
||||
has_wiki: boolean;
|
||||
has_pull_requests: boolean;
|
||||
archived: boolean;
|
||||
}>,
|
||||
): Promise<Repository> {
|
||||
const { data } = await this.client.patch<Repository>(
|
||||
`/repos/${owner}/${repo}`,
|
||||
payload,
|
||||
);
|
||||
return data;
|
||||
}
|
||||
|
||||
async deleteRepo(owner: string, repo: string): Promise<void> {
|
||||
await this.client.delete(`/repos/${owner}/${repo}`);
|
||||
}
|
||||
|
||||
async forkRepo(owner: string, repo: string, organization?: string): Promise<Repository> {
|
||||
const { data } = await this.client.post<Repository>(`/repos/${owner}/${repo}/forks`, {
|
||||
organization,
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
async listForks(owner: string, repo: string): Promise<Repository[]> {
|
||||
const { data } = await this.client.get<Repository[]>(`/repos/${owner}/${repo}/forks`);
|
||||
return data;
|
||||
}
|
||||
|
||||
async starRepo(owner: string, repo: string): Promise<void> {
|
||||
await this.client.put(`/user/starred/${owner}/${repo}`);
|
||||
}
|
||||
|
||||
async unstarRepo(owner: string, repo: string): Promise<void> {
|
||||
await this.client.delete(`/user/starred/${owner}/${repo}`);
|
||||
}
|
||||
|
||||
async isRepoStarred(owner: string, repo: string): Promise<boolean> {
|
||||
try {
|
||||
await this.client.get(`/user/starred/${owner}/${repo}`);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async watchRepo(owner: string, repo: string): Promise<void> {
|
||||
await this.client.put(`/repos/${owner}/${repo}/subscription`);
|
||||
}
|
||||
|
||||
async unwatchRepo(owner: string, repo: string): Promise<void> {
|
||||
await this.client.delete(`/repos/${owner}/${repo}/subscription`);
|
||||
}
|
||||
}
|
||||
226
src/lib/gitea/types.ts
Normal file
@@ -0,0 +1,226 @@
|
||||
export interface User {
|
||||
id: number;
|
||||
login: string;
|
||||
full_name?: string;
|
||||
email?: string;
|
||||
avatar_url?: string;
|
||||
username?: string;
|
||||
is_admin?: boolean;
|
||||
created?: string;
|
||||
restricted?: boolean;
|
||||
}
|
||||
|
||||
export interface Repository {
|
||||
id: number;
|
||||
owner: User;
|
||||
name: string;
|
||||
full_name: string;
|
||||
description?: string;
|
||||
private?: boolean;
|
||||
fork?: boolean;
|
||||
html_url?: string;
|
||||
clone_url?: string;
|
||||
ssh_url?: string;
|
||||
default_branch?: string;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
stars_count?: number;
|
||||
forks_count?: number;
|
||||
open_issues_count?: number;
|
||||
watchers_count?: number;
|
||||
size?: number;
|
||||
archived?: boolean;
|
||||
empty?: boolean;
|
||||
mirror?: boolean;
|
||||
template?: boolean;
|
||||
has_issues?: boolean;
|
||||
has_wiki?: boolean;
|
||||
has_pull_requests?: boolean;
|
||||
}
|
||||
|
||||
export interface Branch {
|
||||
name: string;
|
||||
commit?: Commit;
|
||||
protected?: boolean;
|
||||
effective_branch_protection_name?: string;
|
||||
}
|
||||
|
||||
export interface Commit {
|
||||
id?: string;
|
||||
sha?: string;
|
||||
url?: string;
|
||||
author?: CommitUser;
|
||||
committer?: CommitUser;
|
||||
message?: string;
|
||||
created?: string;
|
||||
html_url?: string;
|
||||
tree?: {
|
||||
sha?: string;
|
||||
url?: string;
|
||||
};
|
||||
parents?: Array<{ sha?: string; url?: string }>;
|
||||
}
|
||||
|
||||
export interface CommitUser {
|
||||
name?: string;
|
||||
email?: string;
|
||||
date?: string;
|
||||
}
|
||||
|
||||
export interface Issue {
|
||||
id: number;
|
||||
number: number;
|
||||
user: User;
|
||||
title: string;
|
||||
body?: string;
|
||||
state?: "open" | "closed";
|
||||
labels?: Label[];
|
||||
milestone?: Milestone;
|
||||
assignees?: User[];
|
||||
comments?: number;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
closed_at?: string;
|
||||
due_date?: string;
|
||||
html_url?: string;
|
||||
pull_request?: { merged?: boolean };
|
||||
}
|
||||
|
||||
export interface Label {
|
||||
id: number;
|
||||
name: string;
|
||||
color: string;
|
||||
description?: string;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export interface Milestone {
|
||||
id: number;
|
||||
title: string;
|
||||
description?: string;
|
||||
state?: "open" | "closed";
|
||||
open_issues?: number;
|
||||
closed_issues?: number;
|
||||
due_on?: string;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
closed_at?: string;
|
||||
}
|
||||
|
||||
export interface PullRequest {
|
||||
id: number;
|
||||
number: number;
|
||||
user: User;
|
||||
title: string;
|
||||
body?: string;
|
||||
state?: "open" | "closed";
|
||||
head?: PRBranchInfo;
|
||||
base?: PRBranchInfo;
|
||||
mergeable?: boolean;
|
||||
merged?: boolean;
|
||||
merged_at?: string;
|
||||
merge_commit_sha?: string;
|
||||
labels?: Label[];
|
||||
milestone?: Milestone;
|
||||
assignees?: User[];
|
||||
comments?: number;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
closed_at?: string;
|
||||
html_url?: string;
|
||||
diff_url?: string;
|
||||
patch_url?: string;
|
||||
}
|
||||
|
||||
export interface PRBranchInfo {
|
||||
label?: string;
|
||||
ref?: string;
|
||||
sha?: string;
|
||||
repo?: Repository;
|
||||
}
|
||||
|
||||
export interface Comment {
|
||||
id: number;
|
||||
user: User;
|
||||
body?: string;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
html_url?: string;
|
||||
}
|
||||
|
||||
export interface Organization {
|
||||
id: number;
|
||||
username: string;
|
||||
full_name?: string;
|
||||
avatar_url?: string;
|
||||
description?: string;
|
||||
website?: string;
|
||||
location?: string;
|
||||
visibility?: string;
|
||||
repo_admin_change_team_access?: boolean;
|
||||
}
|
||||
|
||||
export interface Notification {
|
||||
id: number;
|
||||
unread?: boolean;
|
||||
pinned?: boolean;
|
||||
subject?: {
|
||||
title?: string;
|
||||
url?: string;
|
||||
type?: string;
|
||||
state?: string;
|
||||
};
|
||||
repository?: Repository;
|
||||
updated_at?: string;
|
||||
url?: string;
|
||||
}
|
||||
|
||||
export interface Tag {
|
||||
name: string;
|
||||
commit?: {
|
||||
sha?: string;
|
||||
url?: string;
|
||||
};
|
||||
zipball_url?: string;
|
||||
tarball_url?: string;
|
||||
}
|
||||
|
||||
export interface Release {
|
||||
id: number;
|
||||
tag_name: string;
|
||||
target_commitish?: string;
|
||||
name?: string;
|
||||
body?: string;
|
||||
draft?: boolean;
|
||||
prerelease?: boolean;
|
||||
created_at?: string;
|
||||
published_at?: string;
|
||||
author?: User;
|
||||
assets?: ReleaseAsset[];
|
||||
html_url?: string;
|
||||
tarball_url?: string;
|
||||
zipball_url?: string;
|
||||
}
|
||||
|
||||
export interface ReleaseAsset {
|
||||
id: number;
|
||||
name: string;
|
||||
size?: number;
|
||||
download_count?: number;
|
||||
created_at?: string;
|
||||
browser_download_url?: string;
|
||||
}
|
||||
|
||||
export interface FileContent {
|
||||
type?: "file" | "dir" | "symlink" | "submodule";
|
||||
encoding?: string;
|
||||
size?: number;
|
||||
name?: string;
|
||||
path?: string;
|
||||
content?: string;
|
||||
sha?: string;
|
||||
url?: string;
|
||||
git_url?: string;
|
||||
html_url?: string;
|
||||
download_url?: string;
|
||||
}
|
||||
41
src/lib/gitea/user.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import type { AxiosInstance } from "axios";
|
||||
import type { User } from "./types";
|
||||
|
||||
export class UserAPI {
|
||||
constructor(private client: AxiosInstance) {}
|
||||
|
||||
async getCurrentUser(): Promise<User> {
|
||||
const { data } = await this.client.get<User>("/user");
|
||||
return data;
|
||||
}
|
||||
|
||||
async searchUsers(query: string, limit = 10): Promise<User[]> {
|
||||
const { data } = await this.client.get<{ data: User[] }>("/users/search", {
|
||||
params: { q: query, limit },
|
||||
});
|
||||
return data.data || [];
|
||||
}
|
||||
|
||||
async getUser(username: string): Promise<User> {
|
||||
const { data } = await this.client.get<User>(`/users/${username}`);
|
||||
return data;
|
||||
}
|
||||
|
||||
async getUserFollowers(): Promise<User[]> {
|
||||
const { data } = await this.client.get<User[]>("/user/followers");
|
||||
return data;
|
||||
}
|
||||
|
||||
async getUserFollowing(): Promise<User[]> {
|
||||
const { data } = await this.client.get<User[]>("/user/following");
|
||||
return data;
|
||||
}
|
||||
|
||||
async followUser(username: string): Promise<void> {
|
||||
await this.client.put(`/user/following/${username}`);
|
||||
}
|
||||
|
||||
async unfollowUser(username: string): Promise<void> {
|
||||
await this.client.delete(`/user/following/${username}`);
|
||||
}
|
||||
}
|
||||
14
src/lib/stores/auth.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export type AuthState = {
|
||||
token?: string;
|
||||
baseUrl?: string;
|
||||
};
|
||||
|
||||
export const auth = writable<AuthState>({});
|
||||
|
||||
export function setAuth(token?: string, baseUrl?: string) {
|
||||
auth.set({ token, baseUrl });
|
||||
}
|
||||
|
||||
export default auth;
|
||||
15
src/lib/stores/titleBar.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export interface TitleBarState {
|
||||
baseUrl: string;
|
||||
serverVersion: string;
|
||||
showMenus: boolean;
|
||||
onSignOut?: () => void;
|
||||
}
|
||||
|
||||
export const titleBarStore = writable<TitleBarState>({
|
||||
baseUrl: "",
|
||||
serverVersion: "",
|
||||
showMenus: false,
|
||||
onSignOut: undefined
|
||||
});
|
||||
37
src/routes/+layout.svelte
Normal file
@@ -0,0 +1,37 @@
|
||||
<script lang="ts">
|
||||
import "$lib/css/app.css";
|
||||
import WindowBar from "$lib/components/WindowBar.svelte";
|
||||
import { titleBarStore } from "$lib/stores/titleBar";
|
||||
|
||||
interface Props {
|
||||
children: any;
|
||||
}
|
||||
|
||||
let { children }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="app-container">
|
||||
<WindowBar
|
||||
baseUrl={$titleBarStore.baseUrl}
|
||||
serverVersion={$titleBarStore.serverVersion}
|
||||
showMenus={$titleBarStore.showMenus}
|
||||
onSignOut={$titleBarStore.onSignOut}
|
||||
/>
|
||||
{@render children()}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.app-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:global(.app-container > :last-child) {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,3 @@
|
||||
// Tauri doesn't have a Node.js server to do proper SSR
|
||||
// so we use adapter-static with a fallback to index.html to put the site in SPA mode
|
||||
// See: https://svelte.dev/docs/kit/single-page-apps
|
||||
// See: https://v2.tauri.app/start/frontend/sveltekit/ for more info
|
||||
export const ssr = false;
|
||||
|
||||
|
||||
|
||||
@@ -1,156 +1,203 @@
|
||||
<script lang="ts">
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { onMount } from "svelte";
|
||||
import { goto } from "$app/navigation";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { GiteaClient } from "$lib/gitea/client";
|
||||
import type { Repository } from "$lib/gitea/types";
|
||||
import { setAuth } from "$lib/stores/auth";
|
||||
import { titleBarStore } from "$lib/stores/titleBar";
|
||||
import Icon from "@iconify/svelte";
|
||||
import {
|
||||
RepositorySelector,
|
||||
RepositoryList,
|
||||
RepositoryView,
|
||||
EmptyState
|
||||
} from "$lib/components";
|
||||
|
||||
let name = $state("");
|
||||
let greetMsg = $state("");
|
||||
let baseUrl = $state("");
|
||||
let token = $state("");
|
||||
let loading = $state(false);
|
||||
let error = $state("");
|
||||
let isSignedIn = $state(false);
|
||||
let repos: Repository[] = $state([]);
|
||||
let serverVersion = $state("");
|
||||
let checkingAuth = $state(true);
|
||||
let selectedRepo: Repository | null = $state(null);
|
||||
|
||||
// Update title bar store reactively
|
||||
$effect(() => {
|
||||
titleBarStore.set({
|
||||
baseUrl,
|
||||
serverVersion,
|
||||
showMenus: isSignedIn,
|
||||
onSignOut: handleSignOut
|
||||
});
|
||||
});
|
||||
|
||||
async function greet(event: Event) {
|
||||
event.preventDefault();
|
||||
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
||||
greetMsg = await invoke("greet", { name });
|
||||
}
|
||||
async function handleSignIn(e: Event) {
|
||||
e.preventDefault();
|
||||
error = "";
|
||||
|
||||
if (!baseUrl.trim()) {
|
||||
error = "Please enter your Gitea server URL";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!token.trim()) {
|
||||
error = "Please enter your personal access token";
|
||||
return;
|
||||
}
|
||||
|
||||
loading = true;
|
||||
try {
|
||||
const normalizedUrl = baseUrl.trim().replace(/\/$/, "");
|
||||
const client = new GiteaClient(normalizedUrl, token.trim());
|
||||
|
||||
const user = await client.getCurrentUser();
|
||||
const version = await client.getVersion();
|
||||
serverVersion = version.version;
|
||||
|
||||
await invoke("store_token", { token: token.trim() });
|
||||
await invoke("store_base_url", { baseUrl: normalizedUrl });
|
||||
setAuth(token.trim(), normalizedUrl);
|
||||
|
||||
isSignedIn = true;
|
||||
await loadRepositories(client);
|
||||
} catch (e) {
|
||||
error = `Failed to sign in: ${String(e)}`;
|
||||
isSignedIn = false;
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function loadRepositories(client?: GiteaClient) {
|
||||
if (!client && (!baseUrl || !token)) return;
|
||||
|
||||
loading = true;
|
||||
error = "";
|
||||
try {
|
||||
const giteaClient = client || new GiteaClient(baseUrl, token);
|
||||
repos = await giteaClient.listUserRepos();
|
||||
} catch (e) {
|
||||
error = `Failed to load repositories: ${String(e)}`;
|
||||
repos = [];
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSignOut() {
|
||||
try {
|
||||
await invoke("store_token", { token: "" });
|
||||
await invoke("store_base_url", { baseUrl: "" });
|
||||
token = "";
|
||||
baseUrl = "";
|
||||
isSignedIn = false;
|
||||
repos = [];
|
||||
serverVersion = "";
|
||||
error = "";
|
||||
goto("/oauth");
|
||||
} catch (e) {
|
||||
error = `Failed to sign out: ${String(e)}`;
|
||||
}
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
const existingToken: string = await invoke("get_token");
|
||||
const existingBaseUrl: string = await invoke("get_base_url");
|
||||
|
||||
if (!existingToken || !existingBaseUrl) {
|
||||
checkingAuth = false;
|
||||
goto("/oauth");
|
||||
return;
|
||||
}
|
||||
|
||||
token = existingToken;
|
||||
baseUrl = existingBaseUrl;
|
||||
setAuth(token, baseUrl);
|
||||
|
||||
const client = new GiteaClient(baseUrl, token);
|
||||
await client.getCurrentUser();
|
||||
const version = await client.getVersion();
|
||||
serverVersion = version.version;
|
||||
|
||||
isSignedIn = true;
|
||||
await loadRepositories(client);
|
||||
checkingAuth = false;
|
||||
} catch (e) {
|
||||
console.error("Failed to check auth:", e);
|
||||
isSignedIn = false;
|
||||
checkingAuth = false;
|
||||
goto("/oauth");
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<main class="container">
|
||||
<h1>Welcome to Tauri + Svelte</h1>
|
||||
{#if checkingAuth}
|
||||
<div class="flex items-center justify-center flex-1 bg-[#1c2027]">
|
||||
<div class="flex flex-col items-center gap-4">
|
||||
<Icon icon="mdi:loading" class="text-4xl animate-spin text-[#4c9ac9]" />
|
||||
<p class="text-[#9399a8]">Checking authentication...</p>
|
||||
</div>
|
||||
</div>
|
||||
{:else if isSignedIn}
|
||||
<div class="main-container">
|
||||
<!-- Left Sidebar -->
|
||||
<aside class="sidebar">
|
||||
<RepositorySelector
|
||||
{selectedRepo}
|
||||
{loading}
|
||||
onRefresh={() => loadRepositories()}
|
||||
/>
|
||||
<RepositoryList
|
||||
{repos}
|
||||
{selectedRepo}
|
||||
{loading}
|
||||
{error}
|
||||
onSelectRepo={(repo: Repository) => selectedRepo = repo}
|
||||
/>
|
||||
</aside>
|
||||
|
||||
<div class="row">
|
||||
<a href="https://vite.dev" target="_blank">
|
||||
<img src="/vite.svg" class="logo vite" alt="Vite Logo" />
|
||||
</a>
|
||||
<a href="https://tauri.app" target="_blank">
|
||||
<img src="/tauri.svg" class="logo tauri" alt="Tauri Logo" />
|
||||
</a>
|
||||
<a href="https://svelte.dev" target="_blank">
|
||||
<img src="/svelte.svg" class="logo svelte-kit" alt="SvelteKit Logo" />
|
||||
</a>
|
||||
</div>
|
||||
<p>Click on the Tauri, Vite, and SvelteKit logos to learn more.</p>
|
||||
|
||||
<form class="row" onsubmit={greet}>
|
||||
<input id="greet-input" placeholder="Enter a name..." bind:value={name} />
|
||||
<button type="submit">Greet</button>
|
||||
</form>
|
||||
<p>{greetMsg}</p>
|
||||
</main>
|
||||
<!-- Main Content -->
|
||||
<main class="main-content">
|
||||
{#if selectedRepo}
|
||||
<RepositoryView baseUrl={baseUrl} repo={selectedRepo} />
|
||||
{:else}
|
||||
<EmptyState />
|
||||
{/if}
|
||||
</main>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.logo.vite:hover {
|
||||
filter: drop-shadow(0 0 2em #747bff);
|
||||
}
|
||||
.main-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
background-color: #1c2027;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.logo.svelte-kit:hover {
|
||||
filter: drop-shadow(0 0 2em #ff3e00);
|
||||
}
|
||||
|
||||
:root {
|
||||
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
font-weight: 400;
|
||||
|
||||
color: #0f0f0f;
|
||||
background-color: #f6f6f6;
|
||||
|
||||
font-synthesis: none;
|
||||
text-rendering: optimizeLegibility;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
.container {
|
||||
margin: 0;
|
||||
padding-top: 10vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 6em;
|
||||
padding: 1.5em;
|
||||
will-change: filter;
|
||||
transition: 0.75s;
|
||||
}
|
||||
|
||||
.logo.tauri:hover {
|
||||
filter: drop-shadow(0 0 2em #24c8db);
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
a {
|
||||
font-weight: 500;
|
||||
color: #646cff;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #535bf2;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
input,
|
||||
button {
|
||||
border-radius: 8px;
|
||||
border: 1px solid transparent;
|
||||
padding: 0.6em 1.2em;
|
||||
font-size: 1em;
|
||||
font-weight: 500;
|
||||
font-family: inherit;
|
||||
color: #0f0f0f;
|
||||
background-color: #ffffff;
|
||||
transition: border-color 0.25s;
|
||||
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
border-color: #396cd8;
|
||||
}
|
||||
button:active {
|
||||
border-color: #396cd8;
|
||||
background-color: #e8e8e8;
|
||||
}
|
||||
|
||||
input,
|
||||
button {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#greet-input {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
color: #f6f6f6;
|
||||
background-color: #2f2f2f;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #24c8db;
|
||||
}
|
||||
|
||||
input,
|
||||
button {
|
||||
color: #ffffff;
|
||||
background-color: #0f0f0f98;
|
||||
}
|
||||
button:active {
|
||||
background-color: #0f0f0f69;
|
||||
}
|
||||
}
|
||||
.sidebar {
|
||||
width: 320px;
|
||||
background-color: #23272f;
|
||||
border-right: 1px solid #3e4451;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
min-width: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
217
src/routes/oauth/+page.svelte
Normal file
@@ -0,0 +1,217 @@
|
||||
<script lang="ts">
|
||||
import { goto } from "$app/navigation";
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { GiteaClient } from "$lib/gitea/client";
|
||||
import { setAuth } from "$lib/stores/auth";
|
||||
import { titleBarStore } from "$lib/stores/titleBar";
|
||||
import Icon from "@iconify/svelte";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
let baseUrl = $state("");
|
||||
let loading = $state(false);
|
||||
let error = $state("");
|
||||
let step = $state<"url" | "token">("url");
|
||||
let token = $state("");
|
||||
|
||||
// Hide menu items on OAuth page
|
||||
onMount(() => {
|
||||
titleBarStore.set({
|
||||
baseUrl: "",
|
||||
serverVersion: "",
|
||||
showMenus: false,
|
||||
onSignOut: undefined
|
||||
});
|
||||
});
|
||||
|
||||
async function handleServerSubmit(e: Event) {
|
||||
e.preventDefault();
|
||||
error = "";
|
||||
|
||||
if (!baseUrl.trim()) {
|
||||
error = "Please enter your Gitea server URL";
|
||||
return;
|
||||
}
|
||||
|
||||
step = "token";
|
||||
}
|
||||
|
||||
async function handleTokenSubmit(e: Event) {
|
||||
e.preventDefault();
|
||||
error = "";
|
||||
|
||||
if (!token.trim()) {
|
||||
error = "Please enter your personal access token";
|
||||
return;
|
||||
}
|
||||
|
||||
loading = true;
|
||||
try {
|
||||
const normalizedUrl = baseUrl.trim().replace(/\/$/, "");
|
||||
const client = new GiteaClient(normalizedUrl, token.trim());
|
||||
|
||||
await client.getCurrentUser();
|
||||
|
||||
await invoke("store_token", { token: token.trim() });
|
||||
await invoke("store_base_url", { baseUrl: normalizedUrl });
|
||||
setAuth(token.trim(), normalizedUrl);
|
||||
|
||||
goto("/");
|
||||
} catch (e) {
|
||||
error = `Failed to authenticate: ${String(e)}`;
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
if (step === "token") {
|
||||
step = "url";
|
||||
error = "";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="min-h-screen flex items-center justify-center p-4 md:p-8 bg-[#1c2027]">
|
||||
<div class="bg-[#2a2e3a] border border-[#3e4451] rounded-xl p-8 md:p-12 max-w-2xl w-full shadow-2xl">
|
||||
<!-- Header with Icon -->
|
||||
<div class="text-center mb-8">
|
||||
<div class="inline-flex items-center justify-center w-16 h-16 rounded-xl bg-linear-to-br from-[#4c9ac9] to-[#3a7ea5] mb-4 shadow-lg">
|
||||
<Icon icon="mdi:git" class="text-3xl text-white" />
|
||||
</div>
|
||||
<h1 class="text-3xl font-bold text-white mb-2">Authentication Required</h1>
|
||||
<p class="text-[#9399a8] text-sm">
|
||||
{#if step === "url"}
|
||||
Enter your Gitea instance URL to continue
|
||||
{:else}
|
||||
Generate a personal access token to authenticate
|
||||
{/if}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{#if step === "url"}
|
||||
<form class="space-y-6" onsubmit={handleServerSubmit}>
|
||||
<div class="space-y-2">
|
||||
<label class="block">
|
||||
<span class="text-sm font-semibold text-white mb-2 flex items-center gap-2">
|
||||
<Icon icon="mdi:server" class="text-[#4c9ac9]" />
|
||||
Gitea Server URL
|
||||
</span>
|
||||
<input
|
||||
class="w-full px-4 py-3 bg-[#1c2027] border border-[#3e4451] rounded-lg text-white placeholder-[#6b7280] focus:outline-none focus:border-[#4c9ac9] focus:ring-2 focus:ring-[#4c9ac9]/30 transition-all disabled:opacity-50"
|
||||
type="url"
|
||||
bind:value={baseUrl}
|
||||
placeholder="https://gitea.example.com"
|
||||
disabled={loading}
|
||||
required
|
||||
/>
|
||||
<span class="text-xs text-[#6b7280] mt-1.5 block">
|
||||
Enter the URL of your Gitea instance (e.g., https://git.gitea.com)
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{#if error}
|
||||
<div class="px-4 py-3 rounded-lg bg-[#7d2f2f]/20 border border-[#a04141] text-[#ff6b6b] flex items-start gap-3">
|
||||
<Icon icon="mdi:alert-circle" class="text-xl shrink-0 mt-0.5" />
|
||||
<p class="text-sm">{error}</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="w-full px-4 py-3 rounded-lg bg-linear-to-r from-[#4c9ac9] to-[#5da9d6] hover:from-[#5da9d6] hover:to-[#6eb8e3] text-white font-semibold transition-all disabled:opacity-50 disabled:cursor-not-allowed shadow-lg hover:shadow-xl flex items-center justify-center gap-2"
|
||||
disabled={loading}
|
||||
>
|
||||
{#if loading}
|
||||
<Icon icon="mdi:loading" class="animate-spin" />
|
||||
Connecting...
|
||||
{:else}
|
||||
Continue
|
||||
<Icon icon="mdi:arrow-right" />
|
||||
{/if}
|
||||
</button>
|
||||
</form>
|
||||
{:else}
|
||||
<!-- Token Instructions Card -->
|
||||
<div class="bg-[#1c2027] border border-[#3e4451] rounded-lg p-6 mb-6">
|
||||
<h3 class="text-lg font-bold text-white mb-4 flex items-center gap-2">
|
||||
<Icon icon="mdi:information" class="text-[#4c9ac9]" />
|
||||
How to generate a token:
|
||||
</h3>
|
||||
<ol class="list-decimal list-inside space-y-3 text-sm text-[#c0c6d4]">
|
||||
<li class="pl-2">
|
||||
Go to <span class="px-2 py-0.5 rounded bg-[#3e4451] text-[#84c5fb] font-mono text-xs">{baseUrl}</span>
|
||||
</li>
|
||||
<li class="pl-2">Navigate to <strong class="text-white">Settings → Applications</strong></li>
|
||||
<li class="pl-2">Click <strong class="text-white">Generate New Token</strong></li>
|
||||
<li class="pl-2">Give it a name (e.g., "Gitea Desktop")</li>
|
||||
<li class="pl-2">
|
||||
Select scopes:
|
||||
<span class="inline-flex gap-2 flex-wrap mt-1">
|
||||
<code class="px-2 py-0.5 rounded bg-[#3e4451] text-[#84c5fb] font-mono text-xs">read:user</code>
|
||||
<code class="px-2 py-0.5 rounded bg-[#3e4451] text-[#84c5fb] font-mono text-xs">read:repository</code>
|
||||
<code class="px-2 py-0.5 rounded bg-[#3e4451] text-[#84c5fb] font-mono text-xs">write:repository</code>
|
||||
</span>
|
||||
</li>
|
||||
<li class="pl-2">Click <strong class="text-white">Generate Token</strong></li>
|
||||
<li class="pl-2">Copy the token and paste it below</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<form class="space-y-6" onsubmit={handleTokenSubmit}>
|
||||
<div class="space-y-2">
|
||||
<label class="block">
|
||||
<span class="text-sm font-semibold text-white mb-2 flex items-center gap-2">
|
||||
<Icon icon="mdi:key" class="text-[#4c9ac9]" />
|
||||
Personal Access Token
|
||||
</span>
|
||||
<input
|
||||
class="w-full px-4 py-3 bg-[#1c2027] border border-[#3e4451] rounded-lg text-white placeholder-[#6b7280] focus:outline-none focus:border-[#4c9ac9] focus:ring-2 focus:ring-[#4c9ac9]/30 transition-all disabled:opacity-50 font-mono"
|
||||
type="password"
|
||||
bind:value={token}
|
||||
placeholder="Paste your token here"
|
||||
disabled={loading}
|
||||
required
|
||||
/>
|
||||
<span class="text-xs text-[#6b7280] mt-1.5 flex items-center gap-1.5">
|
||||
<Icon icon="mdi:lock" class="text-sm" />
|
||||
This token will be stored securely on your device
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{#if error}
|
||||
<div class="px-4 py-3 rounded-lg bg-[#7d2f2f]/20 border border-[#a04141] text-[#ff6b6b] flex items-start gap-3">
|
||||
<Icon icon="mdi:alert-circle" class="text-xl shrink-0 mt-0.5" />
|
||||
<p class="text-sm">{error}</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="flex flex-col-reverse md:flex-row gap-3">
|
||||
<button
|
||||
type="button"
|
||||
class="px-4 py-2.5 rounded-lg bg-[#3e4451] hover:bg-[#4a5064] text-[#c0c6d4] font-medium transition-all flex items-center justify-center gap-2"
|
||||
onclick={goBack}
|
||||
disabled={loading}
|
||||
>
|
||||
<Icon icon="mdi:arrow-left" />
|
||||
Back
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="flex-1 px-4 py-2.5 rounded-lg bg-linear-to-r from-[#2d6a3d] to-[#3a7d4c] hover:from-[#3a7d4c] hover:to-[#47905b] text-white font-semibold transition-all disabled:opacity-50 disabled:cursor-not-allowed shadow-lg hover:shadow-xl flex items-center justify-center gap-2"
|
||||
disabled={loading}
|
||||
>
|
||||
{#if loading}
|
||||
<Icon icon="mdi:loading" class="animate-spin text-lg" />
|
||||
Authenticating...
|
||||
{:else}
|
||||
<Icon icon="mdi:login" />
|
||||
Sign In
|
||||
{/if}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 22 KiB |
@@ -1,32 +1,33 @@
|
||||
import { defineConfig } from "vite";
|
||||
import { sveltekit } from "@sveltejs/kit/vite";
|
||||
import tailwindcss from "@tailwindcss/vite";
|
||||
|
||||
// @ts-expect-error process is a nodejs global
|
||||
const host = process.env.TAURI_DEV_HOST;
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig(async () => ({
|
||||
plugins: [sveltekit()],
|
||||
plugins: [sveltekit(), tailwindcss()],
|
||||
|
||||
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
|
||||
//
|
||||
// 1. prevent Vite from obscuring rust errors
|
||||
clearScreen: false,
|
||||
// 2. tauri expects a fixed port, fail if that port is not available
|
||||
server: {
|
||||
port: 1420,
|
||||
strictPort: true,
|
||||
host: host || false,
|
||||
hmr: host
|
||||
? {
|
||||
protocol: "ws",
|
||||
host,
|
||||
port: 1421,
|
||||
}
|
||||
: undefined,
|
||||
watch: {
|
||||
// 3. tell Vite to ignore watching `src-tauri`
|
||||
ignored: ["**/src-tauri/**"],
|
||||
},
|
||||
},
|
||||
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
|
||||
//
|
||||
// 1. prevent Vite from obscuring rust errors
|
||||
clearScreen: false,
|
||||
// 2. tauri expects a fixed port, fail if that port is not available
|
||||
server: {
|
||||
port: 1420,
|
||||
strictPort: true,
|
||||
host: host || false,
|
||||
hmr: host
|
||||
? {
|
||||
protocol: "ws",
|
||||
host,
|
||||
port: 1421,
|
||||
}
|
||||
: undefined,
|
||||
watch: {
|
||||
// 3. tell Vite to ignore watching `src-tauri`
|
||||
ignored: ["**/src-tauri/**"],
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||