From 66668d85d603c5841d755a6023aa1925559fc6d4 Mon Sep 17 00:00:00 2001
From: 苏向夜 <fu050409@163.com>
Date: Wed, 25 Feb 2026 01:32:51 +0800
Subject: chore(workspace): replace legacy codes

---
 packages/ui/src/pages/instances-view.tsx | 315 +++++++++++++++++++++++++++++++
 1 file changed, 315 insertions(+)
 create mode 100644 packages/ui/src/pages/instances-view.tsx

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

diff --git a/packages/ui/src/pages/instances-view.tsx b/packages/ui/src/pages/instances-view.tsx
new file mode 100644
index 0000000..ad6bd38
--- /dev/null
+++ b/packages/ui/src/pages/instances-view.tsx
@@ -0,0 +1,315 @@
+import { Copy, Edit2, Plus, Trash2 } from "lucide-react";
+import { useEffect, useState } from "react";
+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 { toNumber } from "@/lib/tsrs-utils";
+import { useInstancesStore } from "@/models/instances";
+import type { Instance } from "../types/bindings/instance";
+
+export function InstancesView() {
+  const instancesStore = useInstancesStore();
+
+  // 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 formatDate = (timestamp: number): string =>
+    new Date(timestamp * 1000).toLocaleDateString();
+
+  const formatLastPlayed = (timestamp: number): string => {
+    const date = new Date(timestamp * 1000);
+    const now = new Date();
+    const diff = now.getTime() - date.getTime();
+    const days = Math.floor(diff / (1000 * 60 * 60 * 24));
+
+    if (days === 0) return "Today";
+    if (days === 1) return "Yesterday";
+    if (days < 7) return `${days} days ago`;
+    return date.toLocaleDateString();
+  };
+
+  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>
+        <Button
+          type="button"
+          onClick={openCreate}
+          className="px-4 py-2 transition-colors"
+        >
+          <Plus size={18} />
+          Create Instance
+        </Button>
+      </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="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
+          {instancesStore.instances.map((instance) => {
+            const isActive = instancesStore.activeInstance?.id === instance.id;
+
+            return (
+              <li
+                key={instance.id}
+                onClick={() => instancesStore.setActiveInstance(instance)}
+                onKeyDown={(e) =>
+                  e.key === "Enter" &&
+                  instancesStore.setActiveInstance(instance)
+                }
+                className={`relative p-4 text-left border-2 transition-all cursor-pointer hover:border-blue-500 ${
+                  isActive ? "border-blue-500" : "border-transparent"
+                } bg-gray-100 dark:bg-gray-800`}
+              >
+                {/* Instance Icon */}
+                {instance.iconPath ? (
+                  <div className="w-12 h-12 mb-3 rounded overflow-hidden">
+                    <img
+                      src={instance.iconPath}
+                      alt={instance.name}
+                      className="w-full h-full object-cover"
+                    />
+                  </div>
+                ) : (
+                  <div className="w-12 h-12 mb-3 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>
+                )}
+
+                <h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-1">
+                  {instance.name}
+                </h3>
+
+                <div className="space-y-1 text-sm text-gray-600 dark:text-gray-400">
+                  {instance.versionId ? (
+                    <p className="truncate">Version: {instance.versionId}</p>
+                  ) : (
+                    <p className="text-gray-400">No version selected</p>
+                  )}
+
+                  {instance.modLoader && (
+                    <p className="truncate">
+                      Mod Loader:{" "}
+                      <span className="capitalize">{instance.modLoader}</span>
+                    </p>
+                  )}
+
+                  <p className="truncate">
+                    Created: {formatDate(toNumber(instance.createdAt))}
+                  </p>
+
+                  {instance.lastPlayed && (
+                    <p className="truncate">
+                      Last played:{" "}
+                      {formatLastPlayed(toNumber(instance.lastPlayed))}
+                    </p>
+                  )}
+                </div>
+
+                {/* Action Buttons */}
+                <div className="mt-4 flex gap-2">
+                  <button
+                    type="button"
+                    onClick={(e) => {
+                      e.stopPropagation();
+                      openEdit(instance);
+                    }}
+                    className="flex-1 flex items-center justify-center gap-1 px-3 py-1.5 bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 rounded text-sm transition-colors"
+                  >
+                    <Edit2 size={14} />
+                    Edit
+                  </button>
+
+                  <button
+                    type="button"
+                    onClick={(e) => {
+                      e.stopPropagation();
+                      openDuplicate(instance);
+                    }}
+                    className="flex-1 flex items-center justify-center gap-1 px-3 py-1.5 bg-gray-200 dark:bg-gray-700 hover:bg-gray-300 dark:hover:bg-gray-600 rounded text-sm transition-colors"
+                  >
+                    <Copy size={14} />
+                    Duplicate
+                  </button>
+
+                  <button
+                    type="button"
+                    onClick={(e) => {
+                      e.stopPropagation();
+                      openDelete(instance);
+                    }}
+                    className="flex-1 flex items-center justify-center gap-1 px-3 py-1.5 bg-red-500 hover:bg-red-600 text-white rounded text-sm transition-colors"
+                  >
+                    <Trash2 size={14} />
+                    Delete
+                  </button>
+                </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>
+  );
+}
-- 
cgit v1.2.3-70-g09d2


From d95ca2801c19a89a2a845f43b6e0133bf4e9be50 Mon Sep 17 00:00:00 2001
From: 苏向夜 <fu050409@163.com>
Date: Thu, 26 Feb 2026 18:30:57 +0800
Subject: refactor: migrate some invokes

---
 .changes/migrate-apis.md                           |   5 +
 .../ui/src/components/instance-creation-modal.tsx  |  70 +++++------
 .../ui/src/components/instance-editor-modal.tsx    |   4 +-
 packages/ui/src/models/instance.ts                 | 131 ++++++++++++++++++++
 packages/ui/src/models/instances.ts                | 135 ---------------------
 packages/ui/src/pages/index.tsx                    |   5 +-
 packages/ui/src/pages/instances-view.tsx           |   4 +-
 7 files changed, 175 insertions(+), 179 deletions(-)
 create mode 100644 .changes/migrate-apis.md
 create mode 100644 packages/ui/src/models/instance.ts
 delete mode 100644 packages/ui/src/models/instances.ts

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

diff --git a/.changes/migrate-apis.md b/.changes/migrate-apis.md
new file mode 100644
index 0000000..54f1566
--- /dev/null
+++ b/.changes/migrate-apis.md
@@ -0,0 +1,5 @@
+---
+"@dropout/ui": "patch:refactor"
+---
+
+Migrate tauri invokes of instance creation modal to generated client.
diff --git a/packages/ui/src/components/instance-creation-modal.tsx b/packages/ui/src/components/instance-creation-modal.tsx
index 8a2b1b4..7c46d0f 100644
--- a/packages/ui/src/components/instance-creation-modal.tsx
+++ b/packages/ui/src/components/instance-creation-modal.tsx
@@ -1,7 +1,13 @@
-import { invoke } from "@tauri-apps/api/core";
 import { Loader2, Search } from "lucide-react";
 import { useCallback, useEffect, useMemo, useState } from "react";
 import { toast } from "sonner";
+import {
+  getFabricLoadersForVersion,
+  getForgeVersionsForGame,
+  installFabric,
+  installForge,
+  installVersion,
+} from "@/client";
 import { Button } from "@/components/ui/button";
 import {
   Dialog,
@@ -13,12 +19,13 @@ import {
 } from "@/components/ui/dialog";
 import { Input } from "@/components/ui/input";
 import { ScrollArea } from "@/components/ui/scroll-area";
-import { useInstancesStore } from "@/models/instances";
+import { useInstanceStore } from "@/models/instance";
 import { useGameStore } from "@/stores/game-store";
-import type { Version } from "@/types/bindings/manifest";
-import type { FabricLoaderEntry } from "../types/bindings/fabric";
-import type { ForgeVersion as ForgeVersionEntry } from "../types/bindings/forge";
-import type { Instance } from "../types/bindings/instance";
+import type {
+  FabricLoaderEntry,
+  ForgeVersion as ForgeVersionEntry,
+  Version,
+} from "@/types";
 
 interface Props {
   open: boolean;
@@ -27,7 +34,7 @@ interface Props {
 
 export function InstanceCreationModal({ open, onOpenChange }: Props) {
   const gameStore = useGameStore();
-  const instancesStore = useInstancesStore();
+  const instancesStore = useInstanceStore();
 
   // Steps: 1 = name, 2 = version, 3 = mod loader
   const [step, setStep] = useState<number>(1);
@@ -61,12 +68,7 @@ export function InstanceCreationModal({ open, onOpenChange }: Props) {
     setForgeVersions([]);
     try {
       if (modLoaderType === "fabric") {
-        const loaders = await invoke<FabricLoaderEntry[]>(
-          "get_fabric_loaders_for_version",
-          {
-            gameVersion: selectedVersionUI.id,
-          },
-        );
+        const loaders = await getFabricLoadersForVersion(selectedVersionUI.id);
         setFabricLoaders(loaders || []);
         if (loaders && loaders.length > 0) {
           setSelectedFabricLoader(loaders[0].loader.version);
@@ -74,12 +76,7 @@ export function InstanceCreationModal({ open, onOpenChange }: Props) {
           setSelectedFabricLoader("");
         }
       } else if (modLoaderType === "forge") {
-        const versions = await invoke<ForgeVersionEntry[]>(
-          "get_forge_versions_for_game",
-          {
-            gameVersion: selectedVersionUI.id,
-          },
-        );
+        const versions = await getForgeVersionsForGame(selectedVersionUI.id);
         setForgeVersions(versions || []);
         if (versions && versions.length > 0) {
           // Binding `ForgeVersion` uses `version` (not `id`) — use `.version` here.
@@ -182,17 +179,12 @@ export function InstanceCreationModal({ open, onOpenChange }: Props) {
 
     try {
       // Step 1: create instance
-      const instance = await invoke<Instance>("create_instance", {
-        name: instanceName.trim(),
-      });
+      const instance = await instancesStore.create(instanceName.trim());
 
       // If selectedVersion provided, install it
-      if (selectedVersionUI) {
+      if (selectedVersionUI && instance) {
         try {
-          await invoke("install_version", {
-            instanceId: instance.id,
-            versionId: selectedVersionUI.id,
-          });
+          await installVersion(instance?.id, selectedVersionUI.id);
         } catch (err) {
           console.error("Failed to install base version:", err);
           // continue - instance created but version install failed
@@ -203,24 +195,24 @@ export function InstanceCreationModal({ open, onOpenChange }: Props) {
       }
 
       // If mod loader selected, install it
-      if (modLoaderType === "fabric" && selectedFabricLoader) {
+      if (modLoaderType === "fabric" && selectedFabricLoader && instance) {
         try {
-          await invoke("install_fabric", {
-            instanceId: instance.id,
-            gameVersion: selectedVersionUI?.id ?? "",
-            loaderVersion: selectedFabricLoader,
-          });
+          await installFabric(
+            instance?.id,
+            selectedVersionUI?.id ?? "",
+            selectedFabricLoader,
+          );
         } catch (err) {
           console.error("Failed to install Fabric:", err);
           toast.error(`Failed to install Fabric: ${String(err)}`);
         }
-      } else if (modLoaderType === "forge" && selectedForgeLoader) {
+      } else if (modLoaderType === "forge" && selectedForgeLoader && instance) {
         try {
-          await invoke("install_forge", {
-            instanceId: instance.id,
-            gameVersion: selectedVersionUI?.id ?? "",
-            installerVersion: selectedForgeLoader,
-          });
+          await installForge(
+            instance?.id,
+            selectedVersionUI?.id ?? "",
+            selectedForgeLoader,
+          );
         } catch (err) {
           console.error("Failed to install Forge:", err);
           toast.error(`Failed to install Forge: ${String(err)}`);
diff --git a/packages/ui/src/components/instance-editor-modal.tsx b/packages/ui/src/components/instance-editor-modal.tsx
index f880c20..d964185 100644
--- a/packages/ui/src/components/instance-editor-modal.tsx
+++ b/packages/ui/src/components/instance-editor-modal.tsx
@@ -16,7 +16,7 @@ import { Input } from "@/components/ui/input";
 import { Textarea } from "@/components/ui/textarea";
 
 import { toNumber } from "@/lib/tsrs-utils";
-import { useInstancesStore } from "@/models/instances";
+import { useInstanceStore } from "@/models/instance";
 import { useSettingsStore } from "@/models/settings";
 import type { FileInfo } from "../types/bindings/core";
 import type { Instance } from "../types/bindings/instance";
@@ -28,7 +28,7 @@ type Props = {
 };
 
 export function InstanceEditorModal({ open, instance, onOpenChange }: Props) {
-  const instancesStore = useInstancesStore();
+  const instancesStore = useInstanceStore();
   const { config } = useSettingsStore();
 
   const [activeTab, setActiveTab] = useState<
diff --git a/packages/ui/src/models/instance.ts b/packages/ui/src/models/instance.ts
new file mode 100644
index 0000000..a3fda3d
--- /dev/null
+++ b/packages/ui/src/models/instance.ts
@@ -0,0 +1,131 @@
+import { toast } from "sonner";
+import { create } from "zustand";
+import {
+  createInstance,
+  deleteInstance,
+  duplicateInstance,
+  getActiveInstance,
+  getInstance,
+  listInstances,
+  setActiveInstance,
+  updateInstance,
+} from "@/client";
+import type { Instance } from "@/types";
+
+interface InstanceState {
+  instances: Instance[];
+  activeInstance: Instance | null;
+
+  refresh: () => Promise<void>;
+  create: (name: string) => Promise<Instance | null>;
+  delete: (id: string) => Promise<void>;
+  update: (instance: Instance) => Promise<void>;
+  setActiveInstance: (instance: Instance) => Promise<void>;
+  duplicate: (id: string, newName: string) => Promise<Instance | null>;
+  get: (id: string) => Promise<Instance | null>;
+}
+
+export const useInstanceStore = create<InstanceState>((set, get) => ({
+  instances: [],
+  activeInstance: null,
+
+  refresh: async () => {
+    const { setActiveInstance } = get();
+    try {
+      const instances = await listInstances();
+      const activeInstance = await getActiveInstance();
+
+      if (!activeInstance && instances.length > 0) {
+        // If no active instance but instances exist, set the first one as active
+        await setActiveInstance(instances[0]);
+      }
+
+      set({ instances, activeInstance });
+    } catch (e) {
+      console.error("Failed to load instances:", e);
+      toast.error("Error loading instances");
+    }
+  },
+
+  create: async (name) => {
+    const { refresh } = get();
+    try {
+      const instance = await createInstance(name);
+      await refresh();
+      toast.success(`Instance "${name}" created successfully`);
+      return instance;
+    } catch (e) {
+      console.error("Failed to create instance:", e);
+      toast.error("Error creating instance");
+      return null;
+    }
+  },
+
+  delete: async (id) => {
+    const { refresh, instances, activeInstance, setActiveInstance } = get();
+    try {
+      await deleteInstance(id);
+      await refresh();
+
+      // If deleted instance was active, set another as active
+      if (activeInstance?.id === id) {
+        if (instances.length > 0) {
+          await setActiveInstance(instances[0]);
+        } else {
+          set({ activeInstance: null });
+        }
+      }
+
+      toast.success("Instance deleted successfully");
+    } catch (e) {
+      console.error("Failed to delete instance:", e);
+      toast.error("Error deleting instance");
+    }
+  },
+
+  update: async (instance) => {
+    const { refresh } = get();
+    try {
+      await updateInstance(instance);
+      await refresh();
+      toast.success("Instance updated successfully");
+    } catch (e) {
+      console.error("Failed to update instance:", e);
+      toast.error("Error updating instance");
+    }
+  },
+
+  setActiveInstance: async (instance) => {
+    try {
+      await setActiveInstance(instance.id);
+      set({ activeInstance: instance });
+      toast.success("Active instance changed");
+    } catch (e) {
+      console.error("Failed to set active instance:", e);
+      toast.error("Error setting active instance");
+    }
+  },
+
+  duplicate: async (id, newName) => {
+    const { refresh } = get();
+    try {
+      const instance = await duplicateInstance(id, newName);
+      await refresh();
+      toast.success(`Instance duplicated as "${newName}"`);
+      return instance;
+    } catch (e) {
+      console.error("Failed to duplicate instance:", e);
+      toast.error("Error duplicating instance");
+      return null;
+    }
+  },
+
+  get: async (id) => {
+    try {
+      return await getInstance(id);
+    } catch (e) {
+      console.error("Failed to get instance:", e);
+      return null;
+    }
+  },
+}));
diff --git a/packages/ui/src/models/instances.ts b/packages/ui/src/models/instances.ts
deleted file mode 100644
index f434c7c..0000000
--- a/packages/ui/src/models/instances.ts
+++ /dev/null
@@ -1,135 +0,0 @@
-import { toast } from "sonner";
-import { create } from "zustand";
-import {
-  createInstance,
-  deleteInstance,
-  duplicateInstance,
-  getActiveInstance,
-  getInstance,
-  listInstances,
-  setActiveInstance,
-  updateInstance,
-} from "@/client";
-import type { Instance } from "@/types";
-
-interface InstancesState {
-  // State
-  instances: Instance[];
-  activeInstance: Instance | null;
-
-  // Actions
-  refresh: () => Promise<void>;
-  create: (name: string) => Promise<Instance | null>;
-  delete: (id: string) => Promise<void>;
-  update: (instance: Instance) => Promise<void>;
-  setActiveInstance: (instance: Instance) => Promise<void>;
-  duplicate: (id: string, newName: string) => Promise<Instance | null>;
-  getInstance: (id: string) => Promise<Instance | null>;
-}
-
-export const useInstancesStore = create<InstancesState>((set, get) => ({
-  // Initial state
-  instances: [],
-  activeInstance: null,
-
-  // Actions
-  refresh: async () => {
-    const { setActiveInstance } = get();
-    try {
-      const instances = await listInstances();
-      const active = await getActiveInstance();
-
-      if (!active && instances.length > 0) {
-        // If no active instance but instances exist, set the first one as active
-        await setActiveInstance(instances[0]);
-      }
-
-      set({ instances });
-    } catch (e) {
-      console.error("Failed to load instances:", e);
-      toast.error("Error loading instances");
-    }
-  },
-
-  create: async (name) => {
-    const { refresh } = get();
-    try {
-      const instance = await createInstance(name);
-      await refresh();
-      toast.success(`Instance "${name}" created successfully`);
-      return instance;
-    } catch (e) {
-      console.error("Failed to create instance:", e);
-      toast.error("Error creating instance");
-      return null;
-    }
-  },
-
-  delete: async (id) => {
-    const { refresh, instances, activeInstance, setActiveInstance } = get();
-    try {
-      await deleteInstance(id);
-      await refresh();
-
-      // If deleted instance was active, set another as active
-      if (activeInstance?.id === id) {
-        if (instances.length > 0) {
-          await setActiveInstance(instances[0]);
-        } else {
-          set({ activeInstance: null });
-        }
-      }
-
-      toast.success("Instance deleted successfully");
-    } catch (e) {
-      console.error("Failed to delete instance:", e);
-      toast.error("Error deleting instance");
-    }
-  },
-
-  update: async (instance) => {
-    const { refresh } = get();
-    try {
-      await updateInstance(instance);
-      await refresh();
-      toast.success("Instance updated successfully");
-    } catch (e) {
-      console.error("Failed to update instance:", e);
-      toast.error("Error updating instance");
-    }
-  },
-
-  setActiveInstance: async (instance) => {
-    try {
-      await setActiveInstance(instance.id);
-      set({ activeInstance: instance });
-      toast.success("Active instance changed");
-    } catch (e) {
-      console.error("Failed to set active instance:", e);
-      toast.error("Error setting active instance");
-    }
-  },
-
-  duplicate: async (id, newName) => {
-    const { refresh } = get();
-    try {
-      const instance = await duplicateInstance(id, newName);
-      await refresh();
-      toast.success(`Instance duplicated as "${newName}"`);
-      return instance;
-    } catch (e) {
-      console.error("Failed to duplicate instance:", e);
-      toast.error("Error duplicating instance");
-      return null;
-    }
-  },
-
-  getInstance: async (id) => {
-    try {
-      return await getInstance(id);
-    } catch (e) {
-      console.error("Failed to get instance:", e);
-      return null;
-    }
-  },
-}));
diff --git a/packages/ui/src/pages/index.tsx b/packages/ui/src/pages/index.tsx
index 54cfc1e..093ccb2 100644
--- a/packages/ui/src/pages/index.tsx
+++ b/packages/ui/src/pages/index.tsx
@@ -3,18 +3,21 @@ import { Outlet, useLocation } from "react-router";
 import { ParticleBackground } from "@/components/particle-background";
 import { Sidebar } from "@/components/sidebar";
 import { useAuthStore } from "@/models/auth";
+import { useInstanceStore } from "@/models/instance";
 import { useSettingsStore } from "@/models/settings";
 
 export function IndexPage() {
   const authStore = useAuthStore();
   const settingsStore = useSettingsStore();
+  const instanceStore = useInstanceStore();
 
   const location = useLocation();
 
   useEffect(() => {
     authStore.init();
     settingsStore.refresh();
-  }, [authStore.init, settingsStore.refresh]);
+    instanceStore.refresh();
+  }, [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
index ad6bd38..1634905 100644
--- a/packages/ui/src/pages/instances-view.tsx
+++ b/packages/ui/src/pages/instances-view.tsx
@@ -13,11 +13,11 @@ import {
 } from "@/components/ui/dialog";
 import { Input } from "@/components/ui/input";
 import { toNumber } from "@/lib/tsrs-utils";
-import { useInstancesStore } from "@/models/instances";
+import { useInstanceStore } from "@/models/instance";
 import type { Instance } from "../types/bindings/instance";
 
 export function InstancesView() {
-  const instancesStore = useInstancesStore();
+  const instancesStore = useInstanceStore();
 
   // Modal / UI state
   const [showCreateModal, setShowCreateModal] = useState(false);
-- 
cgit v1.2.3-70-g09d2

