> = app_handle.state();
50 | (state.build_fn)(&app_handle)
51 | });
52 |
53 | Ok(())
54 | })
55 | .build()
56 | }
57 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "eslint.workingDirectories": [
3 | {
4 | "mode": "auto"
5 | }
6 | ],
7 | "editor.formatOnSave": true,
8 | "svelte.enable-ts-plugin": true,
9 | "eslint.useFlatConfig": true,
10 | "eslint.format.enable": true,
11 | "eslint.enable": true,
12 | "rust-analyzer.testExplorer": true,
13 | "rust-analyzer.interpret.tests": true,
14 | "rust-analyzer.check.command": "clippy",
15 | "rust-analyzer.server.extraEnv": {
16 | "DATABASE_URL": "sqlite://db/glowsquid.db3"
17 | },
18 | "rust-analyzer.check.extraEnv": {
19 | "DATABASE_URL": "sqlite://db/glowsquid.db3"
20 | },
21 | "rust-analyzer.cargo.extraEnv": {
22 | "DATABASE_URL": "sqlite://db/glowsquid.db3"
23 | },
24 | "eslint.validate": [
25 | "svelte",
26 | "typescript",
27 | "javascript",
28 | "json",
29 | "yaml"
30 | ],
31 | "eslint.probe": [
32 | "svelte",
33 | "typescript",
34 | "javascript",
35 | "json",
36 | "yaml"
37 | ],
38 | "conventionalCommits.scopes": [
39 | "copper",
40 | "glowsquid",
41 | "tauri-plugin-state",
42 | "tauri-plugin-state"
43 | ],
44 | "yaml.schemas": {
45 | "https://moonrepo.dev/schemas/workspace.json": ".moon/workspace.yml",
46 | "https://moonrepo.dev/schemas/project.json": "**/moon.yml",
47 | "https://moonrepo.dev/schemas/tasks.json": ".moon/tasks/*.yml"
48 | },
49 | "rust-analyzer.showUnlinkedFileNotification": false,
50 | "cSpell.words": [
51 | "glowsquid",
52 | "Mojang",
53 | "Pkce",
54 | "quickplay",
55 | "reqwest",
56 | "rusqlite",
57 | "Singleplayer",
58 | "specta",
59 | "Tauri",
60 | "typesafety"
61 | ]
62 | }
--------------------------------------------------------------------------------
/libs/ui/README.md:
--------------------------------------------------------------------------------
1 | # create-svelte
2 |
3 | Everything you need to build a Svelte library, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte).
4 |
5 | Read more about creating a library [in the docs](https://kit.svelte.dev/docs/packaging).
6 |
7 | ## Creating a project
8 |
9 | If you're seeing this, you've probably already done this step. Congrats!
10 |
11 | ```bash
12 | # create a new project in the current directory
13 | npm create svelte@latest
14 |
15 | # create a new project in my-app
16 | npm create svelte@latest my-app
17 | ```
18 |
19 | ## Developing
20 |
21 | Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
22 |
23 | ```bash
24 | npm run dev
25 |
26 | # or start the server and open the app in a new browser tab
27 | npm run dev -- --open
28 | ```
29 |
30 | Everything inside `src/lib` is part of your library, everything inside `src/routes` can be used as a showcase or preview app.
31 |
32 | ## Building
33 |
34 | To build your library:
35 |
36 | ```bash
37 | npm run package
38 | ```
39 |
40 | To create a production version of your showcase app:
41 |
42 | ```bash
43 | npm run build
44 | ```
45 |
46 | You can preview the production build with `npm run preview`.
47 |
48 | > To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment.
49 |
50 | ## Publishing
51 |
52 | Go into the `package.json` and give your package the desired name through the `"name"` option. Also consider adding a `"license"` field and point it to a `LICENSE` file which you can create from a template (one popular option is the [MIT license](https://opensource.org/license/mit/)).
53 |
54 | To publish your library to [npm](https://www.npmjs.com):
55 |
56 | ```bash
57 | npm publish
58 | ```
59 |
--------------------------------------------------------------------------------
/.github/workflows/codecov.yml:
--------------------------------------------------------------------------------
1 | name: Codecov
2 |
3 | on:
4 | push:
5 | branches: ['main', 'dev']
6 | pull_request:
7 | types: [opened, synchronize]
8 |
9 | env:
10 | MOONBASE_SECRET_KEY: ${{ secrets.MOONBASE_SECRET_KEY }}
11 | DO_NOT_TRACK: 1
12 |
13 | jobs:
14 | codecov:
15 | name: Test and Codecov
16 | runs-on: ubuntu-latest
17 | steps:
18 | - name: Check out code
19 | uses: actions/checkout@v4
20 | with:
21 | fetch-depth: 0
22 |
23 | - name: Install binstall
24 | uses: cargo-bins/cargo-binstall@main
25 |
26 | - uses: 'moonrepo/setup-toolchain@v0'
27 | with:
28 | auto-install: true
29 |
30 | - name: Get pnpm store directory
31 | shell: bash
32 | run: |
33 | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
34 |
35 | - uses: actions/cache@v4
36 | name: Cache Pnpm
37 | with:
38 | path: ${{ env.STORE_PATH }}
39 | key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
40 | restore-keys: |
41 | ${{ runner.os }}-pnpm-store-
42 |
43 | - name: Install playwright
44 | run: pnpm dlx playwright install --with-deps
45 |
46 | - name: install tauri dependencies
47 | run: |
48 | sudo apt-get update
49 | sudo apt-get install -y libwebkit2gtk-4.0-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
50 |
51 | - name: Test and Codecov
52 | run: 'pnpm moon run :coverage'
53 |
54 | - name: report to moon app
55 | uses: 'moonrepo/run-report-action@v1'
56 | if: success() || failure()
57 | with:
58 | access-token: ${{ secrets.GITHUB_TOKEN }}
59 |
60 | - name: Upload coverage reports to Codecov
61 | uses: codecov/codecov-action@v4.0.1
62 | with:
63 | token: ${{ secrets.CODECOV_TOKEN }}
64 |
--------------------------------------------------------------------------------
/apps/glowsquid-frontend/static/svelte.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/libs/ui/src/input/Input.svelte:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 | {#if startIcon}
16 |
17 | {@render startIcon()}
18 |
19 | {/if}
20 |
21 |
29 |
30 |
31 |
32 |
80 |
--------------------------------------------------------------------------------
/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | inputs = {
3 | nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
4 | flake-utils.url = "github:numtide/flake-utils";
5 |
6 | rust-overlay = {
7 | url = "github:oxalica/rust-overlay";
8 | inputs = {
9 | nixpkgs.follows = "nixpkgs";
10 | };
11 | };
12 | };
13 |
14 | outputs = {
15 | self,
16 | nixpkgs,
17 | flake-utils,
18 | rust-overlay,
19 | }:
20 | flake-utils.lib.eachDefaultSystem (system: let
21 | pkgs = import nixpkgs {
22 | inherit system;
23 | overlays = [(import rust-overlay)];
24 | };
25 |
26 | rustTC = pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml;
27 |
28 | libraries = with pkgs; [
29 | openssl_3
30 | pkg-config
31 | libGL
32 | mesa_drivers
33 | glfw-wayland
34 | libglvnd
35 | webkitgtk_4_1
36 | gtk3
37 | cairo
38 | gdk-pixbuf
39 | glib
40 | dbus
41 | librsvg
42 | libxkbcommon
43 | ];
44 |
45 | packages = with pkgs; [
46 | # binaries
47 | nodejs
48 | corepack
49 | rustTC
50 | cargo-watch
51 | cargo-binstall # just in case
52 | playwright-driver.browsers
53 | temurin-jre-bin-21
54 |
55 | # lsps
56 | nil
57 | nodePackages.svelte-language-server
58 | nodePackages.typescript-language-server
59 | marksman
60 | alejandra
61 | ];
62 | in {
63 | devShell = pkgs.mkShell {
64 | buildInputs = packages;
65 | nativeBuildInputs = libraries;
66 |
67 | PLAYWRIGHT_BROWSERS_PATH = pkgs.playwright-driver.browsers;
68 | PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS = true;
69 | LD_LIBRARY_PATH = pkgs.lib.makeLibraryPath libraries;
70 | PKG_CONFIG_PATH = pkgs.lib.makeLibraryPath libraries;
71 | RUST_SRC_PATH = "${rustTC}/lib/rustlib/src/rust/library/";
72 | };
73 | });
74 | }
75 |
--------------------------------------------------------------------------------
/libs/ui/src/button/Button.svelte:
--------------------------------------------------------------------------------
1 |
11 |
12 |
25 |
26 |
79 |
--------------------------------------------------------------------------------
/apps/glowsquid/src/main.rs:
--------------------------------------------------------------------------------
1 | // Prevents additional console window on Windows in release, DO NOT REMOVE!!
2 | #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
3 |
4 | mod auth;
5 | pub mod database;
6 | pub mod error;
7 |
8 | use auth::*;
9 | use specta::TypeCollection;
10 | use sqlx::SqlitePool;
11 | use tauri::{async_runtime::block_on, Manager};
12 |
13 | #[tauri::command]
14 | #[specta::specta]
15 | fn greet(name: &str) -> String {
16 | format!("Hello, {}! You've been greeted from Rust!", name)
17 | }
18 |
19 | fn main() {
20 | let invoke_handler = {
21 | let builder = tauri_specta::ts::builder()
22 | .types(TypeCollection::default().register::())
23 | .commands(tauri_specta::collect_commands![
24 | greet,
25 | add_account,
26 | remove_account,
27 | switch_to_account
28 | ]);
29 |
30 | #[cfg(debug_assertions)] // <- Only export on non-release builds
31 | let builder = builder.path("../glowsquid-frontend/src/lib/bindings.ts");
32 |
33 | builder.build().unwrap()
34 | };
35 |
36 | tauri::Builder::default()
37 | .setup(|app| {
38 | let data_path = app.path().app_data_dir()?;
39 | let db_path = data_path.join("glowsquid.db3");
40 |
41 | if !db_path.exists() {
42 | std::fs::create_dir_all(&data_path)?;
43 | std::fs::File::create(&db_path)?;
44 | }
45 |
46 | let db = block_on(async move { SqlitePool::connect(db_path.to_str().unwrap()).await })?;
47 |
48 | let db_state = block_on(database::DbState::new(db))?;
49 |
50 | app.manage(db_state);
51 |
52 | Ok(())
53 | })
54 | .plugin(tauri_plugin_state::init(|app| {
55 | let handle = app.app_handle().clone();
56 | if !handle.manage(block_on(auth::state::State::new(&handle))) {
57 | block_on(handle.state::().sync());
58 | }
59 | }))
60 | .invoke_handler(invoke_handler)
61 | .run(tauri::generate_context!())
62 | .expect("error while running tauri application");
63 | }
64 |
--------------------------------------------------------------------------------
/flake.lock:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": {
3 | "flake-utils": {
4 | "inputs": {
5 | "systems": "systems"
6 | },
7 | "locked": {
8 | "lastModified": 1710146030,
9 | "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
10 | "owner": "numtide",
11 | "repo": "flake-utils",
12 | "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
13 | "type": "github"
14 | },
15 | "original": {
16 | "owner": "numtide",
17 | "repo": "flake-utils",
18 | "type": "github"
19 | }
20 | },
21 | "nixpkgs": {
22 | "locked": {
23 | "lastModified": 1719506693,
24 | "narHash": "sha256-C8e9S7RzshSdHB7L+v9I51af1gDM5unhJ2xO1ywxNH8=",
25 | "owner": "nixos",
26 | "repo": "nixpkgs",
27 | "rev": "b2852eb9365c6de48ffb0dc2c9562591f652242a",
28 | "type": "github"
29 | },
30 | "original": {
31 | "owner": "nixos",
32 | "ref": "nixos-unstable",
33 | "repo": "nixpkgs",
34 | "type": "github"
35 | }
36 | },
37 | "root": {
38 | "inputs": {
39 | "flake-utils": "flake-utils",
40 | "nixpkgs": "nixpkgs",
41 | "rust-overlay": "rust-overlay"
42 | }
43 | },
44 | "rust-overlay": {
45 | "inputs": {
46 | "nixpkgs": [
47 | "nixpkgs"
48 | ]
49 | },
50 | "locked": {
51 | "lastModified": 1719627476,
52 | "narHash": "sha256-LBfULF+2sCaWmkjmj1LkkGrAS/E9ZdXU1A5wWKjt9p0=",
53 | "owner": "oxalica",
54 | "repo": "rust-overlay",
55 | "rev": "5be53be9e5c766fc72fc5d65ba8a566cc0c3217f",
56 | "type": "github"
57 | },
58 | "original": {
59 | "owner": "oxalica",
60 | "repo": "rust-overlay",
61 | "type": "github"
62 | }
63 | },
64 | "systems": {
65 | "locked": {
66 | "lastModified": 1681028828,
67 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
68 | "owner": "nix-systems",
69 | "repo": "default",
70 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
71 | "type": "github"
72 | },
73 | "original": {
74 | "owner": "nix-systems",
75 | "repo": "default",
76 | "type": "github"
77 | }
78 | }
79 | },
80 | "root": "root",
81 | "version": 7
82 | }
83 |
--------------------------------------------------------------------------------
/libs/eslint-config/index.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 |
3 | import eslint from '@eslint/js'
4 | import svelte from 'eslint-plugin-svelte'
5 | import typescript from 'typescript-eslint'
6 | import stylistic from '@stylistic/eslint-plugin'
7 | import svelteParser from 'svelte-eslint-parser'
8 |
9 | /** @type {import("eslint").Linter.FlatConfig[]} */
10 | // @ts-expect-error this is a valid eslint config
11 | export default [
12 | eslint.configs.recommended,
13 | ...typescript.configs.recommended,
14 | stylistic.configs.customize({
15 | flat: true,
16 | indent: 4,
17 | }),
18 | ({
19 | files: ['**/*.ts'],
20 | languageOptions: {
21 | parser: typescript.parser,
22 | parserOptions: {
23 | ecmaVersion: 'latest',
24 | extraFileExtensions: ['.svelte'],
25 | sourceType: 'module',
26 | },
27 | },
28 | rules: {
29 | 'no-undef': 'off',
30 | },
31 | }),
32 | ]
33 |
34 | /**
35 | * Creates a svelte eslint config
36 | *
37 | * @param {import('svelte-eslint-parser').SvelteConfig} svelteConfig
38 | * @returns {import("eslint").Linter.FlatConfig[]}
39 | * */
40 | export const createSvelteConfig = svelteConfig => ([
41 | ...svelte.configs['flat/recommended'].map(({ rules, ...rest }) => ({
42 | rules: {
43 | ...rules,
44 | },
45 | ...rest,
46 | })),
47 | {
48 | files: ['*.svelte', '**/*.svelte'],
49 | languageOptions: {
50 | parser: svelteParser,
51 | parserOptions: {
52 | parser: typescript.parser,
53 | svelteConfig,
54 | svelteFeatures: {
55 | runes: true,
56 | experimentalGenerics: true,
57 | },
58 | },
59 | },
60 | rules: {
61 | 'no-undef': 'off',
62 | },
63 | processor: svelte.processors.svelte,
64 | },
65 | {
66 | files: ['**/*.svelte.ts', '*.svelte.ts'],
67 | languageOptions: {
68 | parser: svelteParser,
69 | parserOptions: {
70 | parser: typescript.parser,
71 | svelteConfig,
72 | svelteFeatures: {
73 | runes: true,
74 | experimentalGenerics: true,
75 | },
76 | },
77 | },
78 | },
79 | ])
80 |
--------------------------------------------------------------------------------
/libs/copper/src/downloader.rs:
--------------------------------------------------------------------------------
1 | use error_stack::Result;
2 | use std::{
3 | error::Error,
4 | fmt::{self, Display, Formatter},
5 | future::Future,
6 | sync::Arc,
7 | };
8 |
9 | use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
10 |
11 | #[derive(Debug)]
12 | /// An error that can occur when downloading a file
13 | pub enum DownloadError {
14 | /// An error occured when requesting/downloading the file
15 | ReqwestError,
16 | /// An I/O error occured when writing or reading the file
17 | IoError,
18 | /// An error occured when joining the download task
19 | JoinError,
20 | /// An error occured during channel communication (such as sending progress updates)
21 | ChannelError,
22 | /// An error occured when extracting the downloaded file (if applicable)
23 | ExtractionError,
24 | }
25 |
26 | impl Display for DownloadError {
27 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
28 | f.write_str(match self {
29 | Self::ReqwestError => "Reqwest Error",
30 | Self::IoError => "I/O Error",
31 | Self::JoinError => "Join Error",
32 | Self::ChannelError => "Channel Error",
33 | Self::ExtractionError => "Extraction Error",
34 | })
35 | }
36 | }
37 |
38 | impl Error for DownloadError {}
39 |
40 | #[derive(Debug)]
41 | pub enum DownloadMessage {
42 | /// A file was successfully downloaded
43 | Downloaded(T),
44 | /// When you recieve this event, you can be sure that all file have been downloaded.
45 | /// This is only sent once. You must close the channel after this, else your program will hang
46 | DownloadedAll,
47 | /// (T, downloaded bytes)
48 | DownloadProgress(T, u64),
49 | }
50 |
51 | pub type DownloadChannelSender = UnboundedSender>;
52 | pub type DownloadChannelReceiver = UnboundedReceiver>;
53 |
54 | pub trait Downloader {
55 | type DownloadItem;
56 |
57 | /// Creates a channel for downloading
58 | fn create_channel(&mut self) -> DownloadChannelReceiver;
59 |
60 | /// Downloads all files for this specific downloader. Requires an Arc for multithreading
61 | /// purposes
62 | ///
63 | /// See the downloaders documentation for more information. Usually appends the path to the
64 | /// main directory.
65 | fn download_all(self: Arc) -> impl Future