From dd05a41567f5d2c22fa5903010add7d453bd9120 Mon Sep 17 00:00:00 2001 From: "Claude (Chronicler #83 - The Compiler)" Date: Sun, 12 Apr 2026 23:44:21 -0500 Subject: [PATCH] v1.1.0 Priority 3b: zero-click widget with recalibrate + ignore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Widget redesign: - Zero-click: loads cached status on mount via GET /status (no API calls) - Shows platform icon + modpack name + version comparison - Orange background + arrow (current → latest) when update available - Cyan + checkmark when up to date - Refresh button triggers manual check - Calibrate button opens dropdown with last 10 releases - Ignore button hides non-modpack servers - Current release highlighted in calibrate dropdown Co-Authored-By: Claude Opus 4.6 (1M context) --- .../archive/MSG-2026-04-13-keep-going-p3b.md | 22 ++ .../views/server/wrapper.tsx | 236 +++++++++++++----- 2 files changed, 190 insertions(+), 68 deletions(-) create mode 100644 docs/code-bridge/archive/MSG-2026-04-13-keep-going-p3b.md diff --git a/docs/code-bridge/archive/MSG-2026-04-13-keep-going-p3b.md b/docs/code-bridge/archive/MSG-2026-04-13-keep-going-p3b.md new file mode 100644 index 0000000..6e06de2 --- /dev/null +++ b/docs/code-bridge/archive/MSG-2026-04-13-keep-going-p3b.md @@ -0,0 +1,22 @@ +# Chronicler Dispatch — Keep Going with Priority 3b (Widget TSX) + +**Date:** April 13, 2026 +**From:** Chronicler #84 — The Meridian +**To:** Code + +--- + +Keep going. Full stack first, one consolidated deploy and test at the end. + +Build the widget TSX redesign (Priority 3b): +- Zero-click `useEffect` loading cached status on mount +- Shows: platform icon | current version → latest version +- Orange background when update available +- "Calibrate" button opens dropdown with last 10 releases +- "Ignore" button for non-modpack servers +- Uses the new GET `/status` endpoint (no API calls on load) +- Recalibrate uses GET `/releases` + POST `/calibrate` + +When done push and file a deploy request — I'll run the full consolidated deploy. + +*— Chronicler #84, The Meridian* diff --git a/services/modpack-version-checker/blueprint-extension/views/server/wrapper.tsx b/services/modpack-version-checker/blueprint-extension/views/server/wrapper.tsx index dd72305..75ac725 100644 --- a/services/modpack-version-checker/blueprint-extension/views/server/wrapper.tsx +++ b/services/modpack-version-checker/blueprint-extension/views/server/wrapper.tsx @@ -1,98 +1,198 @@ -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { ServerContext } from '@/state/server'; import http from '@/api/http'; -import { faCube } from '@fortawesome/free-solid-svg-icons'; +import { faCube, faSync, faEyeSlash, faCog } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import classNames from 'classnames'; -interface VersionData { - success: boolean; +interface StatusData { + configured: boolean; platform?: string; - modpack_id?: string; modpack_name?: string; current_version?: string; latest_version?: string; - status?: string; - message?: string; - error?: string; + update_available?: boolean; + last_checked?: string; + detection_method?: string; + is_ignored?: boolean; } +interface Release { + file_id: string; + version: string; + display_name: string; + release_date?: string; +} + +const platformIcons: Record = { + curseforge: '🔥', + modrinth: '🍃', + technic: '⚙️', + ftb: '📦', +}; + 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(null); + const [data, setData] = useState(null); + const [loading, setLoading] = useState(true); + const [checking, setChecking] = useState(false); + const [showCalibrate, setShowCalibrate] = useState(false); + const [releases, setReleases] = useState([]); + const [loadingReleases, setLoadingReleases] = useState(false); - const checkForUpdates = async () => { + // Zero-click: load cached status on mount + useEffect(() => { if (!uuid) return; + http.get(`/api/client/extensions/modpackchecker/servers/${uuid}/status`) + .then((res) => setData(res.data)) + .catch(() => setData(null)) + .finally(() => setLoading(false)); + }, [uuid]); - setStatus('loading'); + // Manual refresh + const refresh = async () => { + if (!uuid || checking) return; + setChecking(true); try { - const response = await http.post(`/api/client/extensions/modpackchecker/servers/${uuid}/check`); - setData(response.data); - setStatus(response.data.success ? 'success' : 'error'); - } catch (error: unknown) { - const err = error as { response?: { status?: number } }; - if (err.response?.status === 429) { - setData({ success: false, error: 'rate_limited' }); - } else if (err.response?.status === 404) { - setData({ success: false, error: 'not_found' }); - } else { - setData({ success: false, error: 'api_error' }); - } - setStatus('error'); + await http.post(`/api/client/extensions/modpackchecker/servers/${uuid}/check`); + const res = await http.get(`/api/client/extensions/modpackchecker/servers/${uuid}/status`); + setData(res.data); + } catch {} + setChecking(false); + }; + + // Open calibrate dropdown + const openCalibrate = async () => { + if (!uuid) return; + setShowCalibrate(true); + setLoadingReleases(true); + try { + const res = await http.get(`/api/client/extensions/modpackchecker/servers/${uuid}/releases`); + setReleases(res.data.releases || []); + } catch { + setReleases([]); } + setLoadingReleases(false); }; - // Convert error codes to short display messages - const getErrorMessage = (error?: string): string => { - if (!error) return 'Error'; - if (error.includes('detect') || error.includes('MODPACK')) return 'Not configured'; - if (error === 'rate_limited') return 'Wait 60s'; - if (error === 'not_found') return 'Not found'; - if (error === 'api_error') return 'API error'; - if (error.length > 20) return 'Check failed'; - return error; + // Select a release to calibrate + const selectRelease = async (release: Release) => { + if (!uuid) return; + try { + const res = await http.post(`/api/client/extensions/modpackchecker/servers/${uuid}/calibrate`, { + file_id: release.file_id, + version: release.version, + }); + setData((prev) => prev ? { + ...prev, + current_version: release.version, + update_available: res.data.update_available, + } : prev); + } catch {} + setShowCalibrate(false); }; - 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'; + // Ignore server + const toggleIgnore = async () => { + if (!uuid) return; + try { + const res = await http.post(`/api/client/extensions/modpackchecker/servers/${uuid}/ignore`); + setData((prev) => prev ? { ...prev, is_ignored: res.data.is_ignored } : prev); + } catch {} }; + if (loading) return null; + if (!data) return null; + if (data.is_ignored) return null; + + const hasUpdate = data.update_available; + const configured = data.configured; + + const bgColor = hasUpdate ? 'bg-orange-500' : configured ? 'bg-cyan-500' : 'bg-gray-700'; + const icon = data.platform ? (platformIcons[data.platform] || '📦') : '📦'; + return ( -
-
- -
- -
- -
-

- Modpack Version -

-
- {status === 'idle' && Click to check} - {status === 'loading' && Checking...} - {status === 'success' && data?.success && {data.latest_version}} - {(status === 'error' || (status === 'success' && !data?.success)) && ( - {getErrorMessage(data?.error || data?.message)} - )} +
+
+ +
+
+ +
+ +
+
+

+ {configured ? `${icon} ${data.modpack_name || 'Modpack'}` : 'Modpack Version'} +

+
+ + {configured && ( + + )} + +
+
+ +
+ {!configured && Not detected — waiting for cron} + {configured && !hasUpdate && ( + ✓ Up to date — {data.latest_version} + )} + {configured && hasUpdate && ( + + ↑ {data.current_version} → {data.latest_version} + + )} +
+ + {/* Calibrate dropdown */} + {showCalibrate && ( +
+

Select your installed version:

+ {loadingReleases &&

Loading releases...

} + {!loadingReleases && releases.length === 0 && ( +

No releases found

+ )} + {releases.map((r) => ( + + ))} + +
+ )}
); };