From 66668d85d603c5841d755a6023aa1925559fc6d4 Mon Sep 17 00:00:00 2001 From: 苏向夜 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( + null, + ); + const [editingInstance, setEditingInstance] = useState(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 ( +
+
+

+ Instances +

+ +
+ + {instancesStore.instances.length === 0 ? ( +
+
+

No instances yet

+

Create your first instance to get started

+
+
+ ) : ( +
    + {instancesStore.instances.map((instance) => { + const isActive = instancesStore.activeInstance?.id === instance.id; + + return ( +
  • 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 ? ( +
    + {instance.name} +
    + ) : ( +
    + + {instance.name.charAt(0).toUpperCase()} + +
    + )} + +

    + {instance.name} +

    + +
    + {instance.versionId ? ( +

    Version: {instance.versionId}

    + ) : ( +

    No version selected

    + )} + + {instance.modLoader && ( +

    + Mod Loader:{" "} + {instance.modLoader} +

    + )} + +

    + Created: {formatDate(toNumber(instance.createdAt))} +

    + + {instance.lastPlayed && ( +

    + Last played:{" "} + {formatLastPlayed(toNumber(instance.lastPlayed))} +

    + )} +
    + + {/* Action Buttons */} +
    + + + + + +
    +
  • + ); + })} +
+ )} + + + + { + setShowEditModal(open); + if (!open) setEditingInstance(null); + }} + /> + + {/* Delete Confirmation */} + + + + Delete Instance + + Are you sure you want to delete "{selectedInstance?.name}"? This + action cannot be undone. + + + + + + + + + + + {/* Duplicate Modal */} + + + + Duplicate Instance + + Provide a name for the duplicated instance. + + + +
+ setDuplicateName(e.target.value)} + placeholder="New instance name" + onKeyDown={(e) => e.key === "Enter" && confirmDuplicate()} + /> +
+ + + + + +
+
+
+ ); +} -- cgit v1.2.3-70-g09d2