-
Notifications
You must be signed in to change notification settings - Fork 0
Improve dashboard motion and server stop status #76
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,96 +1,88 @@ | ||
|  | ||
|  | ||
|
|
||
| # Catalyst | ||
|
|
||
| [](LICENSE) | ||
| [](#installation) | ||
| [](https://www.electronjs.org/) | ||
| [](LICENSE) | ||
| [](#installation) | ||
| [](https://www.electronjs.org/) | ||
|
|
||
| **A modern, feature-rich Minecraft server launcher and manager.** | ||
| Catalyst is a desktop manager for Minecraft servers. It helps you create, run, configure, back up, and monitor multiple servers from one Windows app. | ||
|
|
||
| Catalyst makes it easy to create, configure, and manage multiple Minecraft servers from a single desktop application. With automatic Java management, plugin support via Modrinth, built-in analytics, and a sleek dark UI — running a Minecraft server has never been simpler. | ||
| ## Highlights | ||
|
|
||
| --- | ||
| - Manage multiple Vanilla, Paper, Fabric, Forge, and Purpur servers | ||
| - Download and select the right Java runtime for each Minecraft version | ||
| - Use a built-in live console for server output and commands | ||
| - Search and install plugins from Modrinth | ||
| - Track TPS, memory, player activity, and events with CatalystAnalytics | ||
| - Schedule backups for worlds and server files | ||
| - Edit server settings, whitelist, and ban list from the UI | ||
| - Share a server through the built-in ngrok integration | ||
|
|
||
| ## ✨ Features | ||
| ## Installation | ||
|
|
||
| - 🖥️ **Multi-Server Management** — Create and run multiple Minecraft servers (Vanilla, Paper, Fabric, Forge, and more) | ||
| - ☕ **Automatic Java Runtime** — Downloads and manages the correct Java version (8, 11, 17, 21) based on your Minecraft version | ||
| - 📟 **Real-Time Console** — Integrated server console with live output | ||
| - 🔌 **Plugin Management** — Search, install, and update plugins directly from Modrinth | ||
| - 📊 **Server Analytics** — Bundled CatalystAnalytics plugin for TPS, memory usage, player stats, and geo-location tracking | ||
| - 💾 **Automated Backups** — Configurable backup schedules to keep your worlds safe | ||
| - 🌐 **Ngrok Integration** — Share your server instantly with built-in tunnel support | ||
| - ⚙️ **Server Properties Editor** — Edit server.properties, whitelist, and banlist from the UI | ||
| - 🔄 **Auto-Updates** — Built-in update checker to stay on the latest version | ||
| - 💻 **Cross-Platform** — Supports Windows (Linux coming soon) | ||
| Download the latest Windows installer from [GitHub Releases](../../releases). | ||
|
|
||
| --- | ||
| | Platform | Status | Format | | ||
| | --- | --- | --- | | ||
| | Windows | Supported | `.exe` installer | | ||
| | Linux | Planned | Not available yet | | ||
|
|
||
| ## 📥 Installation | ||
| ## Development | ||
|
|
||
| Download the latest release from [**GitHub Releases**](../../releases). | ||
| ### Requirements | ||
|
|
||
| | Platform | Format | | ||
| |----------|--------| | ||
| | Windows | `.exe` installer | | ||
| | Linux | 🚧 Coming Soon | | ||
|
|
||
| --- | ||
|
|
||
| ## 🛠️ Development | ||
|
|
||
| ### Prerequisites | ||
|
|
||
| - [Node.js](https://nodejs.org/) 20+ | ||
| - [npm](https://www.npmjs.com/) (included with Node.js) | ||
| - [Node.js](https://nodejs.org/) 20 or newer | ||
| - npm, included with Node.js | ||
| - Windows for packaged desktop builds | ||
|
|
||
| ### Setup | ||
|
|
||
| ```bash | ||
| # Install dependencies | ||
| npm install | ||
|
|
||
| # Start in development mode | ||
| npm run dev | ||
| ``` | ||
|
|
||
| The dev command starts the Electron app and the renderer dev server. | ||
|
|
||
| If `npm run dev` fails with `Error: spawn EPERM`, your terminal, antivirus, or sandbox may be blocking Node from starting the esbuild/Electron child process. Run the same command from a normal trusted terminal and allow Node/Electron when Windows or your antivirus asks. | ||
|
|
||
| ### Build | ||
|
|
||
| ```bash | ||
| # Windows | ||
| npm run build:win | ||
| ``` | ||
|
|
||
| # Linux | ||
| # 🚧 Coming Soon | ||
| For an unpacked local build: | ||
|
|
||
| ```bash | ||
| npm run build:unpack | ||
| ``` | ||
|
|
||
| ### Testing | ||
| ### Checks | ||
|
|
||
| ```bash | ||
| # Run unit tests | ||
| npm run typecheck | ||
| npm run test:unit | ||
|
|
||
| # Run tests with coverage | ||
| npm run coverage | ||
| ``` | ||
|
|
||
| --- | ||
| ## CatalystAnalytics | ||
|
|
||
| The desktop app ships with `resources/plugins/CatalystAnalytics.jar`. When analytics are enabled for a Paper or Purpur server, Catalyst copies the plugin into the server's `plugins/` directory and keeps it installed on future starts. | ||
|
|
||
| ## 🧰 Tech Stack | ||
| The plugin exposes a local REST API for dashboard data such as TPS, memory usage, joins, leaves, play time, chat counts, commands, kills, deaths, and approximate player location. Plugin details live in [catalyst-plugin/README.md](catalyst-plugin/README.md). | ||
|
|
||
| | Category | Technology | | ||
| |----------|------------| | ||
| | **Framework** | [Electron 33](https://www.electronjs.org/) with [electron-vite](https://electron-vite.org/) | | ||
| | **Language** | [TypeScript](https://www.typescriptlang.org/) | | ||
| | **Frontend** | [React 19](https://react.dev/) | | ||
| | **Styling** | [Tailwind CSS 3](https://tailwindcss.com/) + [shadcn/ui](https://ui.shadcn.com/) | | ||
| | **3D Graphics** | [Three.js](https://threejs.org/) | | ||
| | **Testing** | [Vitest](https://vitest.dev/) + [Testing Library](https://testing-library.com/) | | ||
| ## Tech Stack | ||
|
|
||
| --- | ||
| | Area | Tools | | ||
| | --- | --- | | ||
| | Desktop | Electron 33, electron-vite | | ||
| | UI | React 19, Tailwind CSS, shadcn/ui | | ||
| | Language | TypeScript | | ||
| | Charts and visuals | Recharts, Three.js | | ||
| | Testing | Vitest, Testing Library | | ||
|
|
||
| ## 📄 License | ||
| ## License | ||
|
|
||
| This project is licensed under the [GNU General Public License v3.0](LICENSE). | ||
| Catalyst is licensed under the [GNU General Public License v3.0](LICENSE). |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -592,24 +592,38 @@ export async function stopServer( | |
| } | ||
|
|
||
| return new Promise((resolve) => { | ||
| const onClose = () => { | ||
| let settled = false; | ||
| let timeout: ReturnType<typeof setTimeout>; | ||
|
|
||
| const finishStopped = async () => { | ||
| if (settled) return; | ||
| settled = true; | ||
| clearTimeout(timeout); | ||
| runningServers.delete(serverId); | ||
| stopStatsPolling(serverId); | ||
| // Clear the ngrok URL from server record when server stops | ||
| updateServerSettings(serverId, { ngrokUrl: undefined }).catch(() => {}); | ||
| await updateServerSettings(serverId, { ngrokUrl: undefined }).catch(() => {}); | ||
| await updateServerStatus(serverId, "Offline", "0/20"); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. CRITICAL: Missing error handling can leave
|
||
| if (mainWindow && !mainWindow.isDestroyed()) { | ||
| sendStatusUpdate(mainWindow, { serverId, status: "Offline", players: "0/20" }); | ||
| } | ||
| resolve({ success: true }); | ||
| }; | ||
|
|
||
| const onClose = () => { | ||
| finishStopped(); | ||
| }; | ||
|
|
||
| // Send graceful "stop" command to MC server | ||
| if (child.stdin && !child.stdin.destroyed) { | ||
| child.stdin.write("stop\n"); | ||
| } | ||
|
|
||
| const timeout = setTimeout(() => { | ||
| child.removeListener("close", onClose); | ||
| timeout = setTimeout(() => { | ||
| child.kill("SIGKILL"); | ||
| // Clear the ngrok URL from server record when server stops | ||
| updateServerSettings(serverId, { ngrokUrl: undefined }).catch(() => {}); | ||
| resolve({ success: true }); | ||
| setTimeout(finishStopped, 1500); | ||
| }, 15000); | ||
|
|
||
| child.once("close", onClose); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,11 +1,28 @@ | ||
| import { useOutlet } from "react-router-dom" | ||
| import { useLocation, useOutlet } from "react-router-dom" | ||
| import { AnimatePresence, motion } from "motion/react" | ||
|
|
||
| export function AnimatedOutlet() { | ||
| const outlet = useOutlet() | ||
| const location = useLocation() | ||
|
|
||
| return ( | ||
| <div className="flex min-h-full flex-col"> | ||
| {outlet} | ||
| <div className="flex min-h-full flex-col overflow-hidden"> | ||
| <AnimatePresence mode="popLayout" initial={false}> | ||
| <motion.div | ||
| key={location.pathname} | ||
| className="flex min-h-full flex-col" | ||
| initial={{ opacity: 0, y: 18, scale: 0.985 }} | ||
| animate={{ opacity: 1, y: 0, scale: 1 }} | ||
| exit={{ opacity: 0, y: -10, scale: 0.992 }} | ||
| transition={{ | ||
| opacity: { duration: 0.22, ease: "easeOut" }, | ||
| y: { type: "spring", stiffness: 260, damping: 30, mass: 0.72 }, | ||
| scale: { duration: 0.28, ease: [0.22, 1, 0.36, 1] }, | ||
| }} | ||
| > | ||
| {outlet} | ||
| </motion.div> | ||
| </AnimatePresence> | ||
| </div> | ||
| ) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
WARNING:
timeoutis read before TypeScript can prove it is assigned.finishStoppedreadstimeoutviaclearTimeout(timeout), but the variable is declared without an initializer and assigned later in the same function. With strict definite assignment enabled, this can fail typechecking; defensively it should be nullable and cleared only when set.