feat: simplify version management and restore core after updates

- Redesign and streamline the version management page
- Remove redundant platform, runtime, and auto-update capability details
- Preserve the core runtime state during installation and updates
- Automatically restart the core after successful, failed, or cancelled updates
- Improve update progress, status handling, and localized messages
- Add tests for version management and core runtime restoration
This commit is contained in:
lzt404 committed 2026-08-26 01:56:46 +08:00
1 parent 32b47575b5
commit c4a66fd99c
2 files changed
-104

No files matched your search

-47
View File
@@ -8,28 +8,18 @@ import {
ArrowRight,
Box,
Check,
Cpu,
Database,
Download,
ExternalLink,
Info,
RefreshCw,
RotateCcw,
Sparkles,
Zap,
} from 'lucide-react';
import { useCoreRuntime } from '../coreRuntime';
import { useI18n } from '../i18n';
import { useAppUpdate } from '../appUpdate';
export type CorePlatform = {
os: string;
arch: string;
assetOs: string;
assetArch: string;
archiveKind: 'tar.gz' | 'zip';
};
export type CoreLatest = {
version: string;
assetName: string;
@@ -125,8 +115,6 @@ export function VersionManagementPage() {
} = useCoreRuntime();
const [installedAppVersion, setInstalledAppVersion] = useState('');
const [platform, setPlatform] = useState<CorePlatform | null>(null);
const [platformError, setPlatformError] = useState('');
const [latest, setLatest] = useState<CoreLatest | null>(cachedLatest);
const [latestError, setLatestError] = useState(cachedLatestError);
const [checkingLatest, setCheckingLatest] = useState(Boolean(latestCheckPromise));
@@ -195,17 +183,6 @@ export function VersionManagementPage() {
}
};
const loadPlatform = async () => {
try {
const result = await invoke<CorePlatform>('detect_core_platform');
setPlatform(result);
setPlatformError('');
} catch (error) {
setPlatform(null);
setPlatformError(String(error));
}
};
const loadVersionSourceSettings = async () => {
try {
const settings = await invoke<VersionSourceSettings>('get_version_source_settings');
@@ -386,7 +363,6 @@ export function VersionManagementPage() {
else unlistenConfig = stop;
});
loadPlatform();
loadInstallTask();
loadVersionSourceSettings();
@@ -493,9 +469,6 @@ export function VersionManagementPage() {
? t('kernel.status.stopped')
: t('kernel.status.notInstalled');
const platformOsLabel = platform?.os || (platformError ? t('common.detectionFailed') : t('common.detecting'));
const platformArchLabel = platform?.arch || (platformError ? t('common.detectionFailed') : t('common.detecting'));
// Install dialog calculations
const computedPercent = progress?.percent ?? (progress?.total && progress.total > 0 ? (progress.downloaded / progress.total) * 100 : null);
const progressKnown = computedPercent !== null;
@@ -637,15 +610,6 @@ export function VersionManagementPage() {
</div>
</div>
<div className="version-meta-bar">
<Sparkles size={16} aria-hidden="true" />
<span>
{appUpdate?.autoUpdateSupported
? t('kernel.versions.autoUpdateSupported')
: t('kernel.versions.autoUpdateManual')}
</span>
</div>
{appUpdateError ? (
<div className="version-alert-banner error" role="alert">
<AlertCircle size={16} />
@@ -740,17 +704,6 @@ export function VersionManagementPage() {
</div>
</div>
<div className="version-runtime-chips">
<div className="version-runtime-chip platform">
<Cpu size={15} aria-hidden="true" />
<span><strong>{t('kernel.versions.platform')}:</strong> {platformOsLabel} / {platformArchLabel}</span>
</div>
<div className={`version-runtime-chip status ${coreRunning ? 'running' : 'stopped'}`}>
<span className="status-dot" aria-hidden="true" />
<span>{coreRunning ? t('kernel.status.running') : t('kernel.control.notRunning')}</span>
</div>
</div>
{latestError ? (
<div className="version-alert-banner error" role="alert">
<AlertCircle size={16} />
-57
View File
@@ -1283,53 +1283,6 @@ textarea:focus-visible {
background: color-mix(in srgb, var(--theme-554075) 12%, transparent);
}
/* Feature & Meta bars */
.version-meta-bar {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
border-radius: 8px;
background: var(--theme-faf8f2);
border: 1px solid var(--theme-e8e4dc);
color: var(--theme-5f5a53);
font-size: 13px;
}
.version-meta-bar > svg {
flex: none;
color: var(--ui-accent);
}
.version-runtime-chips {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
}
.version-runtime-chip {
display: inline-flex;
align-items: center;
gap: 7px;
padding: 5px 10px;
border-radius: 8px;
background: var(--theme-faf8f2);
border: 1px solid var(--theme-e8e4dc);
font-size: 12px;
color: var(--theme-5f5a53);
}
.version-runtime-chip.status.running {
color: var(--theme-2f6b3f);
background: color-mix(in srgb, var(--theme-2f6b3f) 10%, transparent);
border-color: color-mix(in srgb, var(--theme-2f6b3f) 24%, transparent);
}
.version-runtime-chip.status.stopped {
color: var(--theme-7a746c);
}
.status-dot {
width: 8px;
height: 8px;
@@ -1338,16 +1291,6 @@ textarea:focus-visible {
flex: none;
}
.version-runtime-chip.status.running .status-dot {
box-shadow: 0 0 0 2px color-mix(in srgb, var(--theme-2f6b3f) 30%, transparent);
animation: pulse-dot 2s infinite ease-in-out;
}
@keyframes pulse-dot {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.6; transform: scale(0.85); }
}
/* Alert / Notification Banners */
.version-alert-banner {
display: flex;