docs: Gemini consult — ModpackChecker UX overhaul, 10 questions
Most detailed consult ever. Covers: - Seeding problem (all servers falsely show up_to_date) - Messy CurseForge version strings - Console widget UX redesign - Non-modpack server detection - Proactive notifications - Version history architecture - Multi-panel cloud opportunity - Monetization angles
This commit is contained in:
@@ -0,0 +1,290 @@
|
||||
# Gemini Consultation: ModpackChecker UX Overhaul — Console Widget, Version Detection & Future Architecture
|
||||
|
||||
**Date:** April 12–13, 2026 (late night session)
|
||||
**From:** Michael (The Wizard) + Claude (Chronicler #84 — The Meridian)
|
||||
**To:** Gemini (Architectural Partner)
|
||||
**Re:** ModpackChecker v1.0.0 is live but the UX isn't what we hoped — we need your wildest ideas for v1.1.0+
|
||||
|
||||
---
|
||||
|
||||
## Hey Gemini! 👋
|
||||
|
||||
Long night. We shipped ModpackChecker v1.0.0 to the live Pterodactyl panel tonight after an epic debugging marathon. The good news: it's installed, the background cron detects all 22 servers via the `modpack_installations` table, and the dashboard badges are compiling. The less good news: when Michael clicked the console widget button, it worked — but it didn't feel right. We want your help designing what "right" actually looks like, and we're explicitly asking for wild ideas. Nothing is off the table.
|
||||
|
||||
We're 46 hours from soft launch on April 15, so some of this is v1.1.0+ thinking. But if something is quick to implement, we'll do it tonight.
|
||||
|
||||
---
|
||||
|
||||
## The Product
|
||||
|
||||
**ModpackChecker** is a commercial Blueprint extension for Pterodactyl Panel 1.12.2. It monitors Minecraft modpack servers for updates across 4 platforms: CurseForge, Modrinth, FTB, and Technic. It's being sold on BuiltByBit at $14.99 (Standard) and $24.99 (Professional).
|
||||
|
||||
**The vision:** Panel admins with 10-50 Minecraft servers should never have to manually check if their modpacks are out of date. It should just... tell them. Automatically. Beautifully.
|
||||
|
||||
**Our customers:** Minecraft hosting providers, server network owners, and serious hobbyists who run multiple servers. Michael is our first real customer — he runs 22 servers on his own Firefrost Gaming panel.
|
||||
|
||||
---
|
||||
|
||||
## Current Architecture
|
||||
|
||||
### Detection Pipeline (Background Cron)
|
||||
`php artisan modpackchecker:check` runs on a cron schedule (daily or every 6/12 hours for Pro). Detection priority:
|
||||
|
||||
1. **`modpack_installations` table** — Pterodactyl's own install records (provider + modpack_id). Works for ~60-70% of servers that were installed via the panel's modpack installer.
|
||||
2. **Egg variables** — `MODPACK_PLATFORM` + `MODPACK_ID` in server startup variables. Works if the admin configured them manually.
|
||||
3. **File detection** — `DaemonFileRepository` reads `manifest.json` (CurseForge) or `modrinth.index.json` (Modrinth) from `/home/container/`. In practice this rarely works because the modpack installer extracts files and discards the manifest.
|
||||
|
||||
### Version Tracking
|
||||
- **First run:** Seeds `current_version = latest_version` (assumes freshly installed = current)
|
||||
- **Subsequent runs:** If CurseForge/Modrinth returns a newer `latest_version` than the stored `current_version`, status becomes `update_available`
|
||||
- **Problem:** The seeding assumption is often wrong. Michael KNOWS some of his servers are behind, but since we seeded them as current tonight, they all show "up to date."
|
||||
|
||||
### Database Schema (`modpackchecker_servers`)
|
||||
```sql
|
||||
server_uuid VARCHAR
|
||||
platform VARCHAR (curseforge, modrinth, ftb, technic)
|
||||
modpack_id VARCHAR
|
||||
modpack_name VARCHAR
|
||||
current_version VARCHAR (what we think the server is running)
|
||||
latest_version VARCHAR (latest from the platform API)
|
||||
status VARCHAR (up_to_date, update_available, unconfigured, error)
|
||||
detection_method VARCHAR (installer, egg, file, manual)
|
||||
is_user_overridden BOOLEAN
|
||||
last_checked TIMESTAMP
|
||||
error_message TEXT
|
||||
```
|
||||
|
||||
### The Console Widget (Current State)
|
||||
A React TSX component injected into Pterodactyl's server console page. Currently shows:
|
||||
- **Idle:** "Click to check"
|
||||
- **Loading:** "Checking..."
|
||||
- **Success:** Shows `latest_version` string (e.g., "All the Mods 10-6.6")
|
||||
- **Update available:** Orange background
|
||||
- **Error:** Red text with error code
|
||||
|
||||
**What it doesn't show:** Current version, comparison, when it was last checked, whether the cron has run yet, or any context about what the version string means.
|
||||
|
||||
### The Dashboard Badge (Current State)
|
||||
A small colored dot injected next to the server name on the panel dashboard:
|
||||
- 🟠 Orange = update available
|
||||
- 🟢 Green = up to date
|
||||
- No dot = unconfigured or not yet checked
|
||||
|
||||
**Works when yarn build compiles.** Currently blocked by Blueprint beta CSS module issue on some Node versions (v1.1.0 fix planned with `--openssl-legacy-provider`).
|
||||
|
||||
---
|
||||
|
||||
## What "Worked But Not The Way We Hoped" Means
|
||||
|
||||
When Michael clicked the console widget button on a server tonight, it showed him the latest version string — something like "All the Mods 10-6.6". That's technically correct. But here's what felt wrong:
|
||||
|
||||
1. **It just shows a version string with no context.** "All the Mods 10-6.6" — is that good? Is that the latest? Is HIS server on that version? There's no comparison shown.
|
||||
|
||||
2. **All 22 servers show "up to date" even though Michael knows some aren't.** This is the seeding problem: we set current_version = latest_version on first run, so everything looks current until CurseForge releases a newer version.
|
||||
|
||||
3. **The UX requires clicking.** You have to click the widget button to trigger a live API check. Michael expected it to proactively show him the status without having to click.
|
||||
|
||||
4. **Version strings from CurseForge are messy.** CurseForge's `displayName` field returns things like:
|
||||
- "All the Mods 10-6.6"
|
||||
- "Society - Capital Hill - 0.20.0"
|
||||
- "All the Mons-0.18.0-beta"
|
||||
- "Homestead-1.2.0.zip"
|
||||
These are human-readable titles, not clean semantic versions. Comparing "ATM10-6.5" to "ATM10-6.6" is easy, but "Homestead-1.2.0.zip" vs "Homestead-1.3.0.zip" requires parsing.
|
||||
|
||||
5. **The console widget is in the wrong place.** It's in the server console "right column" alongside CPU/memory/disk stats. But version checking isn't something you want while actively managing a running server. It feels misplaced.
|
||||
|
||||
---
|
||||
|
||||
## Full Current Code
|
||||
|
||||
### ModpackVersionCard.tsx (Console Widget)
|
||||
```tsx
|
||||
import React, { useState } from 'react';
|
||||
import { ServerContext } from '@/state/server';
|
||||
import http from '@/api/http';
|
||||
import { faCube } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import classNames from 'classnames';
|
||||
|
||||
interface VersionData {
|
||||
success: boolean;
|
||||
platform?: string;
|
||||
modpack_id?: string;
|
||||
modpack_name?: string;
|
||||
current_version?: string;
|
||||
latest_version?: string;
|
||||
status?: string;
|
||||
message?: string;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
const ModpackVersionCard: React.FC = () => {
|
||||
const uuid = ServerContext.useStoreState((state) => state.server.data?.uuid);
|
||||
const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
|
||||
const [data, setData] = useState<VersionData | null>(null);
|
||||
|
||||
const checkForUpdates = async () => {
|
||||
if (!uuid) return;
|
||||
setStatus('loading');
|
||||
try {
|
||||
const response = await http.post(`/api/client/extensions/modpackchecker/servers/${uuid}/check`);
|
||||
setData(response.data);
|
||||
setStatus(response.data.success ? 'success' : 'error');
|
||||
} catch (error: any) {
|
||||
if (error.response?.status === 429) {
|
||||
setData({ success: false, error: 'rate_limited' });
|
||||
} else if (error.response?.status === 404) {
|
||||
setData({ success: false, error: 'not_found' });
|
||||
} else {
|
||||
setData({ success: false, error: 'api_error' });
|
||||
}
|
||||
setStatus('error');
|
||||
}
|
||||
};
|
||||
|
||||
const getBgColor = () => {
|
||||
if (status === 'success' && data?.status === 'update_available') return 'bg-orange-500';
|
||||
if (status === 'success' && data?.success) return 'bg-cyan-500';
|
||||
return 'bg-gray-700';
|
||||
};
|
||||
|
||||
return (
|
||||
<div onClick={status !== 'loading' ? checkForUpdates : undefined}>
|
||||
{/* ... renders latest_version string only ... */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### manualCheck() in ModpackAPIController.php
|
||||
```php
|
||||
public function manualCheck(Request $request, Server $server): JsonResponse
|
||||
{
|
||||
// Rate limiting: 2/min per server
|
||||
// Detection: egg vars → modpack_installations → file detection → cached DB
|
||||
// Returns: success, platform, modpack_id, modpack_name,
|
||||
// current_version (from DB cache), latest_version (from API), update_available
|
||||
}
|
||||
```
|
||||
|
||||
### CheckModpackUpdates.php (Cron) — Key Logic
|
||||
```php
|
||||
// Version seeding on first detection:
|
||||
if (!$existing) {
|
||||
// First time seeing this server — seed current = latest
|
||||
$currentVersion = $versionData['version'];
|
||||
} else {
|
||||
// Preserve existing current_version
|
||||
$currentVersion = $existing->current_version ?? $versionData['version'];
|
||||
}
|
||||
|
||||
$updateAvailable = $currentVersion !== $versionData['version'];
|
||||
$status = $updateAvailable ? 'update_available' : 'up_to_date';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## The Core Problems We Want to Solve
|
||||
|
||||
### Problem 1: The Seeding Assumption
|
||||
**"First run = current"** is almost always wrong for panels that have been running modpack servers for months before installing ModpackChecker. We need a way to know what version is ACTUALLY running on the server, not just assume it's the latest.
|
||||
|
||||
### Problem 2: Messy Version Strings
|
||||
CurseForge returns display names like "All the Mods 10-6.6" not semantic versions like "6.6.0". Comparing these is unreliable and the strings look ugly in the UI.
|
||||
|
||||
### Problem 3: The Widget UX
|
||||
The console widget shows a version string only after clicking. It should show more context — current vs latest, when last checked, the platform — proactively, not reactively.
|
||||
|
||||
### Problem 4: "Not Configured" Servers
|
||||
3 of 22 servers have no detection data at all (FoundryVTT, Hytale, Vanilla — obviously not modpack servers). But ModpackChecker doesn't know that. These just sit in `unconfigured` status forever and pollute the view.
|
||||
|
||||
### Problem 5: Update Notifications
|
||||
An update badge is good. But how does the admin KNOW they got an update? There's no notification — they have to visit the panel and see the orange dot. For a 22-server panel, that requires scanning the whole dashboard.
|
||||
|
||||
---
|
||||
|
||||
## Specific Questions
|
||||
|
||||
1. **Seeding problem — how do we know the actual current version?**
|
||||
We have Wings access to the server filesystem. For CurseForge packs installed via the modpack installer, is there any file left behind that contains the installed version? We checked `manifest.json` — it's not there post-install. Is there a `minecraftinstance.json`? A `packinfo` file? Anything in the `config/` folder? What about reading from the server's startup command arguments?
|
||||
|
||||
2. **Alternative version sources — what are we missing?**
|
||||
We've tried: egg variables, manifest.json, modrinth.index.json, modpack_installations table. What other sources exist on a running Minecraft server that could tell us the installed modpack version? Think: server logs on startup, JVM arguments, mod metadata files, world data, Forge/NeoForge version files. Wild ideas welcome.
|
||||
|
||||
3. **Semantic version extraction — best approach?**
|
||||
Given messy version strings like "All the Mods 10-6.6", "Society - Capital Hill - 0.20.0", "Homestead-1.2.0.zip" — what's the most reliable regex or parsing strategy to extract a clean comparable version number? Should we normalize to semver? Store both the raw display name AND an extracted version number?
|
||||
|
||||
4. **Widget redesign — what should it actually show?**
|
||||
The current widget shows a version string on click. What would an ideal modpack status widget look like in the Pterodactyl server console? Should it:
|
||||
- Load the cached DB status automatically (no click required)?
|
||||
- Show current vs latest side by side?
|
||||
- Show the platform logo/icon?
|
||||
- Show time since last check?
|
||||
- Have a "Force Check Now" button that bypasses cache?
|
||||
We're redesigning this for v1.1.0 — no constraints, what's the ideal UX?
|
||||
|
||||
5. **"Not a modpack" detection — how do we handle non-modpack servers?**
|
||||
FoundryVTT and Hytale servers will never have modpack data. Right now they just show "unconfigured" forever. Should we:
|
||||
- Show nothing for unconfigured servers (hide the widget entirely)?
|
||||
- Let the admin mark a server as "not a modpack server" to dismiss it?
|
||||
- Auto-detect server type from the egg name and skip non-Minecraft eggs?
|
||||
- Something else?
|
||||
|
||||
6. **Proactive notifications — what's the right channel?**
|
||||
When an update is detected, how should we notify the admin? Options:
|
||||
- Discord webhook (already planned for Pro tier)
|
||||
- Email via Pterodactyl's mail config?
|
||||
- In-panel notification (Pterodactyl has a notification system)?
|
||||
- An "Update Digest" email showing all pending updates once a week?
|
||||
- SMS via Twilio for critical servers?
|
||||
What would you actually USE as a busy server admin?
|
||||
|
||||
7. **The "known outdated" problem — how do we help admins with servers they know are behind?**
|
||||
Michael knows several servers are running old versions. Right now the only way to fix the false "up to date" status is to manually edit `current_version` in the DB. For a BuiltByBit product, that's unacceptable. What's the right UX for an admin to say "this server is running version X"?
|
||||
- Input field in the admin extension page?
|
||||
- A "Recalibrate" button that triggers a fresh comparison?
|
||||
- Accept a version number in the console widget directly?
|
||||
|
||||
8. **Long-term architecture — should we store version history?**
|
||||
Right now we only store current + latest. Should we log every detected version change? Benefits: admins could see "this pack was updated 3 times since I last looked." Costs: more DB storage, more complex queries. Is a `modpackchecker_version_history` table worth building?
|
||||
|
||||
9. **Multi-panel support — wild idea or real opportunity?**
|
||||
ModpackChecker currently only works on the panel it's installed on. But many hosting providers run multiple Pterodactyl panels. Could there be a "ModpackChecker Cloud" service — a central dashboard that aggregates update status from multiple panels? Is this a real product opportunity or scope creep?
|
||||
|
||||
10. **Monetization angle — are there API opportunities we're missing?**
|
||||
We're currently selling detection + monitoring. Are there adjacent features that would justify higher tiers or a subscription model?
|
||||
- Automatic update execution (trigger a server update with one click)?
|
||||
- Changelogs pulled from CurseForge/Modrinth and displayed in-panel?
|
||||
- "Smart update" that checks if the new version breaks any custom configs?
|
||||
- Integration with backup systems to auto-backup before updating?
|
||||
|
||||
---
|
||||
|
||||
## Context That Might Help
|
||||
|
||||
- **Pterodactyl Panel 1.12.2** — this is the specific version we target. Blueprint beta-2026-01 for extension framework.
|
||||
- **Wings daemon** — gives us sandboxed filesystem access to `/home/container/` via `DaemonFileRepository`. Network calls to Wings are expensive — Gemini previously told us never do this on page load, only in background cron.
|
||||
- **CurseForge API** — uses `x-api-key` header. Returns `displayName` (messy) and `dateModified`. No clean semver.
|
||||
- **Modrinth API** — cleaner data, uses project slugs, returns semantic versions.
|
||||
- **FTB API** — `https://api.modpacks.ch/public/modpack/{id}` — returns clean version numbers.
|
||||
- **Technic API** — `https://api.technicpack.net/modpack/{slug}` — returns build numbers.
|
||||
- **`modpack_installations` table** — this is actually from a DIFFERENT Blueprint extension ("Modpack Installer") that Michael already has installed. We're reading its data. We don't own this table. It could change between Pterodactyl versions or installer extension updates.
|
||||
- **22 servers** — Michael's panel. About 19 are CurseForge, 0 currently Modrinth, 0 FTB (they show as CurseForge because FTB packs are also on CurseForge), 0 Technic.
|
||||
- **BuiltByBit market** — our target customers are Minecraft hosting companies. The average customer probably has 10-100 servers. Some large hosts have 500+.
|
||||
- **We cannot require egg changes** — this was Gemini's own guidance from the April 6 hybrid detection consultation. Egg changes require customer cooperation and break plug-and-play.
|
||||
- **No real-time data** — the background cron is the only way to get data. The console widget triggers a live API call but only to the platform (CurseForge/Modrinth), not to the server itself.
|
||||
|
||||
---
|
||||
|
||||
## What We're Hoping For
|
||||
|
||||
Gemini, we want your best thinking on this. Not just incremental improvements — if you see a fundamentally different architecture that would make this product 10x better, tell us. Michael has been working on this for 6+ hours tonight and he's frustrated that it "worked but not the way he hoped." We want to end this session with a clear v1.1.0 vision that we're genuinely excited to build.
|
||||
|
||||
Wild ideas are explicitly welcome. If you think we're solving the wrong problem entirely, tell us that too.
|
||||
|
||||
---
|
||||
|
||||
Thanks Gemini! This one's a meaty one and we appreciate you. 🔥❄️
|
||||
|
||||
— Michael (The Wizard) + Claude (Chronicler #84 — The Meridian)
|
||||
**Firefrost Gaming | Fire + Frost + Foundation = Where Love Builds Legacy** 💙🔥❄️
|
||||
Reference in New Issue
Block a user