From b8c448dab8af8453c1e4ca067d72464c0d3b0cfa Mon Sep 17 00:00:00 2001 From: /home/neo <158327205+neoapps-dev@users.noreply.github.com> Date: Tue, 7 Jul 2026 16:30:48 +0300 Subject: [PATCH] feat(workshop): dependencies support (#118) --- src/components/views/WorkshopView.tsx | 182 +++++++++++++++++++++++++- 1 file changed, 179 insertions(+), 3 deletions(-) diff --git a/src/components/views/WorkshopView.tsx b/src/components/views/WorkshopView.tsx index 26363f0..db25a8f 100644 --- a/src/components/views/WorkshopView.tsx +++ b/src/components/views/WorkshopView.tsx @@ -74,6 +74,7 @@ interface RegistryPackage { main?: string; permissions?: string[]; files?: string[]; + dependencies?: string[]; } interface ServerListing { @@ -1093,6 +1094,8 @@ const WorkshopView = memo(function WorkshopView({ {selectedPkg && ( void; playPressSound: () => void; installedEntries: InstalledWorkshopPackage[]; @@ -1272,9 +1279,16 @@ function PackageModal({ >("install"); const [showInstall, setShowInstall] = useState(false); const [showUninstall, setShowUninstall] = useState(false); + const [confirmingDeps, setConfirmingDeps] = useState(false); const hasInstalled = installedEntries.length > 0; const needsUpdate = hasInstalled && installedEntries.some((e) => e.version !== pkg.version); + const unresolvedDeps = useMemo( + () => (pkg.dependencies || []).filter( + (depId) => !installedPkgs.some((p) => p.packageId === depId), + ), + [pkg.dependencies, installedPkgs], + ); const focusOptions: Array<"install" | "uninstall" | "close"> = isGameServerTab ? ["install", "close"] : isServerTab @@ -1359,7 +1373,14 @@ function PackageModal({ } else if (isPluginTab) { setShowInstall(true); } else { - setShowInstall(true); + const unresolvedDeps = (pkg.dependencies || []).filter( + (depId) => !installedPkgs.some((p) => p.packageId === depId), + ); + if (unresolvedDeps.length > 0) { + setConfirmingDeps(true); + } else { + setShowInstall(true); + } } }; @@ -1647,6 +1668,32 @@ function PackageModal({ ))} + {pkg.dependencies && pkg.dependencies.length > 0 && ( +
+ + Required Dependencies + +
+ {pkg.dependencies.map((depId) => { + const dep = allPackages.find((p) => p.id === depId); + const isDepInstalled = installedPkgs.some((p) => p.packageId === depId); + return ( + + {dep?.name || depId} + {isDepInstalled ? " (installed)" : ""} + + ); + })} +
+
+ )} {pkg.zips && Object.keys(pkg.zips).length > 0 && (
@@ -1726,10 +1773,94 @@ function PackageModal({
+ + {confirmingDeps && ( + setConfirmingDeps(false)} + > + e.stopPropagation()} + className="flex flex-col w-[520px] font-['Mojangles'] text-white border-2 border-[#555] rounded-sm overflow-hidden" + style={{ + backgroundImage: "url('/images/frame_background.png')", + backgroundSize: "100% 100%", + imageRendering: "pixelated", + }} + > +
+ + Dependencies Required + + + This package requires the following to be installed first + +
+
+ {unresolvedDeps.map((depId) => { + const dep = allPackages.find((p) => p.id === depId); + return ( +
+ + {dep?.name || depId} + + + v{dep?.version || "?"} + +
+ ); + })} + + These will be installed automatically before the main package. + +
+
+ + +
+
+
+ )} +
{showInstall && ( { setShowInstall(false); onInstallComplete(); @@ -1760,11 +1891,15 @@ function PackageModal({ function InstallModal({ pkg, + allPackages, + dependencies, onClose, playPressSound, isPluginTab, }: { pkg: RegistryPackage; + allPackages: RegistryPackage[]; + dependencies: string[]; onClose: () => void; playPressSound: () => void; isPluginTab?: boolean; @@ -1873,11 +2008,31 @@ function InstallModal({ return () => window.removeEventListener("keydown", handleKeyDown); }, [availableEditions, focusedIdx, status, onClose, playPressSound]); + const installDeps = async (instanceId: string) => { + for (const depId of dependencies) { + const depPkg = allPackages.find((p) => p.id === depId); + if (!depPkg || !depPkg.zips) continue; + try { + await TauriService.workshopInstall( + instanceId, + depPkg.id, + depPkg.zips, + depPkg.version, + ); + } catch (e) { + console.error(`Failed to install dependency ${depId}:`, e); + } + } + }; + const installTo = async (instanceId: string) => { setStatus("installing"); setErrorMsg(null); playPressSound(); try { + if (dependencies.length > 0) { + await installDeps(instanceId); + } await TauriService.workshopInstall( instanceId, pkg.id, @@ -1934,13 +2089,34 @@ function InstallModal({ {status === "installing" && (
- Installing... + {dependencies.length > 0 + ? "Installing dependencies..." + : isPluginTab + ? "Installing..." + : "Installing..."} {isPluginTab ? "Downloading plugin files" - : "Downloading and extracting assets"} + : dependencies.length > 0 + ? "Downloading and extracting required dependencies" + : "Downloading and extracting assets"} + {dependencies.length > 0 && ( +
+ {dependencies.map((depId) => { + const dep = allPackages.find((p) => p.id === depId); + return ( + + {dep?.name || depId} + + ); + })} +
+ )} {isPluginTab && pkg.permissions && pkg.permissions.length > 0 && (