From 64b939e6ac0b196d18ee183a37a40b0bf7927a80 Mon Sep 17 00:00:00 2001 From: Natsuu Date: Wed, 14 Jan 2026 03:41:18 +0000 Subject: refactor: split App.svelte into components --- ui/src/App.svelte | 793 ++-------------------------------- ui/src/components/BottomBar.svelte | 86 ++++ ui/src/components/HomeView.svelte | 26 ++ ui/src/components/LoginModal.svelte | 126 ++++++ ui/src/components/SettingsView.svelte | 125 ++++++ ui/src/components/Sidebar.svelte | 66 +++ ui/src/components/StatusToast.svelte | 36 ++ ui/src/components/VersionsView.svelte | 34 ++ ui/src/stores/auth.svelte.ts | 141 ++++++ ui/src/stores/game.svelte.ts | 48 ++ ui/src/stores/settings.svelte.ts | 56 +++ ui/src/stores/ui.svelte.ts | 34 ++ ui/src/types/index.ts | 36 ++ 13 files changed, 844 insertions(+), 763 deletions(-) create mode 100644 ui/src/components/BottomBar.svelte create mode 100644 ui/src/components/HomeView.svelte create mode 100644 ui/src/components/LoginModal.svelte create mode 100644 ui/src/components/SettingsView.svelte create mode 100644 ui/src/components/Sidebar.svelte create mode 100644 ui/src/components/StatusToast.svelte create mode 100644 ui/src/components/VersionsView.svelte create mode 100644 ui/src/stores/auth.svelte.ts create mode 100644 ui/src/stores/game.svelte.ts create mode 100644 ui/src/stores/settings.svelte.ts create mode 100644 ui/src/stores/ui.svelte.ts create mode 100644 ui/src/types/index.ts (limited to 'ui') diff --git a/ui/src/App.svelte b/ui/src/App.svelte index 9b7fe93..3750f11 100644 --- a/ui/src/App.svelte +++ b/ui/src/App.svelte @@ -1,372 +1,36 @@
- - +
@@ -381,417 +45,20 @@
- {#if currentView === "home"} - -
-
- -
-

- MINECRAFT -

-
- JAVA EDITION - Release 1.20.4 -
-
- {:else if currentView === "versions"} -
-

Versions

-
- {#if versions.length === 0} -
Loading versions...
- {:else} - {#each versions as version} - - {/each} - {/if} -
-
- {:else if currentView === "settings"} -
-

Settings

- -
- -
- -
- - -
- - {#if javaInstallations.length > 0} -
-

Detected Java Installations:

- {#each javaInstallations as java} - - {/each} -
- {/if} - -

- The command or path to the Java Runtime Environment. Click "Auto Detect" to find installed Java versions. -

-
- - -
- - -
-
- - -
-
- - -
-
-
- - -
- -
-
- - -
-
- - -
-
-
- -
- -
-
-
+ {#if uiState.currentView === "home"} + + {:else if uiState.currentView === "versions"} + + {:else if uiState.currentView === "settings"} + {/if}
- -
-
-
e.key === "Enter" && openLoginModal()} - > -
- {#if currentAccount} - {currentAccount.username} - {:else} - ? - {/if} -
-
-
- {currentAccount ? currentAccount.username : "Click to Login"} -
-
- - {currentAccount ? "Ready" : "Guest"} -
-
-
- - -
- -
-
- - -
- - -
-
+
- - {#if isLoginModalOpen} -
-
-
-

Login

- -
- - {#if loginMode === "select"} -
- - -
-
-
-
-
- OR -
-
- -
- e.key === "Enter" && performOfflineLogin()} - /> - -
-
- {:else if loginMode === "microsoft"} -
- {#if msLoginLoading && !deviceCodeData} -
- Starting login flow... -
- {:else if deviceCodeData} -
-

1. Go to this URL:

- - -

2. Enter this code:

-
- navigator.clipboard.writeText( - deviceCodeData?.user_code || "" - )} - > - {deviceCodeData.user_code} -
-

Click code to copy

- -
-
-
- {msLoginStatus} -
-

This window will update automatically.

-
- - -
- {/if} -
- {/if} -
-
- {/if} - - - {#if status !== "Ready"} -
-
-
Status
- -
-
{status}
-
-
-
-
- {/if} - - + + - +
diff --git a/ui/src/components/BottomBar.svelte b/ui/src/components/BottomBar.svelte new file mode 100644 index 0000000..ceba5b3 --- /dev/null +++ b/ui/src/components/BottomBar.svelte @@ -0,0 +1,86 @@ + + +
+
+
authState.openLoginModal()} + role="button" + tabindex="0" + onkeydown={(e) => e.key === "Enter" && authState.openLoginModal()} + > +
+ {#if authState.currentAccount} + {authState.currentAccount.username} + {:else} + ? + {/if} +
+
+
+ {authState.currentAccount ? authState.currentAccount.username : "Click to Login"} +
+
+ + {authState.currentAccount ? "Ready" : "Guest"} +
+
+
+ + +
+ +
+
+ + +
+ + +
+
diff --git a/ui/src/components/HomeView.svelte b/ui/src/components/HomeView.svelte new file mode 100644 index 0000000..e876c14 --- /dev/null +++ b/ui/src/components/HomeView.svelte @@ -0,0 +1,26 @@ + + + +
+
+ +
+

+ MINECRAFT +

+
+ JAVA EDITION + Release 1.20.4 +
+
diff --git a/ui/src/components/LoginModal.svelte b/ui/src/components/LoginModal.svelte new file mode 100644 index 0000000..f1ac0d5 --- /dev/null +++ b/ui/src/components/LoginModal.svelte @@ -0,0 +1,126 @@ + + +{#if authState.isLoginModalOpen} +
+
+
+

Login

+ +
+ + {#if authState.loginMode === "select"} +
+ + +
+
+
+
+
+ OR +
+
+ +
+ e.key === "Enter" && authState.performOfflineLogin()} + /> + +
+
+ {:else if authState.loginMode === "microsoft"} +
+ {#if authState.msLoginLoading && !authState.deviceCodeData} +
+ Starting login flow... +
+ {:else if authState.deviceCodeData} +
+

1. Go to this URL:

+ + +

2. Enter this code:

+
e.key === 'Enter' && navigator.clipboard.writeText(authState.deviceCodeData?.user_code || "")} + onclick={() => + navigator.clipboard.writeText( + authState.deviceCodeData?.user_code || "" + )} + > + {authState.deviceCodeData.user_code} +
+

Click code to copy

+ +
+
+
+ {authState.msLoginStatus} +
+

This window will update automatically.

+
+ + +
+ {/if} +
+ {/if} +
+
+{/if} diff --git a/ui/src/components/SettingsView.svelte b/ui/src/components/SettingsView.svelte new file mode 100644 index 0000000..f09be4e --- /dev/null +++ b/ui/src/components/SettingsView.svelte @@ -0,0 +1,125 @@ + + +
+

Settings

+ +
+ +
+ +
+ + +
+ + {#if settingsState.javaInstallations.length > 0} +
+

Detected Java Installations:

+ {#each settingsState.javaInstallations as java} + + {/each} +
+ {/if} + +

+ The command or path to the Java Runtime Environment. Click "Auto Detect" to find installed Java versions. +

+
+ + +
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+ +
+
+ + +
+
+ + +
+
+
+ +
+ +
+
+
diff --git a/ui/src/components/Sidebar.svelte b/ui/src/components/Sidebar.svelte new file mode 100644 index 0000000..a4f4e35 --- /dev/null +++ b/ui/src/components/Sidebar.svelte @@ -0,0 +1,66 @@ + + + diff --git a/ui/src/components/StatusToast.svelte b/ui/src/components/StatusToast.svelte new file mode 100644 index 0000000..b1feffc --- /dev/null +++ b/ui/src/components/StatusToast.svelte @@ -0,0 +1,36 @@ + + +{#if uiState.status !== "Ready"} +
+
+
Status
+ +
+
{uiState.status}
+
+
+
+
+{/if} + + diff --git a/ui/src/components/VersionsView.svelte b/ui/src/components/VersionsView.svelte new file mode 100644 index 0000000..8c0ddfe --- /dev/null +++ b/ui/src/components/VersionsView.svelte @@ -0,0 +1,34 @@ + + +
+

Versions

+
+ {#if gameState.versions.length === 0} +
Loading versions...
+ {:else} + {#each gameState.versions as version} + + {/each} + {/if} +
+
diff --git a/ui/src/stores/auth.svelte.ts b/ui/src/stores/auth.svelte.ts new file mode 100644 index 0000000..9df8835 --- /dev/null +++ b/ui/src/stores/auth.svelte.ts @@ -0,0 +1,141 @@ +import { invoke } from "@tauri-apps/api/core"; +import { open } from "@tauri-apps/plugin-shell"; +import type { Account, DeviceCodeResponse } from "../types"; +import { uiState } from "./ui.svelte"; + +export class AuthState { + currentAccount = $state(null); + isLoginModalOpen = $state(false); + loginMode = $state<"select" | "offline" | "microsoft">("select"); + offlineUsername = $state(""); + deviceCodeData = $state(null); + msLoginLoading = $state(false); + msLoginStatus = $state("Waiting for authorization..."); + + private pollInterval: any; + private isPollingRequestActive = false; + + async checkAccount() { + try { + const acc = await invoke("get_active_account"); + this.currentAccount = acc as Account | null; + } catch (e) { + console.error("Failed to check account:", e); + } + } + + openLoginModal() { + if (this.currentAccount) { + if (confirm("Logout " + this.currentAccount.username + "?")) { + invoke("logout").then(() => (this.currentAccount = null)); + } + return; + } + this.resetLoginState(); + this.isLoginModalOpen = true; + } + + closeLoginModal() { + this.stopPolling(); + this.isLoginModalOpen = false; + } + + resetLoginState() { + this.loginMode = "select"; + this.offlineUsername = ""; + this.deviceCodeData = null; + this.msLoginLoading = false; + } + + async performOfflineLogin() { + if (!this.offlineUsername) return; + try { + this.currentAccount = (await invoke("login_offline", { + username: this.offlineUsername, + })) as Account; + this.isLoginModalOpen = false; + } catch (e) { + alert("Login failed: " + e); + } + } + + async startMicrosoftLogin() { + this.loginMode = "microsoft"; + this.msLoginLoading = true; + this.msLoginStatus = "Waiting for authorization..."; + this.stopPolling(); + + try { + this.deviceCodeData = (await invoke( + "start_microsoft_login" + )) as DeviceCodeResponse; + + if (this.deviceCodeData) { + try { + await navigator.clipboard.writeText(this.deviceCodeData.user_code); + } catch (e) { + console.error("Clipboard failed", e); + } + + open(this.deviceCodeData.verification_uri); + + console.log("Starting polling for token..."); + const intervalMs = (this.deviceCodeData.interval || 5) * 1000; + this.pollInterval = setInterval( + () => this.checkLoginStatus(this.deviceCodeData!.device_code), + intervalMs + ); + } + } catch (e) { + alert("Failed to start Microsoft login: " + e); + this.loginMode = "select"; + } finally { + this.msLoginLoading = false; + } + } + + stopPolling() { + if (this.pollInterval) { + clearInterval(this.pollInterval); + this.pollInterval = null; + } + } + + async checkLoginStatus(deviceCode: string) { + if (this.isPollingRequestActive) return; + this.isPollingRequestActive = true; + + console.log("Polling Microsoft API..."); + try { + this.currentAccount = (await invoke("complete_microsoft_login", { + deviceCode, + })) as Account; + + console.log("Login Successful!", this.currentAccount); + this.stopPolling(); + this.isLoginModalOpen = false; + uiState.setStatus("Welcome back, " + this.currentAccount.username); + } catch (e: any) { + const errStr = e.toString(); + if (errStr.includes("authorization_pending")) { + console.log("Status: Waiting for user to authorize..."); + } else { + console.error("Polling Error:", errStr); + this.msLoginStatus = "Error: " + errStr; + + if ( + errStr.includes("expired_token") || + errStr.includes("access_denied") + ) { + this.stopPolling(); + alert("Login failed: " + errStr); + this.loginMode = "select"; + } + } + } finally { + this.isPollingRequestActive = false; + } + } +} + +export const authState = new AuthState(); diff --git a/ui/src/stores/game.svelte.ts b/ui/src/stores/game.svelte.ts new file mode 100644 index 0000000..feaf8d6 --- /dev/null +++ b/ui/src/stores/game.svelte.ts @@ -0,0 +1,48 @@ +import { invoke } from "@tauri-apps/api/core"; +import type { Version } from "../types"; +import { uiState } from "./ui.svelte"; +import { authState } from "./auth.svelte"; + +export class GameState { + versions = $state([]); + selectedVersion = $state(""); + + async loadVersions() { + try { + this.versions = await invoke("get_versions"); + if (this.versions.length > 0) { + const latest = this.versions.find((v) => v.type === "release"); + this.selectedVersion = latest ? latest.id : this.versions[0].id; + } + } catch (e) { + console.error("Failed to fetch versions:", e); + uiState.setStatus("Error fetching versions: " + e); + } + } + + async startGame() { + if (!authState.currentAccount) { + alert("Please login first!"); + authState.openLoginModal(); + return; + } + + if (!this.selectedVersion) { + alert("Please select a version!"); + return; + } + + uiState.setStatus("Preparing to launch " + this.selectedVersion + "..."); + console.log("Invoking start_game for version:", this.selectedVersion); + try { + const msg = await invoke("start_game", { versionId: this.selectedVersion }); + console.log("Response:", msg); + uiState.setStatus(msg as string); + } catch (e) { + console.error(e); + uiState.setStatus("Error: " + e); + } + } +} + +export const gameState = new GameState(); diff --git a/ui/src/stores/settings.svelte.ts b/ui/src/stores/settings.svelte.ts new file mode 100644 index 0000000..a1f687c --- /dev/null +++ b/ui/src/stores/settings.svelte.ts @@ -0,0 +1,56 @@ +import { invoke } from "@tauri-apps/api/core"; +import type { LauncherConfig, JavaInstallation } from "../types"; +import { uiState } from "./ui.svelte"; + +export class SettingsState { + settings = $state({ + min_memory: 1024, + max_memory: 2048, + java_path: "java", + width: 854, + height: 480, + }); + javaInstallations = $state([]); + isDetectingJava = $state(false); + + async loadSettings() { + try { + this.settings = await invoke("get_settings"); + } catch (e) { + console.error("Failed to load settings:", e); + } + } + + async saveSettings() { + try { + await invoke("save_settings", { config: this.settings }); + uiState.setStatus("Settings saved!"); + } catch (e) { + console.error("Failed to save settings:", e); + uiState.setStatus("Error saving settings: " + e); + } + } + + async detectJava() { + this.isDetectingJava = true; + try { + this.javaInstallations = await invoke("detect_java"); + if (this.javaInstallations.length === 0) { + uiState.setStatus("No Java installations found"); + } else { + uiState.setStatus(`Found ${this.javaInstallations.length} Java installation(s)`); + } + } catch (e) { + console.error("Failed to detect Java:", e); + uiState.setStatus("Error detecting Java: " + e); + } finally { + this.isDetectingJava = false; + } + } + + selectJava(path: string) { + this.settings.java_path = path; + } +} + +export const settingsState = new SettingsState(); diff --git a/ui/src/stores/ui.svelte.ts b/ui/src/stores/ui.svelte.ts new file mode 100644 index 0000000..8fc339b --- /dev/null +++ b/ui/src/stores/ui.svelte.ts @@ -0,0 +1,34 @@ +export class UIState { + currentView = $state("home"); + status = $state("Ready"); + showConsole = $state(false); + appVersion = $state("..."); + + private statusTimeout: any; + + constructor() { + // Watch for status changes to auto-dismiss + $effect(() => { + if (this.status !== "Ready") { + if (this.statusTimeout) clearTimeout(this.statusTimeout); + this.statusTimeout = setTimeout(() => { + this.status = "Ready"; + }, 5000); + } + }); + } + + setStatus(msg: string) { + this.status = msg; + } + + toggleConsole() { + this.showConsole = !this.showConsole; + } + + setView(view: string) { + this.currentView = view; + } +} + +export const uiState = new UIState(); diff --git a/ui/src/types/index.ts b/ui/src/types/index.ts new file mode 100644 index 0000000..dc0915b --- /dev/null +++ b/ui/src/types/index.ts @@ -0,0 +1,36 @@ +export interface Version { + id: string; + type: string; + url: string; + time: string; + releaseTime: string; +} + +export interface Account { + type: "Offline" | "Microsoft"; + username: string; + uuid: string; +} + +export interface DeviceCodeResponse { + user_code: string; + device_code: string; + verification_uri: string; + expires_in: number; + interval: number; + message?: string; +} + +export interface LauncherConfig { + min_memory: number; + max_memory: number; + java_path: string; + width: number; + height: number; +} + +export interface JavaInstallation { + path: string; + version: string; + is_64bit: boolean; +} -- cgit v1.2.3-70-g09d2 From caeec0304d0f6c592b6ce24d8c6c932eea6a5225 Mon Sep 17 00:00:00 2001 From: Natsuu Date: Wed, 14 Jan 2026 03:46:48 +0000 Subject: chore: improve loadSettings method for better result handling --- ui/src/stores/settings.svelte.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'ui') diff --git a/ui/src/stores/settings.svelte.ts b/ui/src/stores/settings.svelte.ts index a1f687c..9bc122e 100644 --- a/ui/src/stores/settings.svelte.ts +++ b/ui/src/stores/settings.svelte.ts @@ -15,7 +15,10 @@ export class SettingsState { async loadSettings() { try { - this.settings = await invoke("get_settings"); + const result = await invoke("get_settings"); + if (result && typeof result === "object") { + this.settings = result as LauncherConfig; + } } catch (e) { console.error("Failed to load settings:", e); } -- cgit v1.2.3-70-g09d2 From 956b15693ebe52a75d6b27c59bd14b47764d0110 Mon Sep 17 00:00:00 2001 From: Natsuu Date: Wed, 14 Jan 2026 03:48:55 +0000 Subject: chore: specify type for pollInterval in AuthState class --- ui/src/stores/auth.svelte.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'ui') diff --git a/ui/src/stores/auth.svelte.ts b/ui/src/stores/auth.svelte.ts index 9df8835..3d58245 100644 --- a/ui/src/stores/auth.svelte.ts +++ b/ui/src/stores/auth.svelte.ts @@ -12,7 +12,7 @@ export class AuthState { msLoginLoading = $state(false); msLoginStatus = $state("Waiting for authorization..."); - private pollInterval: any; + private pollInterval: ReturnType | null = null; private isPollingRequestActive = false; async checkAccount() { -- cgit v1.2.3-70-g09d2 From 66f401fd1248ce8d1624ec82342af5e07c51554e Mon Sep 17 00:00:00 2001 From: Natsuu Date: Wed, 14 Jan 2026 03:52:30 +0000 Subject: refactor: enhance UIState class by adding type for currentView and updating setView method --- ui/src/stores/ui.svelte.ts | 26 ++++++++++++-------------- ui/src/types/index.ts | 2 ++ 2 files changed, 14 insertions(+), 14 deletions(-) (limited to 'ui') diff --git a/ui/src/stores/ui.svelte.ts b/ui/src/stores/ui.svelte.ts index 8fc339b..b010390 100644 --- a/ui/src/stores/ui.svelte.ts +++ b/ui/src/stores/ui.svelte.ts @@ -1,32 +1,30 @@ +import { type ViewType } from "../types"; + export class UIState { - currentView = $state("home"); + currentView: ViewType = $state("home"); status = $state("Ready"); showConsole = $state(false); appVersion = $state("..."); private statusTimeout: any; - constructor() { - // Watch for status changes to auto-dismiss - $effect(() => { - if (this.status !== "Ready") { - if (this.statusTimeout) clearTimeout(this.statusTimeout); - this.statusTimeout = setTimeout(() => { - this.status = "Ready"; - }, 5000); - } - }); - } - setStatus(msg: string) { + if (this.statusTimeout) clearTimeout(this.statusTimeout); + this.status = msg; + + if (msg !== "Ready") { + this.statusTimeout = setTimeout(() => { + this.status = "Ready"; + }, 5000); + } } toggleConsole() { this.showConsole = !this.showConsole; } - setView(view: string) { + setView(view: ViewType) { this.currentView = view; } } diff --git a/ui/src/types/index.ts b/ui/src/types/index.ts index dc0915b..b7ff0a0 100644 --- a/ui/src/types/index.ts +++ b/ui/src/types/index.ts @@ -1,3 +1,5 @@ +export type ViewType = "home" | "versions" | "settings"; + export interface Version { id: string; type: string; -- cgit v1.2.3-70-g09d2 From 4675607a34f57e3a1bd6f3cf065079aa5aca40f1 Mon Sep 17 00:00:00 2001 From: Natsuu Date: Wed, 14 Jan 2026 03:54:44 +0000 Subject: refactor: specify generic type for invoke calls in GameState class --- ui/src/stores/game.svelte.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'ui') diff --git a/ui/src/stores/game.svelte.ts b/ui/src/stores/game.svelte.ts index feaf8d6..0af3daf 100644 --- a/ui/src/stores/game.svelte.ts +++ b/ui/src/stores/game.svelte.ts @@ -9,7 +9,7 @@ export class GameState { async loadVersions() { try { - this.versions = await invoke("get_versions"); + this.versions = await invoke("get_versions"); if (this.versions.length > 0) { const latest = this.versions.find((v) => v.type === "release"); this.selectedVersion = latest ? latest.id : this.versions[0].id; @@ -35,9 +35,9 @@ export class GameState { uiState.setStatus("Preparing to launch " + this.selectedVersion + "..."); console.log("Invoking start_game for version:", this.selectedVersion); try { - const msg = await invoke("start_game", { versionId: this.selectedVersion }); + const msg = await invoke("start_game", { versionId: this.selectedVersion }); console.log("Response:", msg); - uiState.setStatus(msg as string); + uiState.setStatus(msg); } catch (e) { console.error(e); uiState.setStatus("Error: " + e); -- cgit v1.2.3-70-g09d2 From 90a4cdf980b801570c001cd5c75b8aad46bcfb47 Mon Sep 17 00:00:00 2001 From: Natsuu Date: Wed, 14 Jan 2026 03:56:55 +0000 Subject: chore: add for attributes to labels in BottomBar and SettingsView components --- ui/src/components/BottomBar.svelte | 2 ++ ui/src/components/SettingsView.svelte | 24 ++++++++++++++---------- 2 files changed, 16 insertions(+), 10 deletions(-) (limited to 'ui') diff --git a/ui/src/components/BottomBar.svelte b/ui/src/components/BottomBar.svelte index ceba5b3..dcad9e8 100644 --- a/ui/src/components/BottomBar.svelte +++ b/ui/src/components/BottomBar.svelte @@ -54,10 +54,12 @@
- + >Memory Allocation (RAM)
-
-
- + >Game Window Size
- +
- + Date: Wed, 14 Jan 2026 12:01:45 +0800 Subject: fix: avoid any/object process Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> --- ui/src/stores/settings.svelte.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'ui') diff --git a/ui/src/stores/settings.svelte.ts b/ui/src/stores/settings.svelte.ts index 9bc122e..989172c 100644 --- a/ui/src/stores/settings.svelte.ts +++ b/ui/src/stores/settings.svelte.ts @@ -15,10 +15,8 @@ export class SettingsState { async loadSettings() { try { - const result = await invoke("get_settings"); - if (result && typeof result === "object") { - this.settings = result as LauncherConfig; - } + const result = await invoke("get_settings"); + this.settings = result; } catch (e) { console.error("Failed to load settings:", e); } -- cgit v1.2.3-70-g09d2 From e636559dbc509a305dc372887fd1549322092d72 Mon Sep 17 00:00:00 2001 From: 简律纯 Date: Wed, 14 Jan 2026 12:03:04 +0800 Subject: chore: add ReturnType type hint Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com> --- ui/src/stores/ui.svelte.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'ui') diff --git a/ui/src/stores/ui.svelte.ts b/ui/src/stores/ui.svelte.ts index b010390..9c29c25 100644 --- a/ui/src/stores/ui.svelte.ts +++ b/ui/src/stores/ui.svelte.ts @@ -5,8 +5,8 @@ export class UIState { status = $state("Ready"); showConsole = $state(false); appVersion = $state("..."); - - private statusTimeout: any; + + private statusTimeout: ReturnType | null = null; setStatus(msg: string) { if (this.statusTimeout) clearTimeout(this.statusTimeout); -- cgit v1.2.3-70-g09d2