diff --git a/.cursor/rules/authentication.mdc b/.cursor/rules/authentication.mdc index c8924d0..eb7f4a3 100644 --- a/.cursor/rules/authentication.mdc +++ b/.cursor/rules/authentication.mdc @@ -75,8 +75,8 @@ Frontend listens to these events in `src/App.tsx` to update UI and refresh data. ## Environment Variables -- **EVE_CLIENT_ID** (required): OAuth client ID from EVE Developers portal -- **EVE_CALLBACK_URL** (optional): Override default callback URL +- **EVE_CLIENT_ID** (required): OAuth client ID from EVE Developers portal. Can be set in a `.env` file in the project root. +- **EVE_CALLBACK_URL** (optional): Override default callback URL. Can be set in a `.env` file in the project root. ## Security Considerations diff --git a/.cursor/rules/project-setup.mdc b/.cursor/rules/project-setup.mdc index 2f479e6..62b5a05 100644 --- a/.cursor/rules/project-setup.mdc +++ b/.cursor/rules/project-setup.mdc @@ -6,9 +6,17 @@ alwaysApply: true ## Environment Variables +The application reads environment variables from the system environment or a `.env` file in the project root. + - **EVE_CLIENT_ID** (required): EVE Online SSO client ID for OAuth authentication - **EVE_CALLBACK_URL** (optional): OAuth callback URL, defaults to `http://localhost:1421/callback` for development +Example `.env` file: +```env +EVE_CLIENT_ID=your_client_id_here +EVE_CALLBACK_URL=http://localhost:1421/callback +``` + ## Development Commands - `pnpm dev` - Start Vite dev server for frontend development diff --git a/.github/workflows/build-adhoc-windows.yml b/.github/workflows/build-adhoc.yml similarity index 53% rename from .github/workflows/build-adhoc-windows.yml rename to .github/workflows/build-adhoc.yml index 899084c..6f5b260 100644 --- a/.github/workflows/build-adhoc-windows.yml +++ b/.github/workflows/build-adhoc.yml @@ -1,18 +1,52 @@ -name: Build Windows +name: Build Adhoc on: workflow_dispatch: + inputs: + platform: + description: 'Platform to build' + required: true + default: 'All' + type: choice + options: + - All + - Windows + - Linux + - macOS jobs: build: - runs-on: windows-latest + strategy: + fail-fast: false + matrix: + include: + - platform_name: Windows + os: windows-latest + target: x86_64-pc-windows-msvc + - platform_name: Linux + os: ubuntu-latest + target: x86_64-unknown-linux-gnu + - platform_name: macOS + os: macos-latest + target: aarch64-apple-darwin + runs-on: ${{ matrix.os }} + # Only run if 'All' is selected or the specific platform is selected + if: | + github.event.inputs.platform == 'All' || + github.event.inputs.platform == matrix.platform_name steps: - uses: actions/checkout@v4 + - name: Install Linux dependencies + if: matrix.os == 'ubuntu-latest' + 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: Install Rust uses: dtolnay/rust-toolchain@stable with: - targets: x86_64-pc-windows-msvc + targets: ${{ matrix.target }} - name: Install Node.js uses: actions/setup-node@v4 @@ -41,7 +75,9 @@ jobs: id: cache-oas3-gen uses: actions/cache@v4 with: - path: ~/.cargo/bin/oas3-gen.exe + path: | + ~/.cargo/bin/oas3-gen + ~/.cargo/bin/oas3-gen.exe key: ${{ runner.os }}-oas3-gen-0.22.1 - name: Install oas3-gen @@ -52,7 +88,11 @@ jobs: id: cache-tauri-typegen uses: actions/cache@v4 with: - path: ~/.cargo/bin/cargo-tauri-typegen.exe + path: | + ~/.cargo/bin/cargo-tauri-typegen + ~/.cargo/bin/cargo-tauri-typegen.exe + ~/.cargo/bin/tauri-typegen + ~/.cargo/bin/tauri-typegen.exe key: ${{ runner.os }}-tauri-typegen-0.3.4 - name: Install tauri-typegen @@ -91,10 +131,29 @@ jobs: EVE_CLIENT_ID: ${{ secrets.EVE_CLIENT_ID }} run: pnpm tauri build - - name: Upload installer + - name: Upload installer (Windows) + if: matrix.os == 'windows-latest' uses: actions/upload-artifact@v4 with: - name: skillmon-windows-installer + name: skillmon-windows path: | src-tauri/target/release/bundle/msi/*.msi src-tauri/target/release/bundle/nsis/*.exe + + - name: Upload bundle (Linux) + if: matrix.os == 'ubuntu-latest' + uses: actions/upload-artifact@v4 + with: + name: skillmon-linux + path: | + src-tauri/target/release/bundle/deb/*.deb + src-tauri/target/release/bundle/appimage/*.AppImage + + - name: Upload bundle (macOS) + if: matrix.os == 'macos-latest' + uses: actions/upload-artifact@v4 + with: + name: skillmon-macos + path: | + src-tauri/target/release/bundle/dmg/*.dmg + src-tauri/target/release/bundle/macos/*.app diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e6e2380..9148f37 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,21 +11,78 @@ on: type: string jobs: - release: + prepare: permissions: contents: write - runs-on: windows-latest + runs-on: ubuntu-latest + outputs: + version: ${{ inputs.version }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Install Node.js + uses: actions/setup-node@v4 + with: + node-version: '24' + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + + - name: Update version files + shell: bash + run: bash scripts/update-version.sh "${{ inputs.version }}" + + - name: Create git tag + shell: bash + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add package.json src-tauri/tauri.conf.json src-tauri/Cargo.toml src-tauri/Cargo.lock + + if git diff --staged --quiet; then + echo "Error: No changes detected after updating version files" + exit 1 + fi + + git commit -m "chore: bump version to ${{ inputs.version }}" + git tag -a "v${{ inputs.version }}" -m "Release v${{ inputs.version }}" + git push origin HEAD + git push origin "v${{ inputs.version }}" + + build: + needs: prepare + permissions: + contents: write + strategy: + fail-fast: false + matrix: + include: + - platform: 'windows-latest' + target: 'x86_64-pc-windows-msvc' + # - platform: 'macos-latest' + # target: 'aarch64-apple-darwin' + # - platform: 'ubuntu-latest' + # target: 'x86_64-unknown-linux-gnu' + runs-on: ${{ matrix.platform }} env: HUSKY: 0 steps: - uses: actions/checkout@v4 with: - fetch-depth: 0 + ref: v${{ needs.prepare.outputs.version }} + + - name: Install Linux dependencies + if: matrix.platform == 'ubuntu-latest' + 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: Install Rust uses: dtolnay/rust-toolchain@stable with: - targets: x86_64-pc-windows-msvc + targets: ${{ matrix.target }} - name: Install Node.js uses: actions/setup-node@v4 @@ -54,7 +111,9 @@ jobs: id: cache-oas3-gen uses: actions/cache@v4 with: - path: ~/.cargo/bin/oas3-gen.exe + path: | + ~/.cargo/bin/oas3-gen + ~/.cargo/bin/oas3-gen.exe key: ${{ runner.os }}-oas3-gen-0.22.1 - name: Install oas3-gen @@ -65,7 +124,11 @@ jobs: id: cache-tauri-typegen uses: actions/cache@v4 with: - path: ~/.cargo/bin/cargo-tauri-typegen.exe + path: | + ~/.cargo/bin/cargo-tauri-typegen + ~/.cargo/bin/cargo-tauri-typegen.exe + ~/.cargo/bin/tauri-typegen + ~/.cargo/bin/tauri-typegen.exe key: ${{ runner.os }}-tauri-typegen-0.3.4 - name: Install tauri-typegen @@ -99,39 +162,18 @@ jobs: - name: Generate route tree run: pnpm generate-route-tree - - name: Update version files - shell: bash - run: bash scripts/update-version.sh "${{ inputs.version }}" - - name: Type check run: pnpm typecheck - - name: Create git tag - shell: bash - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git add package.json src-tauri/tauri.conf.json src-tauri/Cargo.toml - - # Check if there are changes to commit - if git diff --staged --quiet; then - echo "Error: No changes detected after updating version files" - echo "This indicates the version update script may have failed" - exit 1 - fi - - git commit -m "chore: bump version to ${{ inputs.version }}" - git tag -a "v${{ inputs.version }}" -m "Release v${{ inputs.version }}" - git push origin HEAD - git push origin "v${{ inputs.version }}" - - uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} EVE_CLIENT_ID: ${{ secrets.EVE_CLIENT_ID }} + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} with: - tagName: v__VERSION__ - releaseName: 'Skillmon v__VERSION__' + tagName: v${{ needs.prepare.outputs.version }} + releaseName: 'Skillmon v${{ needs.prepare.outputs.version }}' releaseBody: 'See the assets to download this version and install.' releaseDraft: true prerelease: false diff --git a/.gitignore b/.gitignore index 8e608da..1331c81 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,6 @@ src/routeTree.gen.ts sde/*.jsonl eve-online-static-data-*.zip src-tauri/tests/fixtures/sde_cache/ + +# Environment variables +.env diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b756e6f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 snipereagle1 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 102e366..a107fe3 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,115 @@ -# Tauri + React + Typescript +

