diff --git a/.vscode/settings.json b/.vscode/settings.json
index 75acb0da..8beb3b56 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -23,7 +23,7 @@
"**/.crates": true,
"**/routeTree.gen.ts": true
},
- "rust-analyzer.procMacro.enable": false,
+ "rust-analyzer.procMacro.enable": true,
"rust-analyzer.procMacro.attributes.enable": false,
"rust-analyzer.procMacro.ignored": {
"async-trait": ["async_trait"],
diff --git a/Cargo.lock b/Cargo.lock
index d2d8816c..2108adba 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3344,6 +3344,35 @@ version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
+[[package]]
+name = "oneclient_gui"
+version = "0.2.0-alpha.2"
+dependencies = [
+ "anyhow",
+ "chrono",
+ "interpulse",
+ "onelauncher_core",
+ "serde",
+ "serde_json",
+ "specta",
+ "specta-typescript",
+ "strum 0.27.1",
+ "tauri",
+ "tauri-build",
+ "tauri-plugin-clipboard-manager",
+ "tauri-plugin-deep-link",
+ "tauri-plugin-dialog",
+ "tauri-plugin-single-instance",
+ "tauri-plugin-updater",
+ "tauri-plugin-window-state",
+ "tauri-specta",
+ "thiserror 2.0.12",
+ "tokio",
+ "tracing",
+ "tracing-error",
+ "uuid 1.16.0",
+]
+
[[package]]
name = "onelauncher_core"
version = "0.2.0-alpha.2"
diff --git a/Cargo.toml b/Cargo.toml
index 85c5c90d..84fb4494 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,5 +1,6 @@
[workspace]
members = [
+ "apps/oneclient/desktop",
"apps/onelauncher/desktop",
"packages/core",
"packages/entity",
diff --git a/apps/oneclient/desktop/.gitignore b/apps/oneclient/desktop/.gitignore
new file mode 100644
index 00000000..787194aa
--- /dev/null
+++ b/apps/oneclient/desktop/.gitignore
@@ -0,0 +1,3 @@
+# Generated by Tauri
+.cargo/
+/gen
diff --git a/apps/oneclient/desktop/App.entitlements b/apps/oneclient/desktop/App.entitlements
new file mode 100644
index 00000000..48f4264c
--- /dev/null
+++ b/apps/oneclient/desktop/App.entitlements
@@ -0,0 +1,18 @@
+
+
+
+
+
+ com.apple.security.cs.disable-library-validation
+
+
+ com.apple.security.cs.allow-dyld-environment-variables
+
+
+ com.apple.security.device.audio-input
+
+
+ com.apple.security.device.camera
+
+
+
diff --git a/apps/oneclient/desktop/Cargo.toml b/apps/oneclient/desktop/Cargo.toml
new file mode 100644
index 00000000..dc1368bf
--- /dev/null
+++ b/apps/oneclient/desktop/Cargo.toml
@@ -0,0 +1,80 @@
+[package]
+name = "oneclient_gui"
+default-run = "oneclient_gui"
+description = "Next-generation open source Minecraft launcher"
+version = { workspace = true }
+license = { workspace = true }
+edition = { workspace = true }
+repository = { workspace = true }
+documentation = { workspace = true }
+readme = { workspace = true }
+homepage = { workspace = true }
+authors = { workspace = true }
+
+[lib]
+name = "oneclient_gui"
+crate-type = [
+ "staticlib",
+ "cdylib",
+ "lib"
+]
+
+[lints]
+workspace = true
+
+[build-dependencies]
+tauri-build = { workspace = true }
+
+[dependencies]
+# core
+onelauncher_core = { workspace = true, features = [ "tauri" ] }
+interpulse = { workspace = true }
+
+# tauri
+tauri-plugin-window-state = { workspace = true }
+tauri-plugin-single-instance = { workspace = true }
+tauri-plugin-updater = { workspace = true }
+tauri-plugin-clipboard-manager = { workspace = true }
+tauri-plugin-dialog = { workspace = true }
+tauri-plugin-deep-link = { workspace = true }
+
+# code gen
+tauri-specta = { workspace = true }
+specta = { workspace = true }
+specta-typescript = { workspace = true }
+strum = { workspace = true }
+
+# logging
+tracing = { workspace = true }
+tracing-error = { workspace = true }
+
+# serialization & text
+serde = { workspace = true }
+serde_json = { workspace = true }
+chrono = { workspace = true }
+uuid = { workspace = true }
+
+# io
+tokio = { workspace = true }
+
+# utils
+thiserror = { workspace = true }
+anyhow = { workspace = true }
+
+[dependencies.tauri]
+workspace = true
+features = [
+ "macos-private-api",
+ "image-ico",
+ "image-png",
+ "protocol-asset",
+ "specta"
+]
+
+[features]
+default = [
+ "custom-protocol",
+ "devtools"
+]
+devtools = [ "tauri/devtools" ]
+custom-protocol = [ "tauri/custom-protocol" ]
diff --git a/apps/oneclient/desktop/Info.plist b/apps/oneclient/desktop/Info.plist
new file mode 100644
index 00000000..041bdfca
--- /dev/null
+++ b/apps/oneclient/desktop/Info.plist
@@ -0,0 +1,24 @@
+
+
+
+
+
+ CFBundleURLTypes
+
+
+ CFBundleURLName
+ OneClient
+ CFBundleURLSchemes
+
+ oneclient
+
+
+
+
+ NSCameraUsageDescription
+ A Minecraft mod is requesting access to your camera.
+
+ NSMicrophoneUsageDescription
+ A Minecraft mod is requesting access to your microphone.
+
+
diff --git a/apps/oneclient/desktop/build.rs b/apps/oneclient/desktop/build.rs
new file mode 100644
index 00000000..106b7123
--- /dev/null
+++ b/apps/oneclient/desktop/build.rs
@@ -0,0 +1,3 @@
+fn main() {
+ tauri_build::build();
+}
diff --git a/apps/oneclient/desktop/capabilities/default.json b/apps/oneclient/desktop/capabilities/default.json
new file mode 100644
index 00000000..40d2165e
--- /dev/null
+++ b/apps/oneclient/desktop/capabilities/default.json
@@ -0,0 +1,36 @@
+{
+ "$schema": "../gen/schemas/desktop-schema.json",
+ "identifier": "default",
+ "description": "capabilities for the main window",
+ "windows": ["*"],
+ "permissions": [
+ "core:app:default",
+ "core:webview:default",
+ "core:image:default",
+ "core:menu:default",
+ "core:path:default",
+ "core:resources:default",
+ "core:tray:default",
+ "core:window:default",
+
+ "core:window:allow-close",
+ "core:window:allow-create",
+ "core:window:allow-destroy",
+ "core:window:allow-start-dragging",
+ "core:window:allow-minimize",
+ "core:window:allow-maximize",
+ "core:window:allow-toggle-maximize",
+ "core:window:allow-internal-toggle-maximize",
+ "core:window:allow-set-decorations",
+ "core:window:allow-set-title-bar-style",
+ "core:webview:allow-internal-toggle-devtools",
+
+ "core:event:default",
+ "clipboard-manager:allow-write-text",
+ "dialog:allow-open",
+ "dialog:allow-save",
+ "dialog:allow-confirm",
+ "deep-link:default",
+ "updater:default"
+ ]
+}
diff --git a/apps/oneclient/desktop/package.json b/apps/oneclient/desktop/package.json
new file mode 100644
index 00000000..bb196947
--- /dev/null
+++ b/apps/oneclient/desktop/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "@oneclient/desktop",
+ "type": "module",
+ "version": "0.1.0-alpha.2",
+ "private": true,
+ "description": "Next-generation open source Minecraft launcher",
+ "license": "GPL-3.0-only",
+ "repository": {
+ "type": "git",
+ "url": "git@github.com:polyfrost/onelauncher",
+ "directory": "apps/desktop"
+ },
+ "bugs": "https://github.com/polyfrost/onelauncher/issues",
+ "sideEffects": false,
+ "scripts": {
+ "build": "tauri build",
+ "dev": "tauri dev",
+ "tauri": "tauri",
+ "tauri-ext": "pnpm --filter @onelauncher/scripts -- tauri"
+ },
+ "dependencies": {
+ "@oneclient/frontend": "workspace:*"
+ },
+ "devDependencies": {
+ "@tauri-apps/cli": "^2.0.4"
+ }
+}
diff --git a/apps/oneclient/desktop/src/api/commands.rs b/apps/oneclient/desktop/src/api/commands.rs
new file mode 100644
index 00000000..48fa8e68
--- /dev/null
+++ b/apps/oneclient/desktop/src/api/commands.rs
@@ -0,0 +1,30 @@
+pub mod cluster;
+pub mod users;
+pub mod processor;
+pub mod package;
+pub mod other;
+pub mod settings;
+
+#[macro_export(local_inner_macros)]
+macro_rules! collect_commands {
+ () => {{
+ use $crate::api::commands::*;
+ tauri_specta::collect_commands![
+ users::get_users,
+ users::get_user,
+ users::remove_user,
+ users::get_default_user,
+ users::set_default_user,
+ users::begin_ms_flow,
+
+ cluster::create_cluster,
+ cluster::get_clusters,
+ cluster::get_cluster_by_id,
+
+ settings::get_global_profile,
+ settings::get_profile_or_default,
+
+ other::open_dev_tools,
+ ]
+ }};
+}
diff --git a/apps/oneclient/desktop/src/api/commands/cluster.rs b/apps/oneclient/desktop/src/api/commands/cluster.rs
new file mode 100644
index 00000000..1bb53684
--- /dev/null
+++ b/apps/oneclient/desktop/src/api/commands/cluster.rs
@@ -0,0 +1,40 @@
+use onelauncher_core::{api::{cluster, setting_profiles}, entity::{clusters::Model, icon::Icon, loader::GameLoader}, migration::sea_orm};
+use serde::{Deserialize, Serialize};
+use specta::Type;
+use crate::api::error::SerializableResult;
+
+#[derive(Serialize, Deserialize, Type)]
+pub struct CreateCluster {
+ name: String,
+ mc_version: String,
+ mc_loader: GameLoader,
+ mc_loader_version: Option,
+ icon_url: Option
+}
+
+#[specta::specta]
+#[tauri::command]
+pub async fn create_cluster(options: CreateCluster) -> SerializableResult {
+ let thing = cluster::create_cluster(&options.name, &options.mc_version, options.mc_loader, options.mc_loader_version.as_deref(), options.icon_url).await?;
+
+ if setting_profiles::dao::get_profile_by_name(&options.name).await?.is_none() {
+ setting_profiles::create_profile(&options.name, async |mut profile| {
+ profile.mem_max = sea_orm::ActiveValue::Set(Some(2048));
+ Ok(profile)
+ }).await?;
+ }
+
+ Ok(thing)
+}
+
+#[specta::specta]
+#[tauri::command]
+pub async fn get_clusters() -> SerializableResult> {
+ Ok(cluster::dao::get_all_clusters().await?)
+}
+
+#[specta::specta]
+#[tauri::command]
+pub async fn get_cluster_by_id(id: i64) -> SerializableResult