From eed52135e7d6ffbbbd64070cf567bcf08653c7d5 Mon Sep 17 00:00:00 2001 From: HsiangNianian Date: Wed, 14 Jan 2026 18:15:31 +0800 Subject: feat: Enhance UI components and add visual effects - Updated Sidebar component styles for improved aesthetics and usability. - Refactored VersionsView component with a new layout and enhanced version filtering. - Improved DownloadMonitor and GameConsole components for better performance and visual consistency. - Added new settings for GPU acceleration and visual effects in settings store. - Introduced ParticleBackground component with customizable effects (Constellation and Saturn). - Implemented ConstellationEffect and SaturnEffect classes for dynamic background animations. --- ui/src/components/BottomBar.svelte | 79 ++++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 34 deletions(-) (limited to 'ui/src/components/BottomBar.svelte') diff --git a/ui/src/components/BottomBar.svelte b/ui/src/components/BottomBar.svelte index dcad9e8..dd218f3 100644 --- a/ui/src/components/BottomBar.svelte +++ b/ui/src/components/BottomBar.svelte @@ -5,18 +5,19 @@
-
+ +
authState.openLoginModal()} role="button" tabindex="0" onkeydown={(e) => e.key === "Enter" && authState.openLoginModal()} >
{#if authState.currentAccount} {:else} - ? + ? {/if}
-
- {authState.currentAccount ? authState.currentAccount.username : "Click to Login"} +
+ {authState.currentAccount ? authState.currentAccount.username : "Login"}
-
+
- {authState.currentAccount ? "Ready" : "Guest"} + {authState.currentAccount ? "Ready to play" : "Guest Mode"}
+ +
+
-
+ +
Selected Version - +
+ +
+
-- cgit v1.2.3-70-g09d2 From 74849ad2d18586736d9677dfd10af4875f4ef2ca Mon Sep 17 00:00:00 2001 From: HsiangNianian Date: Wed, 14 Jan 2026 18:40:01 +0800 Subject: feat: enhance dark mode support across UI components - Updated BottomBar, HomeView, LoginModal, ModLoaderSelector, SettingsView, Sidebar, StatusToast, and VersionsView components for improved dark mode styling. - Adjusted color schemes for various elements to ensure better visibility and aesthetics in dark mode. - Added a theme property to settings to enforce dark mode as the default. - Refactored version badges in VersionsView for better color differentiation. - Enhanced button and input styles for consistency in both light and dark themes. --- assets/image.jpg | Bin 172906 -> 161028 bytes src-tauri/src/core/config.rs | 2 + src-tauri/src/main.rs | 4 -- src-tauri/tauri.conf.json | 4 +- ui/src/App.svelte | 39 +++++++++++++------ ui/src/components/BottomBar.svelte | 20 +++++----- ui/src/components/HomeView.svelte | 10 ++--- ui/src/components/LoginModal.svelte | 30 +++++++-------- ui/src/components/ModLoaderSelector.svelte | 40 +++++++++---------- ui/src/components/SettingsView.svelte | 60 ++++++++++++++++++++--------- ui/src/components/Sidebar.svelte | 8 ++-- ui/src/components/StatusToast.svelte | 10 ++--- ui/src/components/VersionsView.svelte | 48 +++++++++++------------ ui/src/stores/settings.svelte.ts | 6 +++ ui/src/types/index.ts | 1 + 15 files changed, 163 insertions(+), 119 deletions(-) (limited to 'ui/src/components/BottomBar.svelte') diff --git a/assets/image.jpg b/assets/image.jpg index 28cffe1..ddda96f 100644 Binary files a/assets/image.jpg and b/assets/image.jpg differ diff --git a/src-tauri/src/core/config.rs b/src-tauri/src/core/config.rs index 27e0011..510b126 100644 --- a/src-tauri/src/core/config.rs +++ b/src-tauri/src/core/config.rs @@ -17,6 +17,7 @@ pub struct LauncherConfig { pub enable_gpu_acceleration: bool, pub enable_visual_effects: bool, pub active_effect: String, + pub theme: String, } impl Default for LauncherConfig { @@ -32,6 +33,7 @@ impl Default for LauncherConfig { enable_gpu_acceleration: false, enable_visual_effects: true, active_effect: "constellation".to_string(), + theme: "dark".to_string(), } } } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index f7a391a..8a955a5 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -854,8 +854,6 @@ async fn get_recommended_java( Ok(core::java::get_recommended_java(required_major_version)) } -// ==================== Fabric Loader Commands ==================== - /// Get Minecraft versions supported by Fabric #[tauri::command] async fn get_fabric_game_versions() -> Result, String> { @@ -949,8 +947,6 @@ async fn is_fabric_installed( )) } -// ==================== Forge Loader Commands ==================== - /// Get Minecraft versions supported by Forge #[tauri::command] async fn get_forge_game_versions() -> Result, String> { diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 9145e1b..682acb0 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -14,8 +14,8 @@ "title": "Minecraft DropOut Launcher", "width": 1024, "height": 768, - "minWidth": 800, - "minHeight": 600, + "minWidth": 905, + "minHeight": 575, "resizable": true } ], diff --git a/ui/src/App.svelte b/ui/src/App.svelte index d93374e..2160b85 100644 --- a/ui/src/App.svelte +++ b/ui/src/App.svelte @@ -31,12 +31,22 @@ onMount(async () => { authState.checkAccount(); - settingsState.loadSettings(); + await settingsState.loadSettings(); gameState.loadVersions(); getVersion().then((v) => (uiState.appVersion = v)); window.addEventListener("mousemove", handleMouseMove); }); + $effect(() => { + if (settingsState.settings.theme === 'light') { + document.documentElement.classList.remove('dark'); + document.documentElement.setAttribute('data-theme', 'light'); + } else { + document.documentElement.classList.add('dark'); + document.documentElement.setAttribute('data-theme', 'dark'); + } + }); + onDestroy(() => { if (typeof window !== 'undefined') window.removeEventListener("mousemove", handleMouseMove); @@ -44,10 +54,10 @@
-
+
{#if settingsState.settings.custom_background_path}
{:else if settingsState.settings.enable_visual_effects} - -
+ + {#if settingsState.settings.theme === 'dark'} +
+ {:else} + +
+ {/if} {#if uiState.currentView === "home"} {/if}
{/if} -
+
-
+
diff --git a/ui/src/components/BottomBar.svelte b/ui/src/components/BottomBar.svelte index dd218f3..0178111 100644 --- a/ui/src/components/BottomBar.svelte +++ b/ui/src/components/BottomBar.svelte @@ -5,7 +5,7 @@
@@ -17,7 +17,7 @@ onkeydown={(e) => e.key === "Enter" && authState.openLoginModal()} >
{#if authState.currentAccount}
-
+
{authState.currentAccount ? authState.currentAccount.username : "Login"}
-
+
{authState.currentAccount ? "Ready to play" : "Guest Mode"}
-
+
@@ -28,7 +28,7 @@
@@ -61,12 +61,12 @@ type="text" bind:value={authState.offlineUsername} placeholder="Offline Username" - class="w-full bg-zinc-950 border border-zinc-700 rounded p-3 text-white focus:border-indigo-500 outline-none" + class="w-full bg-gray-50 border-zinc-200 dark:bg-zinc-950 dark:border-zinc-700 rounded p-3 text-gray-900 dark:text-white focus:border-indigo-500 outline-none" onkeydown={(e) => e.key === "Enter" && authState.performOfflineLogin()} /> @@ -80,18 +80,18 @@
{:else if authState.deviceCodeData}
-

1. Go to this URL:

+

1. Go to this URL:

-

2. Enter this code:

+

2. Enter this code:

e.key === 'Enter' && navigator.clipboard.writeText(authState.deviceCodeData?.user_code || "")} @@ -106,8 +106,8 @@
-
- {authState.msLoginStatus} +
+ {authState.msLoginStatus}

This window will update automatically.

diff --git a/ui/src/components/ModLoaderSelector.svelte b/ui/src/components/ModLoaderSelector.svelte index 4a59916..fd26382 100644 --- a/ui/src/components/ModLoaderSelector.svelte +++ b/ui/src/components/ModLoaderSelector.svelte @@ -114,16 +114,16 @@
-

Select Mod Loader

+

Select Mod Loader

-
+
@@ -81,39 +81,39 @@ {/if}
-

+

Select an image from your computer to replace the default gradient background. Supported formats: PNG, JPG, WEBP, GIF.

-
+
-

Visual Effects

-

Enable particle effects and animated gradients. (Default: On)

+

Visual Effects

+

Enable particle effects and animated gradients. (Default: On)

{#if settingsState.settings.enable_visual_effects} -
+
-

Theme Effect

-

Select the active visual theme.

+

Theme Effect

+

Select the active visual theme.

-
+
{#each ['all', 'release', 'snapshot', 'modded'] as filter}
-
+
-
-
+
+ +
-- cgit v1.2.3-70-g09d2 From 31077dbd39a25eecd24a1dca0f8c9d1879265277 Mon Sep 17 00:00:00 2001 From: HsiangNianian Date: Thu, 15 Jan 2026 17:36:36 +0800 Subject: feat: Implement custom dropdown components for version selection in BottomBar and ModLoaderSelector, enhancing user interaction and UI consistency --- ui/src/app.css | 97 ++++++++++++++----- ui/src/components/BottomBar.svelte | 97 ++++++++++++++++--- ui/src/components/CustomSelect.svelte | 136 ++++++++++++++++++++++++++ ui/src/components/ModLoaderSelector.svelte | 150 ++++++++++++++++++++++++----- ui/src/components/SettingsView.svelte | 33 ++++--- ui/src/lib/GameConsole.svelte | 19 ++-- 6 files changed, 443 insertions(+), 89 deletions(-) create mode 100644 ui/src/components/CustomSelect.svelte (limited to 'ui/src/components/BottomBar.svelte') diff --git a/ui/src/app.css b/ui/src/app.css index 5d0404b..82aa72f 100644 --- a/ui/src/app.css +++ b/ui/src/app.css @@ -14,43 +14,48 @@ select { padding-right: 2rem; } -/* Custom scrollbar for dropdowns and lists */ -select, -.custom-select { - scrollbar-width: thin; - scrollbar-color: #3f3f46 #18181b; -} - -/* Webkit scrollbar for select (when expanded, browser-dependent) */ -select::-webkit-scrollbar { - width: 8px; +/* Option styling - works in WebView/Chromium */ +select option { + background-color: #18181b; + color: #e4e4e7; + padding: 12px 16px; + font-size: 13px; + border: none; } -select::-webkit-scrollbar-track { - background: #18181b; +select option:hover, +select option:focus { + background-color: #3730a3 !important; + color: white !important; } -select::-webkit-scrollbar-thumb { - background-color: #3f3f46; - border-radius: 4px; +select option:checked { + background: linear-gradient(0deg, #4f46e5 0%, #4f46e5 100%); + color: white; + font-weight: 500; } -select::-webkit-scrollbar-thumb:hover { - background-color: #52525b; +select option:disabled { + color: #52525b; + background-color: #18181b; } -/* Option styling (limited browser support but good for Tauri/WebView) */ -select option { +/* Optgroup styling */ +select optgroup { background-color: #18181b; - color: #e4e4e7; - padding: 8px 12px; + color: #a1a1aa; + font-weight: 600; + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.05em; + padding: 8px 12px 4px; } -select option:hover, -select option:focus, -select option:checked { - background: linear-gradient(#3730a3, #3730a3); - color: white; +/* Select focus state */ +select:focus { + outline: none; + border-color: #6366f1; + box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2); } /* ==================== Custom Scrollbar (Global) ==================== */ @@ -118,3 +123,43 @@ input[type="number"]::-webkit-inner-spin-button { input[type="number"] { -moz-appearance: textfield; } + +/* ==================== Checkbox Styling ==================== */ + +input[type="checkbox"] { + appearance: none; + width: 16px; + height: 16px; + border: 1px solid #3f3f46; + border-radius: 4px; + background-color: #18181b; + cursor: pointer; + position: relative; + transition: all 0.15s ease; +} + +input[type="checkbox"]:hover { + border-color: #52525b; +} + +input[type="checkbox"]:checked { + background-color: #4f46e5; + border-color: #4f46e5; +} + +input[type="checkbox"]:checked::after { + content: ''; + position: absolute; + left: 5px; + top: 2px; + width: 4px; + height: 8px; + border: solid white; + border-width: 0 2px 2px 0; + transform: rotate(45deg); +} + +input[type="checkbox"]:focus { + outline: none; + box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.3); +} diff --git a/ui/src/components/BottomBar.svelte b/ui/src/components/BottomBar.svelte index 198d4e6..abb0b23 100644 --- a/ui/src/components/BottomBar.svelte +++ b/ui/src/components/BottomBar.svelte @@ -2,7 +2,39 @@ import { authState } from "../stores/auth.svelte"; import { gameState } from "../stores/game.svelte"; import { uiState } from "../stores/ui.svelte"; - import { Terminal, ChevronDown, Play, User } from 'lucide-svelte'; + import { Terminal, ChevronDown, Play, User, Check } from 'lucide-svelte'; + + let isVersionDropdownOpen = $state(false); + let dropdownRef: HTMLDivElement; + + let versionOptions = $derived( + gameState.versions.length === 0 + ? [{ id: "loading", type: "loading", label: "Loading..." }] + : gameState.versions.map(v => ({ + ...v, + label: `${v.id}${v.type !== 'release' ? ` (${v.type})` : ''}` + })) + ); + + function selectVersion(id: string) { + if (id !== "loading") { + gameState.selectedVersion = id; + isVersionDropdownOpen = false; + } + } + + function handleClickOutside(e: MouseEvent) { + if (dropdownRef && !dropdownRef.contains(e.target as Node)) { + isVersionDropdownOpen = false; + } + } + + $effect(() => { + if (isVersionDropdownOpen) { + document.addEventListener('click', handleClickOutside); + return () => document.removeEventListener('click', handleClickOutside); + } + });
-
- -
- -
+
+ {/if}
diff --git a/ui/src/components/CustomSelect.svelte b/ui/src/components/CustomSelect.svelte new file mode 100644 index 0000000..2e89c75 --- /dev/null +++ b/ui/src/components/CustomSelect.svelte @@ -0,0 +1,136 @@ + + +
+ + + + + {#if isOpen} +
+ {#each options as option} + + {/each} +
+ {/if} +
diff --git a/ui/src/components/ModLoaderSelector.svelte b/ui/src/components/ModLoaderSelector.svelte index d0c1b59..cb949c5 100644 --- a/ui/src/components/ModLoaderSelector.svelte +++ b/ui/src/components/ModLoaderSelector.svelte @@ -6,7 +6,7 @@ ForgeVersion, ModLoaderType, } from "../types"; - import { Loader2, Download, AlertCircle, Check } from 'lucide-svelte'; + import { Loader2, Download, AlertCircle, Check, ChevronDown } from 'lucide-svelte'; interface Props { selectedGameVersion: string; @@ -23,10 +23,15 @@ // Fabric state let fabricLoaders = $state([]); let selectedFabricLoader = $state(""); + let isFabricDropdownOpen = $state(false); // Forge state let forgeVersions = $state([]); let selectedForgeVersion = $state(""); + let isForgeDropdownOpen = $state(false); + + let fabricDropdownRef = $state(null); + let forgeDropdownRef = $state(null); // Load mod loader versions when game version changes $effect(() => { @@ -111,6 +116,44 @@ loadModLoaderVersions(); } } + + function handleFabricClickOutside(e: MouseEvent) { + if (fabricDropdownRef && !fabricDropdownRef.contains(e.target as Node)) { + isFabricDropdownOpen = false; + } + } + + function handleForgeClickOutside(e: MouseEvent) { + if (forgeDropdownRef && !forgeDropdownRef.contains(e.target as Node)) { + isForgeDropdownOpen = false; + } + } + + $effect(() => { + if (isFabricDropdownOpen) { + document.addEventListener('click', handleFabricClickOutside); + return () => document.removeEventListener('click', handleFabricClickOutside); + } + }); + + $effect(() => { + if (isForgeDropdownOpen) { + document.addEventListener('click', handleForgeClickOutside); + return () => document.removeEventListener('click', handleForgeClickOutside); + } + }); + + let selectedFabricLabel = $derived( + fabricLoaders.find(l => l.version === selectedFabricLoader) + ? `${selectedFabricLoader}${fabricLoaders.find(l => l.version === selectedFabricLoader)?.stable ? ' (stable)' : ''}` + : selectedFabricLoader || 'Select version' + ); + + let selectedForgeLabel = $derived( + forgeVersions.find(v => v.version === selectedForgeVersion) + ? `${selectedForgeVersion}${forgeVersions.find(v => v.version === selectedForgeVersion)?.recommended ? ' (Recommended)' : ''}` + : selectedForgeVersion || 'Select version' + );
@@ -163,18 +206,48 @@ -
- + {selectedFabricLabel} + + + + {#if isFabricDropdownOpen} +
+ {#each fabricLoaders as loader} + + {/each} +
+ {/if}
@@ -199,19 +272,48 @@ -
- + {selectedForgeLabel} + + + + {#if isForgeDropdownOpen} +
+ {#each forgeVersions as version} + + {/each} +
+ {/if}
diff --git a/ui/src/components/SettingsView.svelte b/ui/src/components/SettingsView.svelte index 732f857..76d441b 100644 --- a/ui/src/components/SettingsView.svelte +++ b/ui/src/components/SettingsView.svelte @@ -1,11 +1,22 @@