+ skillmon logo +

-This template should help get you started developing with Tauri, React and Typescript in Vite. +# skillmon -## Recommended IDE Setup +Skillmon is a desktop application for monitoring and planning character training in EVE Online. -- [VS Code](https://code.visualstudio.com/) + [Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) + [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) +# DISCLAIMER + +This project is not yet in a stable state, no support will be provided yet. Your skill queues might be haunted. + +

+ haunted +

+ +## Major Features + +- **Multi-Character Support**: Manage and monitor all your EVE Online characters in one place. +- **Account Grouping**: Organize your characters into custom accounts. +- **Skill Queue Monitoring**: Real-time tracking of skill queues. +- **Advanced Skill Planning**: + - Create, edit, and manage complex skill plans. + - Import/Export plans in various formats (Text, XML, JSON). + - Compare plans against your characters' current skills. + - Automatic prerequisite handling. +- **Simulation & Optimization**: + - **Timeline Simulation**: Visualize when your skills will finish. + - **Remap Optimization**: Calculate the most efficient attribute remaps for your plan. + - **Reorder Optimization**: Automatically reorder your plan to maximize training speed based on attributes. +- **Character Notifications**: System tray notifications for low skill queues and other important events. +- **Offline First**: All your data is stored in a local SQLite database. No external servers or account requirements beyond EVE SSO. +- **SDE Integration**: Built-in EVE Static Data Export (SDE) management. + +## Tech Stack + +Skillmon leverages a modern, high-performance stack: + +- **Backend**: [Rust](https://www.rust-lang.org/) with [Tauri v2](https://tauri.app/) +- **Frontend**: [React](https://react.dev/) + [TypeScript](https://www.typescriptlang.org/) +- **Database**: [SQLite](https://www.sqlite.org/) via [sqlx](https://github.com/launchbadge/sqlx) +- **Styling**: [Tailwind CSS](https://tailwindcss.com/) + [shadcn/ui](https://ui.shadcn.com/) +- **State Management**: [Zustand](https://github.com/pmndrs/zustand) & [TanStack Query](https://tanstack.com/query/latest) + +## Getting Started + +### Prerequisites + +To build and run skillmon locally, you will need: + +- [Rust](https://www.rust-lang.org/tools/install) (latest stable) +- [Node.js](https://nodejs.org/) & [pnpm](https://pnpm.io/) +- [EVE Online Developer Application](https://developers.eveonline.com/) credentials + +### Environment Setup + +Create a `.env` file in the root directory (or set these environment variables in your shell): + +```bash +# Required: Your EVE Online SSO Client ID +EVE_CLIENT_ID=your_client_id_here + +# Optional: Defaults to http://localhost:1421/callback for dev +EVE_CALLBACK_URL=http://localhost:1421/callback +``` + +_Note: Ensure your EVE Developer App has the correct callback URL configured._ + +### Development Commands + +1. **Install dependencies**: + + ```bash + pnpm install + ``` + +2. **Generate ESI Client**: + + ```bash + ./scripts/generate-esi.sh + ``` + +3. **Run in development mode**: + + ```bash + pnpm tauri dev + ``` + +4. **Run tests**: + ```bash + pnpm test # Frontend tests + cargo test # Rust tests (in src-tauri) + ``` + +## Development Documentation + +Technical details and architecture overviews are documented in `.cursor/rules`. These files provide valuable context for developers and are optimized for AI-assisted development. + +## Contributing + +This project is in an early stage of development. Contributions, bug reports, and feature suggestions are welcome! + +1. Fork the repository +2. Create your feature branch (`git checkout -b feature/amazing-feature`) +3. Commit your changes (`git commit -m 'Add some amazing feature'`) +4. Push to the branch (`git push origin feature/amazing-feature`) +5. Open a Pull Request + +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +--- + +_Disclaimer: EVE Online and the EVE logo are the registered trademarks of CCP hf. All rights are reserved worldwide. All other trademarks are the property of their respective owners. CCP hf. has granted permission to use EVE Online and all associated logos and designs for promotional and information purposes on its website but does not endorse, and is not in any way affiliated with, skillmon. CCP is not responsible for the content, functional, or operational aspects of this website and application._ diff --git a/package.json b/package.json index 51fc65d..f75a8ac 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,8 @@ { "name": "skillmon", - "private": true, "version": "0.8.0", + "license": "MIT", + "author": "snipereagle1 ", "type": "module", "scripts": { "dev": "vite", @@ -37,6 +38,9 @@ "src-tauri/**/*.rs": [ "sh -c 'cd src-tauri && cargo clippy -- -D warnings'", "sh -c 'cd src-tauri && cargo fmt -- --check'" + ], + "*.md": [ + "prettier --write" ] }, "dependencies": { @@ -68,6 +72,8 @@ "@tauri-apps/plugin-dialog": "^2", "@tauri-apps/plugin-fs": "^2", "@tauri-apps/plugin-opener": "^2", + "@tauri-apps/plugin-process": "^2.3.1", + "@tauri-apps/plugin-updater": "^2.9.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a875634..1d807e5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -92,6 +92,12 @@ importers: '@tauri-apps/plugin-opener': specifier: ^2 version: 2.5.2 + '@tauri-apps/plugin-process': + specifier: ^2.3.1 + version: 2.3.1 + '@tauri-apps/plugin-updater': + specifier: ^2.9.0 + version: 2.9.0 class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -1768,6 +1774,12 @@ packages: '@tauri-apps/plugin-opener@2.5.2': resolution: {integrity: sha512-ei/yRRoCklWHImwpCcDK3VhNXx+QXM9793aQ64YxpqVF0BDuuIlXhZgiAkc15wnPVav+IbkYhmDJIv5R326Mew==} + '@tauri-apps/plugin-process@2.3.1': + resolution: {integrity: sha512-nCa4fGVaDL/B9ai03VyPOjfAHRHSBz5v6F/ObsB73r/dA3MHHhZtldaDMIc0V/pnUw9ehzr2iEG+XkSEyC0JJA==} + + '@tauri-apps/plugin-updater@2.9.0': + resolution: {integrity: sha512-j++sgY8XpeDvzImTrzWA08OqqGqgkNyxczLD7FjNJJx/uXxMZFz5nDcfkyoI/rCjYuj2101Tci/r/HFmOmoxCg==} + '@ts-morph/common@0.27.0': resolution: {integrity: sha512-Wf29UqxWDpc+i61k3oIOzcUfQt79PIT9y/MWfAGlrkjg6lBC1hwDECLXPVJAhWjiGbfBCxZd65F/LIZF3+jeJQ==} @@ -5734,6 +5746,14 @@ snapshots: dependencies: '@tauri-apps/api': 2.9.1 + '@tauri-apps/plugin-process@2.3.1': + dependencies: + '@tauri-apps/api': 2.9.1 + + '@tauri-apps/plugin-updater@2.9.0': + dependencies: + '@tauri-apps/api': 2.9.1 + '@ts-morph/common@0.27.0': dependencies: fast-glob: 3.3.3 diff --git a/scripts/generate-esi.sh b/scripts/generate-esi.sh index 94c7c1a..3318cd7 100755 --- a/scripts/generate-esi.sh +++ b/scripts/generate-esi.sh @@ -17,11 +17,11 @@ echo "Generating client..." oas3-gen generate client -i openapi.json -o client.rs --exclude get_meta_changelog echo "Adding types import to client.rs..." -sed -i '/^use validator::Validate;/a use super::types::*;' client.rs +perl -pi -e 's/^use validator::Validate;$/use validator::Validate;\nuse super::types::*;/' client.rs echo "Suppressing unused code warnings..." -sed -i '1a #![allow(dead_code)]' types.rs -sed -i '1a #![allow(dead_code)]' client.rs +perl -pi -e '$_ = "#![allow(dead_code)]\n" . $_ if $. == 1' types.rs +perl -pi -e '$_ = "#![allow(dead_code)]\n" . $_ if $. == 1' client.rs echo "Done! Generated types.rs and client.rs in $ESI_DIR" diff --git a/scripts/update-version.sh b/scripts/update-version.sh index fdc5799..b3d0f9a 100755 --- a/scripts/update-version.sh +++ b/scripts/update-version.sh @@ -28,5 +28,14 @@ echo "Updated src-tauri/tauri.conf.json" node -e "const fs = require('fs'); const content = fs.readFileSync('src-tauri/Cargo.toml', 'utf8'); const updated = content.replace(/^version = \".*\"/m, 'version = \"$VERSION\"'); fs.writeFileSync('src-tauri/Cargo.toml', updated);" echo "Updated src-tauri/Cargo.toml" +# Update Cargo.lock if cargo is available +if command -v cargo &> /dev/null; then + echo "Updating Cargo.lock..." + (cd src-tauri && cargo update -p skillmon --offline || cargo update -p skillmon) + echo "Updated src-tauri/Cargo.lock" +else + echo "Warning: cargo command not found, Cargo.lock was not updated." +fi + echo "Version update complete!" diff --git a/src-tauri/.env.sample b/src-tauri/.env.sample new file mode 100644 index 0000000..3fbc55e --- /dev/null +++ b/src-tauri/.env.sample @@ -0,0 +1,7 @@ +# EVE Online SSO Configuration +# Get your Client ID at https://developers.eveonline.com/ +EVE_CLIENT_ID=your_client_id_here + +# Optional: OAuth callback URL. +# Defaults to http://localhost:1421/callback in development +# EVE_CALLBACK_URL=http://localhost:1421/callback diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index bc612f8..0369227 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -66,6 +66,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 = "arboard" version = "3.6.1" @@ -936,6 +945,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.111", +] + [[package]] name = "derive_more" version = "0.99.20" @@ -1304,6 +1324,17 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "filetime" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" +dependencies = [ + "cfg-if", + "libc", + "libredox", +] + [[package]] name = "find-msvc-tools" version = "0.1.5" @@ -1666,9 +1697,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]] @@ -2037,6 +2070,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", + "webpki-roots", ] [[package]] @@ -2539,6 +2573,12 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +[[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" @@ -2641,6 +2681,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[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" @@ -3129,6 +3175,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" @@ -3290,6 +3348,20 @@ dependencies = [ "windows-sys 0.61.2", ] +[[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" @@ -3770,6 +3842,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" @@ -3996,6 +4123,8 @@ dependencies = [ "native-tls", "percent-encoding", "pin-project-lite", + "quinn", + "rustls", "rustls-pki-types", "serde", "serde_json", @@ -4003,6 +4132,7 @@ dependencies = [ "sync_wrapper", "tokio", "tokio-native-tls", + "tokio-rustls", "tokio-util", "tower", "tower-http", @@ -4012,6 +4142,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", + "webpki-roots", ] [[package]] @@ -4083,6 +4214,12 @@ dependencies = [ "ordered-multimap", ] +[[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" @@ -4112,6 +4249,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ "once_cell", + "ring", "rustls-pki-types", "rustls-webpki", "subtle", @@ -4124,6 +4262,7 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "708c0f9d5f54ba0272468c1d306a52c495b31fa155e91bc25371e6df7996908c" dependencies = [ + "web-time", "zeroize", ] @@ -4552,13 +4691,14 @@ checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" [[package]] name = "skillmon" -version = "0.7.1" +version = "0.8.0" dependencies = [ "anyhow", "async-trait", "axum", "base64 0.22.1", "chrono", + "dotenvy", "futures-util", "http", "hyper", @@ -4582,6 +4722,7 @@ dependencies = [ "tauri-plugin-notification", "tauri-plugin-opener", "tauri-plugin-single-instance", + "tauri-plugin-updater", "tempfile", "tokio", "tower", @@ -4589,7 +4730,7 @@ dependencies = [ "url", "urlencoding", "validator 0.18.1", - "zip", + "zip 0.6.6", ] [[package]] @@ -5083,6 +5224,17 @@ dependencies = [ "syn 2.0.111", ] +[[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" @@ -5353,6 +5505,38 @@ dependencies = [ "zbus", ] +[[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 4.6.1", +] + [[package]] name = "tauri-runtime" version = "2.9.2" @@ -6353,6 +6537,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" @@ -6397,6 +6591,15 @@ dependencies = [ "system-deps", ] +[[package]] +name = "webpki-roots" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12bed680863276c63889429bfd6cab3b99943659923822de1c8a39c49e4d722c" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "webview2-com" version = "0.38.0" @@ -7112,6 +7315,16 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" +[[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" @@ -7289,6 +7502,18 @@ dependencies = [ "flate2", ] +[[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.1", + "memchr", +] + [[package]] name = "zune-core" version = "0.4.12" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 369a7b8..c6b6a8b 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,9 +1,10 @@ [package] name = "skillmon" version = "0.8.0" -description = "A Tauri App" -authors = ["you"] +description = "A modern EVE Online skill monitoring and planning application." +authors = ["snipereagle1 "] edition = "2021" +license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -54,3 +55,5 @@ oas3-gen-support = "0.22" serde_plain = "1" zip = { version = "0.6", default-features = false, features = ["deflate"] } quick-xml = "0.37" +dotenvy = "0.15.7" +tauri-plugin-updater = "2" diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 0c9fc62..09da00c 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -35,6 +35,8 @@ async fn is_startup_complete( #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { + let _ = dotenvy::dotenv(); + tauri::Builder::default() .setup(|app| { tauri::async_runtime::block_on(async { @@ -250,6 +252,7 @@ pub fn run() { .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_fs::init()) .plugin(tauri_plugin_notification::init()) + .plugin(tauri_plugin_updater::Builder::new().build()) .plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_deep_link::init()) .on_menu_event(|app, event| match event.id().as_ref() { diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 01980fc..df1fc4b 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -29,13 +29,19 @@ ] } }, - "tauri-typegen": { - "project_path": "./src-tauri", - "output_path": "./src/generated", - "validation_library": "none", - "verbose": true - } - }, + "tauri-typegen": { + "project_path": "./src-tauri", + "output_path": "./src/generated", + "validation_library": "none", + "verbose": true + }, + "updater": { + "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDhFQjFDMjE1Q0NCQTA1OUEKUldTYUJick1GY0t4anRUbFVya3BsNVBSa0FGa01mQzROWmhFakdvQnRTdCtaL09DM0F1TFlsMEoK", + "endpoints": [ + "https://github.com/snipereagle1/skillmon/releases/latest/download/latest.json" + ] + } + }, "bundle": { "active": true, "targets": "all", diff --git a/src/routes/__root.tsx b/src/routes/__root.tsx index 19144e3..1526590 100644 --- a/src/routes/__root.tsx +++ b/src/routes/__root.tsx @@ -1,6 +1,8 @@ import { useIsFetching } from '@tanstack/react-query'; -import { createRootRoute, Outlet } from '@tanstack/react-router'; -import { useState } from 'react'; +import { createRootRoute, Link, Outlet } from '@tanstack/react-router'; +import { check } from '@tauri-apps/plugin-updater'; +import { Download } from 'lucide-react'; +import { useEffect, useState } from 'react'; import { AddCharacterDialog } from '@/components/AddCharacterDialog'; import { NotificationBell } from '@/components/NotificationBell'; @@ -14,6 +16,7 @@ import { useAuthEvents } from '@/hooks/tauri/useAuthEvents'; import { useStartupState } from '@/hooks/tauri/useStartupState'; import { cn } from '@/lib/utils'; import { useSkillDetailStore } from '@/stores/skillDetailStore'; +import { useUpdateStore } from '@/stores/updateStore'; function RootComponent() { useAuthEvents(); @@ -23,6 +26,22 @@ function RootComponent() { const [notificationDrawerOpen, setNotificationDrawerOpen] = useState(false); const { open, skillId, characterId, closeSkillDetail } = useSkillDetailStore(); + const { updateAvailable, setUpdate } = useUpdateStore(); + + useEffect(() => { + const checkForUpdates = async () => { + try { + const update = await check(); + if (update) { + setUpdate(update); + } + } catch (error) { + console.error('Failed to check for updates:', error); + } + }; + + checkForUpdates(); + }, [setUpdate]); if (isStartingUp) { return ( @@ -49,6 +68,18 @@ function RootComponent() { ]} />
+ {updateAvailable && ( + + + + )} (''); + const [checking, setChecking] = useState(false); + const [downloading, setDownloading] = useState(false); + const [downloadProgress, setDownloadProgress] = useState(null); + + const { update, updateAvailable, setUpdate } = useUpdateStore(); useEffect(() => { const fetchVersion = async () => { @@ -21,23 +34,148 @@ function AboutPage() { fetchVersion(); }, []); + const handleCheckUpdate = async () => { + setChecking(true); + try { + const result = await check(); + setUpdate(result); + if (!result) { + toast.info('You are on the latest version'); + } + } catch (error) { + console.error('Failed to check for updates:', error); + toast.error('Failed to check for updates'); + } finally { + setChecking(false); + } + }; + + const handleInstallUpdate = async () => { + if (!update) return; + + setDownloading(true); + try { + let downloaded = 0; + let contentLength = 0; + + await update.downloadAndInstall((event) => { + switch (event.event) { + case 'Started': + contentLength = event.data.contentLength ?? 0; + break; + case 'Progress': + downloaded += event.data.chunkLength; + if (contentLength > 0) { + setDownloadProgress( + Math.round((downloaded / contentLength) * 100) + ); + } + break; + case 'Finished': + setDownloading(false); + setDownloadProgress(null); + break; + } + }); + + toast.success('Update installed, restarting...'); + await relaunch(); + } catch (error) { + console.error('Failed to install update:', error); + toast.error('Failed to install update'); + setDownloading(false); + setDownloadProgress(null); + } + }; + return ( -
-
-

About Skillmon

-

- EVE Online skill monitoring application -

-
-
-

Version

-

{version || 'Loading...'}

+
+
+
+

+ About Skillmon +

+

+ A modern EVE Online skill monitoring and planning application. +

+
+ Skillmon Logo
-
-

Release Notes

-

- Release notes will be displayed here in future updates. -

+ +
+
+
+

Version

+

+ {version || 'Loading...'} +

+
+ +
+ + {updateAvailable && update && ( +
+
+
+

+ New Version Available: v{update.version} +

+

+ Released on {new Date(update.date || '').toLocaleDateString()} +

+
+ +
+ + {downloading && downloadProgress !== null && ( +
+
+
+
+

+ {downloadProgress}% downloaded +

+
+ )} + + {update.body && ( +
+

+ What's New +

+
+ {update.body} +
+
+ )} +
+ )} + + {!updateAvailable && ( +
+
+ You're up to date +
+ )}
); diff --git a/src/stores/updateStore.ts b/src/stores/updateStore.ts new file mode 100644 index 0000000..c4982af --- /dev/null +++ b/src/stores/updateStore.ts @@ -0,0 +1,14 @@ +import type { Update } from '@tauri-apps/plugin-updater'; +import { create } from 'zustand'; + +interface UpdateState { + updateAvailable: boolean; + update: Update | null; + setUpdate: (update: Update | null) => void; +} + +export const useUpdateStore = create((set) => ({ + updateAvailable: false, + update: null, + setUpdate: (update) => set({ update, updateAvailable: !!update }), +}));