From 4a504c7e3d0c50cb90907d7903bc325d7daaf369 Mon Sep 17 00:00:00 2001 From: HsiangNianian Date: Thu, 12 Mar 2026 15:40:18 +0800 Subject: feat(instance): finish multi instances system --- packages/ui/src/pages/instances-view.tsx | 164 +++++++++++++++++++++++++++---- 1 file changed, 145 insertions(+), 19 deletions(-) (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 index e99004c..4d36201 100644 --- a/packages/ui/src/pages/instances-view.tsx +++ b/packages/ui/src/pages/instances-view.tsx @@ -1,7 +1,16 @@ -import { CopyIcon, EditIcon, Plus, RocketIcon, Trash2Icon } from "lucide-react"; +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 { startGame } from "@/client"; +import { openFileExplorer } from "@/client"; import InstanceCreationModal from "@/components/instance-creation-modal"; import InstanceEditorModal from "@/components/instance-editor-modal"; import { Button } from "@/components/ui/button"; @@ -15,11 +24,22 @@ import { } 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(null); // Modal / UI state const [showCreateModal, setShowCreateModal] = useState(false); @@ -78,20 +98,73 @@ export function InstancesView() { 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 (

Instances

- +
+ + + +
{instancesStore.instances.length === 0 ? ( @@ -105,6 +178,10 @@ export function InstancesView() {
    {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 (
  • + + -