From 97fe5046f68b5e4ee5f750945bcc39a27f5eb37b Mon Sep 17 00:00:00 2001
From: 苏向夜 <fu050409@163.com>
Date: Sun, 29 Mar 2026 00:54:21 +0800
Subject: chore(ui): refactor effect instance check

---
 packages/ui/src/pages/home-view.tsx | 94 +++++--------------------------------
 1 file changed, 11 insertions(+), 83 deletions(-)

(limited to 'packages/ui/src/pages/home-view.tsx')

diff --git a/packages/ui/src/pages/home-view.tsx b/packages/ui/src/pages/home-view.tsx
index 4f80cb0..6060370 100644
--- a/packages/ui/src/pages/home-view.tsx
+++ b/packages/ui/src/pages/home-view.tsx
@@ -1,18 +1,11 @@
-import { useEffect, useState } from "react";
+import { useState } from "react";
 import { BottomBar } from "@/components/bottom-bar";
-import type { SaturnEffect } from "@/lib/effects/SaturnEffect";
-import { useGameStore } from "../stores/game-store";
-import { useReleasesStore } from "../stores/releases-store";
+import { useSaturnEffect } from "@/components/particle-background";
 
 export function HomeView() {
-  const gameStore = useGameStore();
-  const releasesStore = useReleasesStore();
   const [mouseX, setMouseX] = useState(0);
   const [mouseY, setMouseY] = useState(0);
-
-  useEffect(() => {
-    releasesStore.loadReleases();
-  }, [releasesStore.loadReleases]);
+  const saturn = useSaturnEffect();
 
   const handleMouseMove = (e: React.MouseEvent) => {
     const x = (e.clientX / window.innerWidth) * 2 - 1;
@@ -21,100 +14,42 @@ export function HomeView() {
     setMouseY(y);
 
     // Forward mouse move to SaturnEffect (if available) for parallax/rotation interactions
-    try {
-      const saturn = (
-        window as unknown as {
-          getSaturnEffect?: () => SaturnEffect;
-        }
-      ).getSaturnEffect?.();
-      if (saturn?.handleMouseMove) {
-        saturn.handleMouseMove(e.clientX);
-      }
-    } catch {
-      /* best-effort, ignore errors from effect */
-    }
+    saturn?.handleMouseMove(e.clientX);
   };
 
   const handleSaturnMouseDown = (e: React.MouseEvent) => {
-    try {
-      const saturn = (window as any).getSaturnEffect?.();
-      if (saturn?.handleMouseDown) {
-        saturn.handleMouseDown(e.clientX);
-      }
-    } catch {
-      /* ignore */
-    }
+    saturn?.handleMouseDown(e.clientX);
   };
 
   const handleSaturnMouseUp = () => {
-    try {
-      const saturn = (window as any).getSaturnEffect?.();
-      if (saturn?.handleMouseUp) {
-        saturn.handleMouseUp();
-      }
-    } catch {
-      /* ignore */
-    }
+    saturn?.handleMouseUp();
   };
 
   const handleSaturnMouseLeave = () => {
     // Treat leaving the area as mouse-up for the effect
-    try {
-      const saturn = (window as any).getSaturnEffect?.();
-      if (saturn?.handleMouseUp) {
-        saturn.handleMouseUp();
-      }
-    } catch {
-      /* ignore */
-    }
+    saturn?.handleMouseUp();
   };
 
   const handleSaturnTouchStart = (e: React.TouchEvent) => {
     if (e.touches && e.touches.length === 1) {
-      try {
         const clientX = e.touches[0].clientX;
-        const saturn = (window as any).getSaturnEffect?.();
-        if (saturn?.handleTouchStart) {
-          saturn.handleTouchStart(clientX);
-        }
-      } catch {
-        /* ignore */
-      }
+        saturn?.handleTouchStart(clientX);
     }
   };
 
   const handleSaturnTouchMove = (e: React.TouchEvent) => {
     if (e.touches && e.touches.length === 1) {
-      try {
         const clientX = e.touches[0].clientX;
-        const saturn = (window as any).getSaturnEffect?.();
-        if (saturn?.handleTouchMove) {
-          saturn.handleTouchMove(clientX);
-        }
-      } catch {
-        /* ignore */
-      }
+        saturn?.handleTouchMove(clientX);
     }
   };
 
   const handleSaturnTouchEnd = () => {
-    try {
-      const saturn = (window as any).getSaturnEffect?.();
-      if (saturn?.handleTouchEnd) {
-        saturn.handleTouchEnd();
-      }
-    } catch {
-      /* ignore */
-    }
+      saturn?.handleTouchEnd();
   };
 
   return (
-    <div
-      className="relative z-10 h-full overflow-y-auto custom-scrollbar scroll-smooth"
-      style={{
-        overflow: releasesStore.isLoading ? "hidden" : "auto",
-      }}
-    >
+    <div className="relative z-10 h-full overflow-y-auto custom-scrollbar scroll-smooth">
       {/* Hero Section (Full Height) - Interactive area */}
       <div
         role="tab"
@@ -150,13 +85,6 @@ export function HomeView() {
             <div className="bg-white/10 backdrop-blur-md border border-white/10 px-3 py-1 rounded-sm text-xs font-bold uppercase tracking-widest text-white shadow-sm">
               Java Edition
             </div>
-            <div className="h-4 w-px bg-white/20"></div>
-            <div className="text-sm text-zinc-400">
-              Latest Release{" "}
-              <span className="text-white font-medium">
-                {gameStore.latestRelease?.id || "..."}
-              </span>
-            </div>
           </div>
         </div>
 
-- 
cgit v1.2.3-70-g09d2


From 397cbb34b327a0addfdf8e36f859b456956b66fe Mon Sep 17 00:00:00 2001
From: 苏向夜 <fu050409@163.com>
Date: Sun, 29 Mar 2026 01:19:25 +0800
Subject: fix(lint): apply ui code lint

---
 packages/ui/src/components/bottom-bar.tsx            | 14 ++++++++++----
 packages/ui/src/components/instance-editor-modal.tsx | 15 +++++++++------
 packages/ui/src/models/auth.ts                       |  5 ++++-
 packages/ui/src/models/instance.ts                   |  5 ++++-
 packages/ui/src/pages/home-view.tsx                  | 10 +++++-----
 packages/ui/src/pages/index.tsx                      |  7 ++++++-
 packages/ui/src/pages/instances-view.tsx             | 16 ++++++++++++----
 packages/ui/src/stores/game-store.ts                 |  4 +++-
 8 files changed, 53 insertions(+), 23 deletions(-)

(limited to 'packages/ui/src/pages/home-view.tsx')

diff --git a/packages/ui/src/components/bottom-bar.tsx b/packages/ui/src/components/bottom-bar.tsx
index 8f70985..2746e00 100644
--- a/packages/ui/src/components/bottom-bar.tsx
+++ b/packages/ui/src/components/bottom-bar.tsx
@@ -21,13 +21,17 @@ export function BottomBar() {
   const account = useAuthStore((state) => state.account);
   const instances = useInstanceStore((state) => state.instances);
   const activeInstance = useInstanceStore((state) => state.activeInstance);
-  const setActiveInstance = useInstanceStore((state) => state.setActiveInstance);
+  const setActiveInstance = useInstanceStore(
+    (state) => state.setActiveInstance,
+  );
   const selectedVersion = useGameStore((state) => state.selectedVersion);
   const setSelectedVersion = useGameStore((state) => state.setSelectedVersion);
   const startGame = useGameStore((state) => state.startGame);
   const stopGame = useGameStore((state) => state.stopGame);
   const runningInstanceId = useGameStore((state) => state.runningInstanceId);
-  const launchingInstanceId = useGameStore((state) => state.launchingInstanceId);
+  const launchingInstanceId = useGameStore(
+    (state) => state.launchingInstanceId,
+  );
   const stoppingInstanceId = useGameStore((state) => state.stoppingInstanceId);
 
   const [showLoginModal, setShowLoginModal] = useState(false);
@@ -39,7 +43,7 @@ export function BottomBar() {
     }
 
     setSelectedVersion(nextVersion);
-  }, [activeInstance?.id, activeInstance?.versionId, selectedVersion, setSelectedVersion]);
+  }, [activeInstance?.versionId, selectedVersion, setSelectedVersion]);
 
   const handleInstanceChange = useCallback(
     async (instanceId: string) => {
@@ -47,7 +51,9 @@ export function BottomBar() {
         return;
       }
 
-      const nextInstance = instances.find((instance) => instance.id === instanceId);
+      const nextInstance = instances.find(
+        (instance) => instance.id === instanceId,
+      );
       if (!nextInstance) {
         return;
       }
diff --git a/packages/ui/src/components/instance-editor-modal.tsx b/packages/ui/src/components/instance-editor-modal.tsx
index 2a2bd7d..105d7e9 100644
--- a/packages/ui/src/components/instance-editor-modal.tsx
+++ b/packages/ui/src/components/instance-editor-modal.tsx
@@ -1,8 +1,12 @@
-
 import { toNumber } from "es-toolkit/compat";
 import { Folder, Loader2, Save, Trash2, X } from "lucide-react";
 import { useCallback, useEffect, useState } from "react";
 import { toast } from "sonner";
+import {
+  deleteInstanceFile,
+  listInstanceDirectory,
+  openFileExplorer,
+} from "@/client";
 import { Button } from "@/components/ui/button";
 import {
   Dialog,
@@ -18,7 +22,6 @@ import { useInstanceStore } from "@/models/instance";
 import { useSettingsStore } from "@/models/settings";
 import type { FileInfo } from "../types/bindings/core";
 import type { Instance } from "../types/bindings/instance";
-import { deleteInstanceFile, listInstanceDirectory, openFileExplorer } from "@/client";
 
 type Props = {
   open: boolean;
@@ -97,7 +100,7 @@ export function InstanceEditorModal({ open, instance, onOpenChange }: Props) {
         setFileList(files);
       } catch (err) {
         console.error("Failed to load files:", err);
-        toast.error("Failed to load files: " + String(err));
+        toast.error(`Failed to load files: ${String(err)}`);
         setFileList([]);
       } finally {
         setLoadingFiles(false);
@@ -137,7 +140,7 @@ export function InstanceEditorModal({ open, instance, onOpenChange }: Props) {
       toast.success("Deleted");
     } catch (err) {
       console.error("Failed to delete file:", err);
-      toast.error("Failed to delete file: " + String(err));
+      toast.error(`Failed to delete file: ${String(err)}`);
     } finally {
       setDeletingPath(null);
     }
@@ -148,7 +151,7 @@ export function InstanceEditorModal({ open, instance, onOpenChange }: Props) {
       await openFileExplorer(filePath);
     } catch (err) {
       console.error("Failed to open in explorer:", err);
-      toast.error("Failed to open file explorer: " + String(err));
+      toast.error(`Failed to open file explorer: ${String(err)}`);
     }
   }
 
@@ -180,7 +183,7 @@ export function InstanceEditorModal({ open, instance, onOpenChange }: Props) {
     } catch (err) {
       console.error("Failed to save instance:", err);
       setErrorMessage(String(err));
-      toast.error("Failed to save instance: " + String(err));
+      toast.error(`Failed to save instance: ${String(err)}`);
     } finally {
       setSaving(false);
     }
diff --git a/packages/ui/src/models/auth.ts b/packages/ui/src/models/auth.ts
index 9c814d2..d64b67a 100644
--- a/packages/ui/src/models/auth.ts
+++ b/packages/ui/src/models/auth.ts
@@ -95,7 +95,10 @@ export const useAuthStore = create<AuthState>((set, get) => ({
     } catch (error) {
       const message = getAuthErrorMessage(error);
       console.error("Failed to start Microsoft login:", error);
-      set({ loginMode: null, statusMessage: `Failed to start login: ${message}` });
+      set({
+        loginMode: null,
+        statusMessage: `Failed to start login: ${message}`,
+      });
       toast.error(`Failed to start Microsoft login: ${message}`);
     }
   },
diff --git a/packages/ui/src/models/instance.ts b/packages/ui/src/models/instance.ts
index e1eb7c1..2f338b5 100644
--- a/packages/ui/src/models/instance.ts
+++ b/packages/ui/src/models/instance.ts
@@ -26,7 +26,10 @@ interface InstanceState {
   setActiveInstance: (instance: Instance) => Promise<void>;
   duplicate: (id: string, newName: string) => Promise<Instance | null>;
   exportArchive: (id: string, archivePath: string) => Promise<void>;
-  importArchive: (archivePath: string, newName?: string) => Promise<Instance | null>;
+  importArchive: (
+    archivePath: string,
+    newName?: string,
+  ) => Promise<Instance | null>;
   repair: () => Promise<void>;
   get: (id: string) => Promise<Instance | null>;
 }
diff --git a/packages/ui/src/pages/home-view.tsx b/packages/ui/src/pages/home-view.tsx
index 6060370..da7238f 100644
--- a/packages/ui/src/pages/home-view.tsx
+++ b/packages/ui/src/pages/home-view.tsx
@@ -32,20 +32,20 @@ export function HomeView() {
 
   const handleSaturnTouchStart = (e: React.TouchEvent) => {
     if (e.touches && e.touches.length === 1) {
-        const clientX = e.touches[0].clientX;
-        saturn?.handleTouchStart(clientX);
+      const clientX = e.touches[0].clientX;
+      saturn?.handleTouchStart(clientX);
     }
   };
 
   const handleSaturnTouchMove = (e: React.TouchEvent) => {
     if (e.touches && e.touches.length === 1) {
-        const clientX = e.touches[0].clientX;
-        saturn?.handleTouchMove(clientX);
+      const clientX = e.touches[0].clientX;
+      saturn?.handleTouchMove(clientX);
     }
   };
 
   const handleSaturnTouchEnd = () => {
-      saturn?.handleTouchEnd();
+    saturn?.handleTouchEnd();
   };
 
   return (
diff --git a/packages/ui/src/pages/index.tsx b/packages/ui/src/pages/index.tsx
index bccca22..2acd377 100644
--- a/packages/ui/src/pages/index.tsx
+++ b/packages/ui/src/pages/index.tsx
@@ -22,7 +22,12 @@ export function IndexPage() {
     void initGameLifecycle().catch((error) => {
       console.error("Failed to initialize game lifecycle:", error);
     });
-  }, [authStore.init, settingsStore.refresh, instanceStore.refresh, initGameLifecycle]);
+  }, [
+    authStore.init,
+    settingsStore.refresh,
+    instanceStore.refresh,
+    initGameLifecycle,
+  ]);
 
   return (
     <div className="relative h-screen w-full overflow-hidden bg-background font-sans">
diff --git a/packages/ui/src/pages/instances-view.tsx b/packages/ui/src/pages/instances-view.tsx
index 07a2135..7bb3302 100644
--- a/packages/ui/src/pages/instances-view.tsx
+++ b/packages/ui/src/pages/instances-view.tsx
@@ -35,7 +35,9 @@ export function InstancesView() {
   const startGame = useGameStore((state) => state.startGame);
   const stopGame = useGameStore((state) => state.stopGame);
   const runningInstanceId = useGameStore((state) => state.runningInstanceId);
-  const launchingInstanceId = useGameStore((state) => state.launchingInstanceId);
+  const launchingInstanceId = useGameStore(
+    (state) => state.launchingInstanceId,
+  );
   const stoppingInstanceId = useGameStore((state) => state.stoppingInstanceId);
   const [isImporting, setIsImporting] = useState(false);
   const [repairing, setRepairing] = useState(false);
@@ -191,7 +193,8 @@ export function InstancesView() {
             const isRunning = runningInstanceId === instance.id;
             const isLaunching = launchingInstanceId === instance.id;
             const isStopping = stoppingInstanceId === instance.id;
-            const otherInstanceRunning = runningInstanceId !== null && !isRunning;
+            const otherInstanceRunning =
+              runningInstanceId !== null && !isRunning;
 
             return (
               <li
@@ -259,7 +262,10 @@ export function InstancesView() {
                           try {
                             await instancesStore.setActiveInstance(instance);
                           } catch (error) {
-                            console.error("Failed to set active instance:", error);
+                            console.error(
+                              "Failed to set active instance:",
+                              error,
+                            );
                             toast.error("Error setting active instance");
                             return;
                           }
@@ -284,7 +290,9 @@ export function InstancesView() {
                             () => undefined,
                           );
                         }}
-                        disabled={otherInstanceRunning || isLaunching || isStopping}
+                        disabled={
+                          otherInstanceRunning || isLaunching || isStopping
+                        }
                       >
                         {isLaunching || isStopping ? (
                           <span className="text-xs">...</span>
diff --git a/packages/ui/src/stores/game-store.ts b/packages/ui/src/stores/game-store.ts
index 1eaf7e7..7b6e746 100644
--- a/packages/ui/src/stores/game-store.ts
+++ b/packages/ui/src/stores/game-store.ts
@@ -70,7 +70,9 @@ export const useGameStore = create<GameState>((set, get) => ({
       });
 
       if (wasStopped) {
-        toast.success(`Stopped Minecraft ${versionId} for instance ${instanceId}`);
+        toast.success(
+          `Stopped Minecraft ${versionId} for instance ${instanceId}`,
+        );
       } else {
         toast.info(`Minecraft ${versionId} exited for instance ${instanceId}`);
       }
-- 
cgit v1.2.3-70-g09d2


From ffbfce895c37e8e8306d426a2e59e73647ed6a86 Mon Sep 17 00:00:00 2001
From: 苏向夜 <fu050409@163.com>
Date: Sun, 29 Mar 2026 02:46:51 +0800
Subject: refactor(ui): rewrite game store

---
 .changes/game-store.md                    |   5 +
 packages/ui/src/components/bottom-bar.tsx |  31 +-
 packages/ui/src/models/game.ts            | 113 ++++++++
 packages/ui/src/pages/home-view.tsx       | 102 -------
 packages/ui/src/pages/home.tsx            | 102 +++++++
 packages/ui/src/pages/index.tsx           |  12 +-
 packages/ui/src/pages/instances-view.tsx  | 457 ------------------------------
 packages/ui/src/pages/routes.ts           |   6 +-
 packages/ui/src/stores/game-store.ts      | 184 ------------
 9 files changed, 236 insertions(+), 776 deletions(-)
 create mode 100644 .changes/game-store.md
 create mode 100644 packages/ui/src/models/game.ts
 delete mode 100644 packages/ui/src/pages/home-view.tsx
 create mode 100644 packages/ui/src/pages/home.tsx
 delete mode 100644 packages/ui/src/pages/instances-view.tsx
 delete mode 100644 packages/ui/src/stores/game-store.ts

(limited to 'packages/ui/src/pages/home-view.tsx')

diff --git a/.changes/game-store.md b/.changes/game-store.md
new file mode 100644
index 0000000..0b2f7f6
--- /dev/null
+++ b/.changes/game-store.md
@@ -0,0 +1,5 @@
+---
+"@dropout/ui": "patch:refactor"
+---
+
+Refactor game store and rename `HomePage` component.
diff --git a/packages/ui/src/components/bottom-bar.tsx b/packages/ui/src/components/bottom-bar.tsx
index 2746e00..fd4a681 100644
--- a/packages/ui/src/components/bottom-bar.tsx
+++ b/packages/ui/src/components/bottom-bar.tsx
@@ -3,8 +3,8 @@ import { useCallback, useEffect, useState } from "react";
 import { toast } from "sonner";
 import { cn } from "@/lib/utils";
 import { useAuthStore } from "@/models/auth";
+import { useGameStore } from "@/models/game";
 import { useInstanceStore } from "@/models/instance";
-import { useGameStore } from "@/stores/game-store";
 import { LoginModal } from "./login-modal";
 import { Button } from "./ui/button";
 import {
@@ -19,21 +19,17 @@ import { Spinner } from "./ui/spinner";
 
 export function BottomBar() {
   const account = useAuthStore((state) => state.account);
-  const instances = useInstanceStore((state) => state.instances);
-  const activeInstance = useInstanceStore((state) => state.activeInstance);
-  const setActiveInstance = useInstanceStore(
-    (state) => state.setActiveInstance,
-  );
-  const selectedVersion = useGameStore((state) => state.selectedVersion);
-  const setSelectedVersion = useGameStore((state) => state.setSelectedVersion);
-  const startGame = useGameStore((state) => state.startGame);
-  const stopGame = useGameStore((state) => state.stopGame);
-  const runningInstanceId = useGameStore((state) => state.runningInstanceId);
-  const launchingInstanceId = useGameStore(
-    (state) => state.launchingInstanceId,
-  );
-  const stoppingInstanceId = useGameStore((state) => state.stoppingInstanceId);
 
+  const { instances, activeInstance, setActiveInstance } = useInstanceStore();
+  const {
+    runningInstanceId,
+    launchingInstanceId,
+    stoppingInstanceId,
+    startGame,
+    stopGame,
+  } = useGameStore();
+
+  const [selectedVersion, setSelectedVersion] = useState<string | null>(null);
   const [showLoginModal, setShowLoginModal] = useState(false);
 
   useEffect(() => {
@@ -43,7 +39,7 @@ export function BottomBar() {
     }
 
     setSelectedVersion(nextVersion);
-  }, [activeInstance?.versionId, selectedVersion, setSelectedVersion]);
+  }, [activeInstance?.versionId, selectedVersion]);
 
   const handleInstanceChange = useCallback(
     async (instanceId: string) => {
@@ -75,11 +71,8 @@ export function BottomBar() {
     }
 
     await startGame(
-      account,
-      () => setShowLoginModal(true),
       activeInstance.id,
       selectedVersion || activeInstance.versionId,
-      () => undefined,
     );
   };
 
diff --git a/packages/ui/src/models/game.ts b/packages/ui/src/models/game.ts
new file mode 100644
index 0000000..5342078
--- /dev/null
+++ b/packages/ui/src/models/game.ts
@@ -0,0 +1,113 @@
+import { listen, type UnlistenFn } from "@tauri-apps/api/event";
+import { toast } from "sonner";
+import { create } from "zustand";
+import {
+  startGame as startGameCommand,
+  stopGame as stopGameCommand,
+} from "@/client";
+import type { GameExitedEvent } from "@/types/bindings/core";
+
+interface GameState {
+  runningInstanceId: string | null;
+  runningVersionId: string | null;
+  launchingInstanceId: string | null;
+  stoppingInstanceId: string | null;
+  lifecycleUnlisten: UnlistenFn | null;
+
+  isGameRunning: boolean;
+  startGame: (instanceId: string, versionId: string) => Promise<string | null>;
+  stopGame: (instanceId?: string | null) => Promise<string | null>;
+}
+
+export const useGameStore = create<GameState>((set, get) => ({
+  runningInstanceId: null,
+  runningVersionId: null,
+  launchingInstanceId: null,
+  stoppingInstanceId: null,
+  lifecycleUnlisten: null,
+
+  get isGameRunning() {
+    return get().runningInstanceId !== null;
+  },
+
+  startGame: async (instanceId, versionId) => {
+    const { isGameRunning, lifecycleUnlisten } = get();
+
+    if (isGameRunning) {
+      toast.info("A game is already running");
+      return null;
+    } else {
+      lifecycleUnlisten?.();
+    }
+
+    set({
+      launchingInstanceId: instanceId,
+    });
+    toast.info(`Preparing to launch ${versionId}...`);
+
+    const unlisten = await listen<GameExitedEvent>("game-exited", (event) => {
+      const { instanceId, versionId, wasStopped, exitCode } = event.payload;
+
+      set({
+        runningInstanceId: null,
+        runningVersionId: null,
+        launchingInstanceId: null,
+        stoppingInstanceId: null,
+      });
+
+      if (wasStopped) {
+        toast.success(
+          `Stopped Minecraft ${versionId} for instance ${instanceId}`,
+        );
+      } else {
+        toast.info(
+          `Minecraft ${versionId} exited with code ${exitCode} for instance ${instanceId}`,
+        );
+      }
+    });
+
+    set({ lifecycleUnlisten: unlisten });
+
+    try {
+      const message = await startGameCommand(instanceId, versionId);
+      set({
+        launchingInstanceId: null,
+        runningInstanceId: instanceId,
+        runningVersionId: versionId,
+      });
+      toast.success(message);
+      return message;
+    } catch (e) {
+      console.error(e);
+      set({ launchingInstanceId: null });
+      toast.error(`Error: ${e}`);
+      return null;
+    }
+  },
+
+  stopGame: async (instanceId) => {
+    const { runningInstanceId } = get();
+
+    if (!runningInstanceId) {
+      toast.info("No running game found");
+      return null;
+    }
+
+    if (instanceId !== runningInstanceId) {
+      toast.info("That instance is not the one currently running");
+      return null;
+    }
+
+    set({ stoppingInstanceId: runningInstanceId });
+
+    try {
+      return await stopGameCommand();
+    } catch (e) {
+      console.error("Failed to stop game:", e);
+      toast.error(`Failed to stop game: ${e}`);
+      return null;
+    } finally {
+      set({ stoppingInstanceId: null });
+    }
+  },
+}));
diff --git a/packages/ui/src/pages/home-view.tsx b/packages/ui/src/pages/home-view.tsx
deleted file mode 100644
index da7238f..0000000
--- a/packages/ui/src/pages/home-view.tsx
+++ /dev/null
@@ -1,102 +0,0 @@
-import { useState } from "react";
-import { BottomBar } from "@/components/bottom-bar";
-import { useSaturnEffect } from "@/components/particle-background";
-
-export function HomeView() {
-  const [mouseX, setMouseX] = useState(0);
-  const [mouseY, setMouseY] = useState(0);
-  const saturn = useSaturnEffect();
-
-  const handleMouseMove = (e: React.MouseEvent) => {
-    const x = (e.clientX / window.innerWidth) * 2 - 1;
-    const y = (e.clientY / window.innerHeight) * 2 - 1;
-    setMouseX(x);
-    setMouseY(y);
-
-    // Forward mouse move to SaturnEffect (if available) for parallax/rotation interactions
-    saturn?.handleMouseMove(e.clientX);
-  };
-
-  const handleSaturnMouseDown = (e: React.MouseEvent) => {
-    saturn?.handleMouseDown(e.clientX);
-  };
-
-  const handleSaturnMouseUp = () => {
-    saturn?.handleMouseUp();
-  };
-
-  const handleSaturnMouseLeave = () => {
-    // Treat leaving the area as mouse-up for the effect
-    saturn?.handleMouseUp();
-  };
-
-  const handleSaturnTouchStart = (e: React.TouchEvent) => {
-    if (e.touches && e.touches.length === 1) {
-      const clientX = e.touches[0].clientX;
-      saturn?.handleTouchStart(clientX);
-    }
-  };
-
-  const handleSaturnTouchMove = (e: React.TouchEvent) => {
-    if (e.touches && e.touches.length === 1) {
-      const clientX = e.touches[0].clientX;
-      saturn?.handleTouchMove(clientX);
-    }
-  };
-
-  const handleSaturnTouchEnd = () => {
-    saturn?.handleTouchEnd();
-  };
-
-  return (
-    <div className="relative z-10 h-full overflow-y-auto custom-scrollbar scroll-smooth">
-      {/* Hero Section (Full Height) - Interactive area */}
-      <div
-        role="tab"
-        className="min-h-full flex flex-col justify-end p-12 pb-32 cursor-grab active:cursor-grabbing select-none"
-        onMouseDown={handleSaturnMouseDown}
-        onMouseMove={handleMouseMove}
-        onMouseUp={handleSaturnMouseUp}
-        onMouseLeave={handleSaturnMouseLeave}
-        onTouchStart={handleSaturnTouchStart}
-        onTouchMove={handleSaturnTouchMove}
-        onTouchEnd={handleSaturnTouchEnd}
-        tabIndex={0}
-      >
-        {/* 3D Floating Hero Text */}
-        <div
-          className="transition-transform duration-200 ease-out origin-bottom-left"
-          style={{
-            transform: `perspective(1000px) rotateX(${mouseY * -1}deg) rotateY(${mouseX * 1}deg)`,
-          }}
-        >
-          <div className="flex items-center gap-3 mb-6">
-            <div className="h-px w-12 bg-white/50"></div>
-            <span className="text-xs font-mono font-bold tracking-[0.2em] text-white/50 uppercase">
-              Launcher Active
-            </span>
-          </div>
-
-          <h1 className="text-8xl font-black tracking-tighter text-white mb-6 leading-none">
-            MINECRAFT
-          </h1>
-
-          <div className="flex items-center gap-4">
-            <div className="bg-white/10 backdrop-blur-md border border-white/10 px-3 py-1 rounded-sm text-xs font-bold uppercase tracking-widest text-white shadow-sm">
-              Java Edition
-            </div>
-          </div>
-        </div>
-
-        {/* Action Area */}
-        <div className="mt-8 flex gap-4">
-          <div className="text-zinc-500 text-sm font-mono">
-            &gt; Ready to launch session.
-          </div>
-        </div>
-
-        <BottomBar />
-      </div>
-    </div>
-  );
-}
diff --git a/packages/ui/src/pages/home.tsx b/packages/ui/src/pages/home.tsx
new file mode 100644
index 0000000..dc1413d
--- /dev/null
+++ b/packages/ui/src/pages/home.tsx
@@ -0,0 +1,102 @@
+import { useState } from "react";
+import { BottomBar } from "@/components/bottom-bar";
+import { useSaturnEffect } from "@/components/particle-background";
+
+export function HomePage() {
+  const [mouseX, setMouseX] = useState(0);
+  const [mouseY, setMouseY] = useState(0);
+  const saturn = useSaturnEffect();
+
+  const handleMouseMove = (e: React.MouseEvent) => {
+    const x = (e.clientX / window.innerWidth) * 2 - 1;
+    const y = (e.clientY / window.innerHeight) * 2 - 1;
+    setMouseX(x);
+    setMouseY(y);
+
+    // Forward mouse move to SaturnEffect (if available) for parallax/rotation interactions
+    saturn?.handleMouseMove(e.clientX);
+  };
+
+  const handleSaturnMouseDown = (e: React.MouseEvent) => {
+    saturn?.handleMouseDown(e.clientX);
+  };
+
+  const handleSaturnMouseUp = () => {
+    saturn?.handleMouseUp();
+  };
+
+  const handleSaturnMouseLeave = () => {
+    // Treat leaving the area as mouse-up for the effect
+    saturn?.handleMouseUp();
+  };
+
+  const handleSaturnTouchStart = (e: React.TouchEvent) => {
+    if (e.touches && e.touches.length === 1) {
+      const clientX = e.touches[0].clientX;
+      saturn?.handleTouchStart(clientX);
+    }
+  };
+
+  const handleSaturnTouchMove = (e: React.TouchEvent) => {
+    if (e.touches && e.touches.length === 1) {
+      const clientX = e.touches[0].clientX;
+      saturn?.handleTouchMove(clientX);
+    }
+  };
+
+  const handleSaturnTouchEnd = () => {
+    saturn?.handleTouchEnd();
+  };
+
+  return (
+    <div className="relative z-10 h-full overflow-y-auto custom-scrollbar scroll-smooth">
+      {/* Hero Section (Full Height) - Interactive area */}
+      <div
+        role="tab"
+        className="min-h-full flex flex-col justify-end p-12 pb-32 cursor-grab active:cursor-grabbing select-none"
+        onMouseDown={handleSaturnMouseDown}
+        onMouseMove={handleMouseMove}
+        onMouseUp={handleSaturnMouseUp}
+        onMouseLeave={handleSaturnMouseLeave}
+        onTouchStart={handleSaturnTouchStart}
+        onTouchMove={handleSaturnTouchMove}
+        onTouchEnd={handleSaturnTouchEnd}
+        tabIndex={0}
+      >
+        {/* 3D Floating Hero Text */}
+        <div
+          className="transition-transform duration-200 ease-out origin-bottom-left"
+          style={{
+            transform: `perspective(1000px) rotateX(${mouseY * -1}deg) rotateY(${mouseX * 1}deg)`,
+          }}
+        >
+          <div className="flex items-center gap-3 mb-6">
+            <div className="h-px w-12 bg-white/50"></div>
+            <span className="text-xs font-mono font-bold tracking-[0.2em] text-white/50 uppercase">
+              Launcher Active
+            </span>
+          </div>
+
+          <h1 className="text-8xl font-black tracking-tighter text-white mb-6 leading-none">
+            MINECRAFT
+          </h1>
+
+          <div className="flex items-center gap-4">
+            <div className="bg-white/10 backdrop-blur-md border border-white/10 px-3 py-1 rounded-sm text-xs font-bold uppercase tracking-widest text-white shadow-sm">
+              Java Edition
+            </div>
+          </div>
+        </div>
+
+        {/* Action Area */}
+        <div className="mt-8 flex gap-4">
+          <div className="text-zinc-500 text-sm font-mono">
+            &gt; Ready to launch session.
+          </div>
+        </div>
+
+        <BottomBar />
+      </div>
+    </div>
+  );
+}
diff --git a/packages/ui/src/pages/index.tsx b/packages/ui/src/pages/index.tsx
index 2acd377..d12646b 100644
--- a/packages/ui/src/pages/index.tsx
+++ b/packages/ui/src/pages/index.tsx
@@ -5,13 +5,11 @@ import { Sidebar } from "@/components/sidebar";
 import { useAuthStore } from "@/models/auth";
 import { useInstanceStore } from "@/models/instance";
 import { useSettingsStore } from "@/models/settings";
-import { useGameStore } from "@/stores/game-store";
 
 export function IndexPage() {
   const authStore = useAuthStore();
   const settingsStore = useSettingsStore();
   const instanceStore = useInstanceStore();
-  const initGameLifecycle = useGameStore((state) => state.initLifecycle);
 
   const location = useLocation();
 
@@ -19,15 +17,7 @@ export function IndexPage() {
     authStore.init();
     settingsStore.refresh();
     instanceStore.refresh();
-    void initGameLifecycle().catch((error) => {
-      console.error("Failed to initialize game lifecycle:", error);
-    });
-  }, [
-    authStore.init,
-    settingsStore.refresh,
-    instanceStore.refresh,
-    initGameLifecycle,
-  ]);
+  }, [authStore.init, settingsStore.refresh, instanceStore.refresh]);
 
   return (
     <div className="relative h-screen w-full overflow-hidden bg-background font-sans">
diff --git a/packages/ui/src/pages/instances-view.tsx b/packages/ui/src/pages/instances-view.tsx
deleted file mode 100644
index 7bb3302..0000000
--- a/packages/ui/src/pages/instances-view.tsx
+++ /dev/null
@@ -1,457 +0,0 @@
-import { open, save } from "@tauri-apps/plugin-dialog";
-import {
-  CopyIcon,
-  EditIcon,
-  FolderOpenIcon,
-  Plus,
-  RocketIcon,
-  Trash2Icon,
-  XIcon,
-} from "lucide-react";
-import { useEffect, useState } from "react";
-import { toast } from "sonner";
-import { openFileExplorer } from "@/client";
-import InstanceCreationModal from "@/components/instance-creation-modal";
-import InstanceEditorModal from "@/components/instance-editor-modal";
-import { Button } from "@/components/ui/button";
-import {
-  Dialog,
-  DialogContent,
-  DialogDescription,
-  DialogFooter,
-  DialogHeader,
-  DialogTitle,
-} from "@/components/ui/dialog";
-import { Input } from "@/components/ui/input";
-import { cn } from "@/lib/utils";
-import { useAuthStore } from "@/models/auth";
-import { useInstanceStore } from "@/models/instance";
-import { useGameStore } from "@/stores/game-store";
-import type { Instance } from "@/types";
-
-export function InstancesView() {
-  const account = useAuthStore((state) => state.account);
-  const instancesStore = useInstanceStore();
-  const startGame = useGameStore((state) => state.startGame);
-  const stopGame = useGameStore((state) => state.stopGame);
-  const runningInstanceId = useGameStore((state) => state.runningInstanceId);
-  const launchingInstanceId = useGameStore(
-    (state) => state.launchingInstanceId,
-  );
-  const stoppingInstanceId = useGameStore((state) => state.stoppingInstanceId);
-  const [isImporting, setIsImporting] = useState(false);
-  const [repairing, setRepairing] = useState(false);
-  const [exportingId, setExportingId] = useState<string | null>(null);
-
-  // Modal / UI state
-  const [showCreateModal, setShowCreateModal] = useState(false);
-  const [showEditModal, setShowEditModal] = useState(false);
-  const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
-  const [showDuplicateModal, setShowDuplicateModal] = useState(false);
-
-  // Selected / editing instance state
-  const [selectedInstance, setSelectedInstance] = useState<Instance | null>(
-    null,
-  );
-  const [editingInstance, setEditingInstance] = useState<Instance | null>(null);
-
-  // Form fields
-  const [duplicateName, setDuplicateName] = useState("");
-
-  useEffect(() => {
-    instancesStore.refresh();
-  }, [instancesStore.refresh]);
-
-  // Handlers to open modals
-  const openCreate = () => {
-    setShowCreateModal(true);
-  };
-
-  const openEdit = (instance: Instance) => {
-    setEditingInstance({ ...instance });
-    setShowEditModal(true);
-  };
-
-  const openDelete = (instance: Instance) => {
-    setSelectedInstance(instance);
-    setShowDeleteConfirm(true);
-  };
-
-  const openDuplicate = (instance: Instance) => {
-    setSelectedInstance(instance);
-    setDuplicateName(`${instance.name} (Copy)`);
-    setShowDuplicateModal(true);
-  };
-
-  const confirmDelete = async () => {
-    if (!selectedInstance) return;
-    await instancesStore.delete(selectedInstance.id);
-    setSelectedInstance(null);
-    setShowDeleteConfirm(false);
-  };
-
-  const confirmDuplicate = async () => {
-    if (!selectedInstance) return;
-    const name = duplicateName.trim();
-    if (!name) return;
-    await instancesStore.duplicate(selectedInstance.id, name);
-    setSelectedInstance(null);
-    setDuplicateName("");
-    setShowDuplicateModal(false);
-  };
-
-  const handleImport = async () => {
-    setIsImporting(true);
-    try {
-      const selected = await open({
-        multiple: false,
-        filters: [{ name: "Zip Archive", extensions: ["zip"] }],
-      });
-
-      if (typeof selected !== "string") {
-        return;
-      }
-
-      await instancesStore.importArchive(selected);
-    } finally {
-      setIsImporting(false);
-    }
-  };
-
-  const handleRepair = async () => {
-    setRepairing(true);
-    try {
-      await instancesStore.repair();
-    } finally {
-      setRepairing(false);
-    }
-  };
-
-  const handleExport = async (instance: Instance) => {
-    setExportingId(instance.id);
-    try {
-      const filePath = await save({
-        defaultPath: `${instance.name.replace(/[\\/:*?"<>|]/g, "_")}.zip`,
-        filters: [{ name: "Zip Archive", extensions: ["zip"] }],
-      });
-
-      if (!filePath) {
-        return;
-      }
-
-      await instancesStore.exportArchive(instance.id, filePath);
-    } finally {
-      setExportingId(null);
-    }
-  };
-
-  return (
-    <div className="h-full flex flex-col gap-4 p-6 overflow-y-auto">
-      <div className="flex items-center justify-between">
-        <h1 className="text-2xl font-bold text-gray-900 dark:text-white">
-          Instances
-        </h1>
-        <div className="flex items-center gap-2">
-          <Button
-            type="button"
-            variant="outline"
-            onClick={handleImport}
-            disabled={isImporting}
-          >
-            {isImporting ? "Importing..." : "Import"}
-          </Button>
-          <Button
-            type="button"
-            variant="outline"
-            onClick={handleRepair}
-            disabled={repairing}
-          >
-            {repairing ? "Repairing..." : "Repair Index"}
-          </Button>
-          <Button
-            type="button"
-            onClick={openCreate}
-            className="px-4 py-2 transition-colors"
-          >
-            <Plus size={18} />
-            Create Instance
-          </Button>
-        </div>
-      </div>
-
-      {instancesStore.instances.length === 0 ? (
-        <div className="flex-1 flex items-center justify-center">
-          <div className="text-center text-gray-500 dark:text-gray-400">
-            <p className="text-lg mb-2">No instances yet</p>
-            <p className="text-sm">Create your first instance to get started</p>
-          </div>
-        </div>
-      ) : (
-        <ul className="flex flex-col space-y-3">
-          {instancesStore.instances.map((instance) => {
-            const isActive = instancesStore.activeInstance?.id === instance.id;
-            const isRunning = runningInstanceId === instance.id;
-            const isLaunching = launchingInstanceId === instance.id;
-            const isStopping = stoppingInstanceId === instance.id;
-            const otherInstanceRunning =
-              runningInstanceId !== null && !isRunning;
-
-            return (
-              <li
-                key={instance.id}
-                onClick={() => instancesStore.setActiveInstance(instance)}
-                onKeyDown={async (e) => {
-                  if (e.key === "Enter") {
-                    try {
-                      await instancesStore.setActiveInstance(instance);
-                    } catch (e) {
-                      console.error("Failed to set active instance:", e);
-                      toast.error("Error setting active instance");
-                    }
-                  }
-                }}
-                className="cursor-pointer"
-              >
-                <div
-                  className={cn(
-                    "flex flex-row space-x-3 p-3 justify-between",
-                    "border bg-card/5 backdrop-blur-xl",
-                    "hover:bg-accent/50 transition-colors",
-                    isActive && "border-primary",
-                  )}
-                >
-                  <div className="flex flex-row space-x-4">
-                    {instance.iconPath ? (
-                      <div className="w-12 h-12 rounded overflow-hidden">
-                        <img
-                          src={instance.iconPath}
-                          alt={instance.name}
-                          className="w-full h-full object-cover"
-                        />
-                      </div>
-                    ) : (
-                      <div className="w-12 h-12 rounded bg-linear-to-br from-blue-500 to-purple-600 flex items-center justify-center">
-                        <span className="text-white font-bold text-lg">
-                          {instance.name.charAt(0).toUpperCase()}
-                        </span>
-                      </div>
-                    )}
-
-                    <div className="flex flex-col">
-                      <h3 className="text-lg font-semibold">{instance.name}</h3>
-                      {instance.versionId ? (
-                        <p className="text-sm text-muted-foreground">
-                          {instance.versionId}
-                        </p>
-                      ) : (
-                        <p className="text-sm text-muted-foreground">
-                          No version selected
-                        </p>
-                      )}
-                    </div>
-                  </div>
-
-                  <div className="flex items-center">
-                    <div className="flex flex-row space-x-2">
-                      <Button
-                        variant={isRunning ? "destructive" : "ghost"}
-                        size="icon"
-                        onClick={async (e) => {
-                          e.stopPropagation();
-
-                          try {
-                            await instancesStore.setActiveInstance(instance);
-                          } catch (error) {
-                            console.error(
-                              "Failed to set active instance:",
-                              error,
-                            );
-                            toast.error("Error setting active instance");
-                            return;
-                          }
-
-                          if (isRunning) {
-                            await stopGame(instance.id);
-                            return;
-                          }
-
-                          if (!instance.versionId) {
-                            toast.error("No version selected or installed");
-                            return;
-                          }
-
-                          await startGame(
-                            account,
-                            () => {
-                              toast.info("Please login first");
-                            },
-                            instance.id,
-                            instance.versionId,
-                            () => undefined,
-                          );
-                        }}
-                        disabled={
-                          otherInstanceRunning || isLaunching || isStopping
-                        }
-                      >
-                        {isLaunching || isStopping ? (
-                          <span className="text-xs">...</span>
-                        ) : isRunning ? (
-                          <XIcon />
-                        ) : (
-                          <RocketIcon />
-                        )}
-                      </Button>
-                      <Button
-                        variant="ghost"
-                        size="icon"
-                        onClick={(e) => {
-                          e.stopPropagation();
-                          void openFileExplorer(instance.gameDir);
-                        }}
-                      >
-                        <FolderOpenIcon />
-                      </Button>
-                      <Button
-                        variant="ghost"
-                        size="icon"
-                        onClick={(e) => {
-                          e.stopPropagation();
-                          void handleExport(instance);
-                        }}
-                        disabled={exportingId === instance.id}
-                      >
-                        <span className="text-xs">
-                          {exportingId === instance.id ? "..." : "ZIP"}
-                        </span>
-                      </Button>
-                      <Button
-                        variant="ghost"
-                        size="icon"
-                        onClick={(e) => {
-                          e.stopPropagation();
-                          openDuplicate(instance);
-                        }}
-                      >
-                        <CopyIcon />
-                      </Button>
-                      <Button
-                        variant="ghost"
-                        size="icon"
-                        onClick={(e) => {
-                          e.stopPropagation();
-                          openEdit(instance);
-                        }}
-                      >
-                        <EditIcon />
-                      </Button>
-                      <Button
-                        variant="destructive"
-                        size="icon"
-                        onClick={(e) => {
-                          e.stopPropagation();
-                          openDelete(instance);
-                        }}
-                      >
-                        <Trash2Icon />
-                      </Button>
-                    </div>
-                  </div>
-                </div>
-              </li>
-            );
-          })}
-        </ul>
-      )}
-
-      <InstanceCreationModal
-        open={showCreateModal}
-        onOpenChange={setShowCreateModal}
-      />
-
-      <InstanceEditorModal
-        open={showEditModal}
-        instance={editingInstance}
-        onOpenChange={(open) => {
-          setShowEditModal(open);
-          if (!open) setEditingInstance(null);
-        }}
-      />
-
-      {/* Delete Confirmation */}
-      <Dialog open={showDeleteConfirm} onOpenChange={setShowDeleteConfirm}>
-        <DialogContent>
-          <DialogHeader>
-            <DialogTitle>Delete Instance</DialogTitle>
-            <DialogDescription>
-              Are you sure you want to delete "{selectedInstance?.name}"? This
-              action cannot be undone.
-            </DialogDescription>
-          </DialogHeader>
-
-          <DialogFooter>
-            <Button
-              type="button"
-              variant="outline"
-              onClick={() => {
-                setShowDeleteConfirm(false);
-                setSelectedInstance(null);
-              }}
-            >
-              Cancel
-            </Button>
-            <Button
-              type="button"
-              onClick={confirmDelete}
-              className="bg-red-600 text-white hover:bg-red-500"
-            >
-              Delete
-            </Button>
-          </DialogFooter>
-        </DialogContent>
-      </Dialog>
-
-      {/* Duplicate Modal */}
-      <Dialog open={showDuplicateModal} onOpenChange={setShowDuplicateModal}>
-        <DialogContent>
-          <DialogHeader>
-            <DialogTitle>Duplicate Instance</DialogTitle>
-            <DialogDescription>
-              Provide a name for the duplicated instance.
-            </DialogDescription>
-          </DialogHeader>
-
-          <div className="mt-4">
-            <Input
-              value={duplicateName}
-              onChange={(e) => setDuplicateName(e.target.value)}
-              placeholder="New instance name"
-              onKeyDown={(e) => e.key === "Enter" && confirmDuplicate()}
-            />
-          </div>
-
-          <DialogFooter>
-            <Button
-              type="button"
-              variant="outline"
-              onClick={() => {
-                setShowDuplicateModal(false);
-                setSelectedInstance(null);
-                setDuplicateName("");
-              }}
-            >
-              Cancel
-            </Button>
-            <Button
-              type="button"
-              onClick={confirmDuplicate}
-              disabled={!duplicateName.trim()}
-            >
-              Duplicate
-            </Button>
-          </DialogFooter>
-        </DialogContent>
-      </Dialog>
-    </div>
-  );
-}
diff --git a/packages/ui/src/pages/routes.ts b/packages/ui/src/pages/routes.ts
index 8d105d4..55eb8fd 100644
--- a/packages/ui/src/pages/routes.ts
+++ b/packages/ui/src/pages/routes.ts
@@ -1,6 +1,6 @@
 import { createHashRouter } from "react-router";
-import { IndexPage } from ".";
-import { HomeView } from "./home-view";
+import { HomePage } from "./home";
+import { IndexPage } from "./index";
 import instanceRoute from "./instances/routes";
 import { SettingsPage } from "./settings";
 
@@ -11,7 +11,7 @@ const router = createHashRouter([
     children: [
       {
         index: true,
-        Component: HomeView,
+        Component: HomePage,
       },
       {
         path: "settings",
diff --git a/packages/ui/src/stores/game-store.ts b/packages/ui/src/stores/game-store.ts
deleted file mode 100644
index 7b6e746..0000000
--- a/packages/ui/src/stores/game-store.ts
+++ /dev/null
@@ -1,184 +0,0 @@
-import { listen, type UnlistenFn } from "@tauri-apps/api/event";
-import { toast } from "sonner";
-import { create } from "zustand";
-import {
-  getVersions,
-  getVersionsOfInstance,
-  startGame as startGameCommand,
-  stopGame as stopGameCommand,
-} from "@/client";
-import type { Account } from "@/types/bindings/auth";
-import type { GameExitedEvent } from "@/types/bindings/core";
-import type { Version } from "@/types/bindings/manifest";
-
-interface GameState {
-  versions: Version[];
-  selectedVersion: string;
-  runningInstanceId: string | null;
-  runningVersionId: string | null;
-  launchingInstanceId: string | null;
-  stoppingInstanceId: string | null;
-  lifecycleUnlisten: UnlistenFn | null;
-
-  latestRelease: Version | undefined;
-  isGameRunning: boolean;
-
-  initLifecycle: () => Promise<void>;
-  loadVersions: (instanceId?: string) => Promise<void>;
-  startGame: (
-    currentAccount: Account | null,
-    openLoginModal: () => void,
-    activeInstanceId: string | null,
-    versionId: string | null,
-    setView: (view: string) => void,
-  ) => Promise<string | null>;
-  stopGame: (instanceId?: string | null) => Promise<string | null>;
-  setSelectedVersion: (version: string) => void;
-  setVersions: (versions: Version[]) => void;
-}
-
-export const useGameStore = create<GameState>((set, get) => ({
-  versions: [],
-  selectedVersion: "",
-  runningInstanceId: null,
-  runningVersionId: null,
-  launchingInstanceId: null,
-  stoppingInstanceId: null,
-  lifecycleUnlisten: null,
-
-  get latestRelease() {
-    return get().versions.find((v) => v.type === "release");
-  },
-
-  get isGameRunning() {
-    return get().runningInstanceId !== null;
-  },
-
-  initLifecycle: async () => {
-    if (get().lifecycleUnlisten) {
-      return;
-    }
-
-    const unlisten = await listen<GameExitedEvent>("game-exited", (event) => {
-      const { instanceId, versionId, wasStopped } = event.payload;
-
-      set({
-        runningInstanceId: null,
-        runningVersionId: null,
-        launchingInstanceId: null,
-        stoppingInstanceId: null,
-      });
-
-      if (wasStopped) {
-        toast.success(
-          `Stopped Minecraft ${versionId} for instance ${instanceId}`,
-        );
-      } else {
-        toast.info(`Minecraft ${versionId} exited for instance ${instanceId}`);
-      }
-    });
-
-    set({ lifecycleUnlisten: unlisten });
-  },
-
-  loadVersions: async (instanceId?: string) => {
-    try {
-      const versions = instanceId
-        ? await getVersionsOfInstance(instanceId)
-        : await getVersions();
-      set({ versions: versions ?? [] });
-    } catch (e) {
-      console.error("Failed to load versions:", e);
-      set({ versions: [] });
-    }
-  },
-
-  startGame: async (
-    currentAccount,
-    openLoginModal,
-    activeInstanceId,
-    versionId,
-    setView,
-  ) => {
-    const { isGameRunning } = get();
-    const targetVersion = versionId ?? get().selectedVersion;
-
-    if (!currentAccount) {
-      toast.info("Please login first");
-      openLoginModal();
-      return null;
-    }
-
-    if (!targetVersion) {
-      toast.info("Please select a version first");
-      return null;
-    }
-
-    if (!activeInstanceId) {
-      toast.info("Please select an instance first");
-      setView("instances");
-      return null;
-    }
-
-    if (isGameRunning) {
-      toast.info("A game is already running");
-      return null;
-    }
-
-    set({
-      launchingInstanceId: activeInstanceId,
-      selectedVersion: targetVersion,
-    });
-    toast.info(`Preparing to launch ${targetVersion}...`);
-
-    try {
-      const message = await startGameCommand(activeInstanceId, targetVersion);
-      set({
-        launchingInstanceId: null,
-        runningInstanceId: activeInstanceId,
-        runningVersionId: targetVersion,
-      });
-      toast.success(message);
-      return message;
-    } catch (e) {
-      console.error(e);
-      set({ launchingInstanceId: null });
-      toast.error(`Error: ${e}`);
-      return null;
-    }
-  },
-
-  stopGame: async (instanceId) => {
-    const { runningInstanceId } = get();
-
-    if (!runningInstanceId) {
-      toast.info("No running game found");
-      return null;
-    }
-
-    if (instanceId && instanceId !== runningInstanceId) {
-      toast.info("That instance is not the one currently running");
-      return null;
-    }
-
-    set({ stoppingInstanceId: runningInstanceId });
-
-    try {
-      return await stopGameCommand();
-    } catch (e) {
-      console.error("Failed to stop game:", e);
-      toast.error(`Failed to stop game: ${e}`);
-      return null;
-    } finally {
-      set({ stoppingInstanceId: null });
-    }
-  },
-
-  setSelectedVersion: (version: string) => {
-    set({ selectedVersion: version });
-  },
-
-  setVersions: (versions: Version[]) => {
-    set({ versions });
-  },
-}));
-- 
cgit v1.2.3-70-g09d2

