feat(ui): redesign the version management page
This commit is contained in:
1 parent
3b9e3b9fd4
commit
6462699a3d
2 files changed
+462
-176
No files matched your search
+161
-111
@@ -2,7 +2,23 @@ import { useEffect, useRef, useState } from 'react';
|
||||
import { getVersion } from '@tauri-apps/api/app';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { listen } from '@tauri-apps/api/event';
|
||||
import { AlertCircle, Check, Copy, ExternalLink, Eye, EyeOff, Info } from 'lucide-react';
|
||||
import {
|
||||
AlertCircle,
|
||||
AppWindow,
|
||||
ArrowRight,
|
||||
Box,
|
||||
Check,
|
||||
Copy,
|
||||
Database,
|
||||
Download,
|
||||
ExternalLink,
|
||||
Eye,
|
||||
EyeOff,
|
||||
Info,
|
||||
PackageOpen,
|
||||
RefreshCw,
|
||||
RotateCcw,
|
||||
} from 'lucide-react';
|
||||
import { type CoreStatus, useCoreRuntime } from '../coreRuntime';
|
||||
import openaiIcon from '../assets/icons/openai-light.svg';
|
||||
import claudeIcon from '../assets/icons/claude.svg';
|
||||
@@ -668,6 +684,22 @@ export function KernelPage({ view = 'home' }: { view?: KernelView }) {
|
||||
const currentAppVersion = resolvedAppVersion
|
||||
? displayAppVersion(resolvedAppVersion)
|
||||
: t('common.detecting');
|
||||
const appVersionStatusLabel = appUpdateTask.running
|
||||
? t(`appUpdate.phase.${appUpdateTask.phase}` as Parameters<typeof t>[0])
|
||||
: appUpdateError
|
||||
? t('kernel.update.failed')
|
||||
: appUpdate?.updateAvailable
|
||||
? t('appUpdate.available', { version: displayAppVersion(appUpdate.latestVersion) })
|
||||
: appUpdate
|
||||
? t('appUpdate.upToDate')
|
||||
: t('appUpdate.phase.checking');
|
||||
const appVersionStatusTone = appUpdateError
|
||||
? 'error'
|
||||
: appUpdate?.updateAvailable
|
||||
? 'update'
|
||||
: appUpdate
|
||||
? 'success'
|
||||
: '';
|
||||
const latestLabel = checkingLatest
|
||||
? t('kernel.update.checking')
|
||||
: latestVersion || (latestError ? t('kernel.update.failed') : t('kernel.update.notChecked'));
|
||||
@@ -746,6 +778,51 @@ export function KernelPage({ view = 'home' }: { view?: KernelView }) {
|
||||
|
||||
return (
|
||||
<section className={`page kernel-page ${view === 'home' ? 'home-page' : 'version-management-page'}`}>
|
||||
{view === 'versions' ? (
|
||||
<header className="version-page-header">
|
||||
<div className="version-page-title">
|
||||
<span className="version-page-title-icon" aria-hidden="true">
|
||||
<PackageOpen size={22} />
|
||||
</span>
|
||||
<div>
|
||||
<h1>{t('app.nav.versions')}</h1>
|
||||
<p>{t('kernel.versions.offlineHint')}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="version-source-control">
|
||||
<div className="version-source-copy">
|
||||
<Info size={17} aria-hidden="true" />
|
||||
<div>
|
||||
<strong>{t('kernel.versions.gitcodeSource')}</strong>
|
||||
<span>
|
||||
{versionSource?.gitcodeAvailable === false
|
||||
? t('kernel.versions.gitcodeUnavailable')
|
||||
: t('kernel.versions.gitcodeSourceHint')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<label className="switch-control" title={t('kernel.versions.gitcodeSource')}>
|
||||
<input
|
||||
type="checkbox"
|
||||
role="switch"
|
||||
checked={versionSource?.preferGitcodeDownloads ?? false}
|
||||
disabled={
|
||||
(!versionSource?.gitcodeAvailable && !versionSource?.preferGitcodeDownloads)
|
||||
|| versionSourceSaving
|
||||
|| appUpdateTask.running
|
||||
|| installing
|
||||
}
|
||||
aria-label={t('kernel.versions.gitcodeSource')}
|
||||
onChange={(event) => void updateVersionSource(event.currentTarget.checked)}
|
||||
/>
|
||||
<span className="switch-track" />
|
||||
</label>
|
||||
{versionSourceError ? <p className="error version-source-error" role="alert">{versionSourceError}</p> : null}
|
||||
</div>
|
||||
</header>
|
||||
) : null}
|
||||
|
||||
<div className={view === 'home' ? 'kernel-layout home-layout' : 'kernel-layout version-management-layout'}>
|
||||
{view === 'home' ? (
|
||||
<div className="panel control-panel">
|
||||
@@ -822,85 +899,36 @@ export function KernelPage({ view = 'home' }: { view?: KernelView }) {
|
||||
) : null}
|
||||
|
||||
{view === 'versions' ? (
|
||||
<div className="panel version-source-panel">
|
||||
<div className="version-source-copy">
|
||||
<Info size={18} aria-hidden="true" />
|
||||
<div>
|
||||
<strong>{t('kernel.versions.gitcodeSource')}</strong>
|
||||
<span>
|
||||
{versionSource?.gitcodeAvailable === false
|
||||
? t('kernel.versions.gitcodeUnavailable')
|
||||
: t('kernel.versions.gitcodeSourceHint')}
|
||||
<article className="panel version-product-card software-update-panel">
|
||||
<div className="version-product-heading">
|
||||
<div className="version-product-identity">
|
||||
<span className="version-product-icon software" aria-hidden="true">
|
||||
<AppWindow size={21} />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<label className="switch-control" title={t('kernel.versions.gitcodeSource')}>
|
||||
<input
|
||||
type="checkbox"
|
||||
role="switch"
|
||||
checked={versionSource?.preferGitcodeDownloads ?? false}
|
||||
disabled={
|
||||
(!versionSource?.gitcodeAvailable && !versionSource?.preferGitcodeDownloads)
|
||||
|| versionSourceSaving
|
||||
|| appUpdateTask.running
|
||||
|| installing
|
||||
}
|
||||
aria-label={t('kernel.versions.gitcodeSource')}
|
||||
onChange={(event) => void updateVersionSource(event.currentTarget.checked)}
|
||||
/>
|
||||
<span className="switch-track" />
|
||||
</label>
|
||||
{versionSourceError ? <p className="error version-source-error" role="alert">{versionSourceError}</p> : null}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{view === 'versions' ? (
|
||||
<div className="panel software-update-panel">
|
||||
<div className="panel-heading">
|
||||
<div>
|
||||
<h2>{t('appUpdate.title')}</h2>
|
||||
<p className={appUpdateError ? 'error' : appUpdate?.updateAvailable ? 'success' : ''}>
|
||||
{appUpdateError
|
||||
|| (appUpdate?.updateAvailable
|
||||
? t('appUpdate.available', { version: displayAppVersion(appUpdate.latestVersion) })
|
||||
: appUpdate
|
||||
? t('appUpdate.upToDate')
|
||||
: t('appUpdate.phase.checking'))}
|
||||
</p>
|
||||
</div>
|
||||
{!appUpdate?.autoUpdateSupported && (
|
||||
<span className={`state-pill ${appUpdateError ? 'error' : 'success'}`}>
|
||||
{t('appUpdate.manualFallback')}
|
||||
</span>
|
||||
)}
|
||||
<span className={`state-pill ${appVersionStatusTone}`} title={appUpdateError || appVersionStatusLabel}>
|
||||
{appVersionStatusLabel}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<dl className="panel-detail-grid software-update-details">
|
||||
<div className="panel-detail-row">
|
||||
<dt>{t('appUpdate.current')}</dt>
|
||||
<dd>{currentAppVersion}</dd>
|
||||
<div className="version-comparison" aria-label={t('appUpdate.title')}>
|
||||
<div className="version-value-block current">
|
||||
<span>{t('appUpdate.current')}</span>
|
||||
<strong>{currentAppVersion}</strong>
|
||||
</div>
|
||||
<div className="panel-detail-row">
|
||||
<dt>{t('appUpdate.latest')}</dt>
|
||||
<dd>{appUpdate ? displayAppVersion(appUpdate.latestVersion) : t('common.detecting')}</dd>
|
||||
<ArrowRight className="version-comparison-arrow" size={20} aria-hidden="true" />
|
||||
<div className="version-value-block latest">
|
||||
<span>{t('appUpdate.latest')}</span>
|
||||
<strong>{appUpdate ? displayAppVersion(appUpdate.latestVersion) : t('common.detecting')}</strong>
|
||||
</div>
|
||||
<div className="panel-detail-row">
|
||||
<dt>{t('appUpdate.status')}</dt>
|
||||
<dd className={appUpdateError ? 'error' : appUpdate?.updateAvailable ? 'success' : ''}>
|
||||
{appUpdateTask.running
|
||||
? t(`appUpdate.phase.${appUpdateTask.phase}` as Parameters<typeof t>[0])
|
||||
: appUpdateError
|
||||
? t('kernel.update.failed')
|
||||
: appUpdate?.updateAvailable
|
||||
? t('appUpdate.available', { version: displayAppVersion(appUpdate.latestVersion) })
|
||||
: appUpdate
|
||||
? t('appUpdate.upToDate')
|
||||
: t('appUpdate.phase.checking')}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="panel-detail-row">
|
||||
<dt>{t('appUpdate.catalog.label')}</dt>
|
||||
<dd
|
||||
</div>
|
||||
|
||||
<div className="version-card-support">
|
||||
<Database size={17} aria-hidden="true" />
|
||||
<div>
|
||||
<strong>{t('appUpdate.catalog.label')}</strong>
|
||||
<span
|
||||
className={catalogUpdateError ? 'error' : catalogUpdateNotice ? 'success' : ''}
|
||||
role={catalogUpdateError ? 'alert' : undefined}
|
||||
aria-live="polite"
|
||||
@@ -908,9 +936,17 @@ export function KernelPage({ view = 'home' }: { view?: KernelView }) {
|
||||
{catalogUpdateError
|
||||
|| catalogUpdateNotice
|
||||
|| t('appUpdate.catalog.description')}
|
||||
</dd>
|
||||
</span>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div className="version-card-note-slot">
|
||||
{appUpdateError ? (
|
||||
<p className="version-card-note error" role="alert">{appUpdateError}</p>
|
||||
) : !appUpdate?.autoUpdateSupported ? (
|
||||
<p className="version-card-note">{t('appUpdate.manualFallback')}</p>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="button-row panel-action-row software-update-actions">
|
||||
<button
|
||||
@@ -919,6 +955,7 @@ export function KernelPage({ view = 'home' }: { view?: KernelView }) {
|
||||
disabled={checkingAppUpdate || appUpdateTask.running}
|
||||
onClick={() => void checkAppUpdate()}
|
||||
>
|
||||
<RefreshCw size={15} aria-hidden="true" />
|
||||
{checkingAppUpdate ? t('appUpdate.checking') : t('appUpdate.check')}
|
||||
</button>
|
||||
{appUpdate?.updateAvailable && appUpdate.autoUpdateSupported ? (
|
||||
@@ -928,10 +965,15 @@ export function KernelPage({ view = 'home' }: { view?: KernelView }) {
|
||||
disabled={appUpdateTask.running}
|
||||
onClick={requestAppUpdate}
|
||||
>
|
||||
<Download size={15} aria-hidden="true" />
|
||||
{t('appUpdate.installNow')}
|
||||
</button>
|
||||
) : (
|
||||
<button type="button" className="secondary-button" onClick={() => void openAppRelease()}>
|
||||
<button
|
||||
type="button"
|
||||
className={appUpdate?.updateAvailable ? 'primary-button' : 'secondary-button'}
|
||||
onClick={() => void openAppRelease()}
|
||||
>
|
||||
{t('appUpdate.openRelease')} <ExternalLink size={14} aria-hidden="true" />
|
||||
</button>
|
||||
)}
|
||||
@@ -942,51 +984,55 @@ export function KernelPage({ view = 'home' }: { view?: KernelView }) {
|
||||
onClick={() => void updateCodexModelCatalog()}
|
||||
title={t('appUpdate.catalog.updateHint')}
|
||||
>
|
||||
<Database size={15} aria-hidden="true" />
|
||||
{catalogUpdating
|
||||
? t('appUpdate.catalog.updating')
|
||||
: t('appUpdate.catalog.update')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
) : null}
|
||||
|
||||
{view === 'versions' ? (
|
||||
<div className="panel version-panel">
|
||||
<div className="panel-heading">
|
||||
<div className="version-heading-inline">
|
||||
<h2>{t('kernel.versions.title')}</h2>
|
||||
<span className="version-offline-hint">
|
||||
{t('kernel.versions.offlineHint')}
|
||||
<article className="panel version-product-card version-panel">
|
||||
<div className="version-product-heading">
|
||||
<div className="version-product-identity">
|
||||
<span className="version-product-icon core" aria-hidden="true">
|
||||
<Box size={21} />
|
||||
</span>
|
||||
<h2>{t('kernel.versions.title')}</h2>
|
||||
</div>
|
||||
<span className={`state-pill ${versionStatusTone}`} title={versionStatusLabel}>
|
||||
{versionStatusLabel}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="version-core-values">
|
||||
<div className="version-value-block current">
|
||||
<span>{t('kernel.versions.current')}</span>
|
||||
<strong>{currentVersion || t('kernel.status.notInstalled')}</strong>
|
||||
</div>
|
||||
<div className="version-value-block latest">
|
||||
<span>{t('kernel.versions.latest')}</span>
|
||||
<strong>{latestLabel}</strong>
|
||||
</div>
|
||||
<div className="version-value-block bundled">
|
||||
<span>{t('kernel.versions.bundled')}</span>
|
||||
<strong title={bundledCoreError || bundledCore?.assetName}>
|
||||
{bundledCore?.version ?? (bundledCoreError ? t('common.detectionFailed') : t('kernel.versions.notIncluded'))}
|
||||
</strong>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<dl className="panel-detail-grid">
|
||||
<div className="panel-detail-row">
|
||||
<dt>{t('kernel.versions.current')}</dt>
|
||||
<dd>{currentVersion || t('kernel.status.notInstalled')}</dd>
|
||||
<div className="version-card-support core-platform-line">
|
||||
<Box size={17} aria-hidden="true" />
|
||||
<div>
|
||||
<strong>{t('kernel.versions.platform')}</strong>
|
||||
<span title={platformError || undefined}>{platformOsLabel} / {platformArchLabel}</span>
|
||||
</div>
|
||||
<div className="panel-detail-row">
|
||||
<dt>{t('kernel.versions.latest')}</dt>
|
||||
<dd>{latestLabel}</dd>
|
||||
</div>
|
||||
<div className="panel-detail-row">
|
||||
<dt>{t('kernel.versions.bundled')}</dt>
|
||||
<dd title={bundledCoreError || bundledCore?.assetName}>
|
||||
{bundledCore?.version ?? (bundledCoreError ? t('common.detectionFailed') : t('kernel.versions.notIncluded'))}
|
||||
</dd>
|
||||
</div>
|
||||
<div className="panel-detail-row">
|
||||
<dt>{t('kernel.versions.platform')}</dt>
|
||||
<dd title={platformError || undefined}>{platformOsLabel} / {platformArchLabel}</dd>
|
||||
</div>
|
||||
<div className="panel-detail-row">
|
||||
<dt>{t('kernel.versions.updateStatus')}</dt>
|
||||
<dd className={versionStatusTone} title={versionStatusLabel}>
|
||||
{versionStatusLabel}
|
||||
</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<div className="version-card-note-slot" aria-hidden="true" />
|
||||
|
||||
<div className="button-row panel-action-row version-action-row">
|
||||
<button
|
||||
@@ -995,15 +1041,17 @@ export function KernelPage({ view = 'home' }: { view?: KernelView }) {
|
||||
disabled={busy}
|
||||
onClick={() => void checkLatest()}
|
||||
>
|
||||
<RefreshCw size={15} aria-hidden="true" />
|
||||
{checkingLatest ? t('kernel.update.checking') : t('kernel.versions.check')}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="secondary-button"
|
||||
className={latestVersion && currentVersion !== latestVersion ? 'primary-button' : 'secondary-button'}
|
||||
title={latestVersion ? t('kernel.versions.stopAndUpdateVersion', { version: latestVersion }) : t('kernel.versions.installLatest')}
|
||||
disabled={!latestVersion || busy}
|
||||
onClick={() => void stopAndInstallLatest()}
|
||||
>
|
||||
<Download size={15} aria-hidden="true" />
|
||||
{t('kernel.versions.installLatest')}
|
||||
</button>
|
||||
<button
|
||||
@@ -1013,20 +1061,22 @@ export function KernelPage({ view = 'home' }: { view?: KernelView }) {
|
||||
disabled={!currentVersion || installDisabled}
|
||||
onClick={() => installVersion(currentVersion)}
|
||||
>
|
||||
<RotateCcw size={15} aria-hidden="true" />
|
||||
{t('kernel.versions.reinstall')}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="primary-button"
|
||||
className={offlineInstallRequired ? 'primary-button' : 'secondary-button'}
|
||||
title={(bundledCore?.assetName ?? bundledCoreError) || t('kernel.versions.noBundled')}
|
||||
disabled={!bundledCore || offlineInstallDisabled}
|
||||
onClick={() => void installBundledCore()}
|
||||
>
|
||||
<PackageOpen size={15} aria-hidden="true" />
|
||||
{t('kernel.versions.offlineInstall')}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</article>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
|
||||
+301
-65
@@ -1011,6 +1011,69 @@ textarea:focus-visible {
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.version-management-page {
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.version-page-header {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
min-width: 0;
|
||||
padding: 22px 24px;
|
||||
border: 1px solid var(--theme-e3e1db);
|
||||
border-radius: 14px;
|
||||
background:
|
||||
linear-gradient(135deg, color-mix(in srgb, var(--ui-accent-soft) 62%, transparent), transparent 48%),
|
||||
var(--theme-fffdf8);
|
||||
box-shadow: var(--ui-panel-shadow);
|
||||
}
|
||||
|
||||
.version-page-title {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.version-page-title-icon,
|
||||
.version-product-icon {
|
||||
display: grid;
|
||||
flex: none;
|
||||
place-items: center;
|
||||
color: var(--ui-accent-strong);
|
||||
background: var(--ui-accent-soft);
|
||||
}
|
||||
|
||||
.version-page-title-icon {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
border: 1px solid color-mix(in srgb, var(--ui-accent) 22%, transparent);
|
||||
border-radius: 13px;
|
||||
}
|
||||
|
||||
.version-page-title > div {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.version-page-title h1 {
|
||||
margin: 0;
|
||||
color: var(--theme-2d2a26);
|
||||
font-size: 24px;
|
||||
line-height: var(--line-height-tight);
|
||||
}
|
||||
|
||||
.version-page-title p {
|
||||
margin: 5px 0 0;
|
||||
overflow: hidden;
|
||||
color: var(--theme-7a746c);
|
||||
font-size: var(--font-size-meta);
|
||||
line-height: var(--line-height-body);
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.config-subpage-panel {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
@@ -1025,14 +1088,16 @@ textarea:focus-visible {
|
||||
grid-column: auto;
|
||||
}
|
||||
|
||||
.version-source-panel {
|
||||
display: flex;
|
||||
min-height: 68px;
|
||||
flex-wrap: wrap;
|
||||
.version-source-control {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
padding-block: 14px;
|
||||
gap: 12px 18px;
|
||||
min-width: 0;
|
||||
padding: 12px 14px;
|
||||
border: 1px solid color-mix(in srgb, var(--ui-accent) 17%, var(--theme-e3e1db));
|
||||
border-radius: 10px;
|
||||
background: color-mix(in srgb, var(--theme-fffdf8) 82%, var(--ui-accent-soft));
|
||||
}
|
||||
|
||||
.version-source-copy {
|
||||
@@ -1044,6 +1109,8 @@ textarea:focus-visible {
|
||||
|
||||
.version-source-copy > svg {
|
||||
flex: none;
|
||||
align-self: flex-start;
|
||||
margin-top: 2px;
|
||||
color: var(--ui-accent);
|
||||
}
|
||||
|
||||
@@ -1055,45 +1122,200 @@ textarea:focus-visible {
|
||||
|
||||
.version-source-copy strong {
|
||||
color: var(--theme-39352f);
|
||||
font-size: var(--font-size-body);
|
||||
font-size: var(--font-size-label);
|
||||
}
|
||||
|
||||
.version-source-copy span {
|
||||
display: -webkit-box;
|
||||
overflow: hidden;
|
||||
color: var(--theme-7a746c);
|
||||
font-size: var(--font-size-meta);
|
||||
line-height: var(--line-height-body);
|
||||
font-size: var(--font-size-caption);
|
||||
line-height: 1.4;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
}
|
||||
|
||||
.version-source-error {
|
||||
width: 100%;
|
||||
grid-column: 1 / -1;
|
||||
margin: 0;
|
||||
font-size: var(--font-size-meta);
|
||||
}
|
||||
|
||||
.version-heading-inline {
|
||||
.version-product-card {
|
||||
display: grid;
|
||||
grid-template-rows: auto auto auto minmax(0, 1fr) auto;
|
||||
align-content: start;
|
||||
gap: 18px;
|
||||
padding: 26px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.version-product-heading {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
align-items: baseline;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.version-heading-inline h2 {
|
||||
.version-product-identity {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
align-items: center;
|
||||
gap: 11px;
|
||||
}
|
||||
|
||||
.version-product-icon {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.version-product-icon.core {
|
||||
color: var(--theme-554075);
|
||||
background: var(--theme-f1ecfb);
|
||||
}
|
||||
|
||||
.version-product-identity h2 {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.version-product-heading .state-pill {
|
||||
min-width: 0;
|
||||
max-width: 52%;
|
||||
flex: none;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.version-offline-hint {
|
||||
.version-comparison {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
min-height: 118px;
|
||||
padding: 20px;
|
||||
border: 1px solid var(--theme-e3e1db);
|
||||
border-radius: 12px;
|
||||
background: var(--theme-f4f1ea);
|
||||
}
|
||||
|
||||
.version-comparison-arrow {
|
||||
color: var(--theme-9a958d);
|
||||
}
|
||||
|
||||
.version-value-block {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
gap: 7px;
|
||||
}
|
||||
|
||||
.version-value-block span {
|
||||
color: var(--theme-8a847c);
|
||||
font-size: var(--font-size-meta);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.version-value-block strong {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
color: var(--theme-8a847c);
|
||||
font-size: var(--font-size-caption);
|
||||
font-weight: 400;
|
||||
color: var(--theme-2d2a26);
|
||||
font-size: var(--font-size-metric-compact);
|
||||
line-height: var(--line-height-tight);
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.kernel-layout > .version-panel > .panel-heading {
|
||||
padding-bottom: 10px;
|
||||
.version-value-block.latest strong {
|
||||
color: var(--ui-accent-strong);
|
||||
}
|
||||
|
||||
.version-core-values {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
min-height: 118px;
|
||||
border: 1px solid var(--theme-e3e1db);
|
||||
border-radius: 12px;
|
||||
background: var(--theme-f4f1ea);
|
||||
}
|
||||
|
||||
.version-core-values .version-value-block {
|
||||
align-content: center;
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
.version-core-values .version-value-block + .version-value-block {
|
||||
border-left: 1px solid var(--theme-e3e1db);
|
||||
}
|
||||
|
||||
.version-core-values .version-value-block strong {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.version-card-support {
|
||||
display: flex;
|
||||
min-width: 0;
|
||||
align-items: center;
|
||||
gap: 11px;
|
||||
min-height: 58px;
|
||||
padding: 11px 13px;
|
||||
border: 1px solid var(--theme-e3e1db);
|
||||
border-radius: 10px;
|
||||
background: var(--theme-faf8f2);
|
||||
}
|
||||
|
||||
.version-card-support > svg {
|
||||
flex: none;
|
||||
color: var(--theme-7a746c);
|
||||
}
|
||||
|
||||
.version-card-support > div {
|
||||
display: grid;
|
||||
min-width: 0;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.version-card-support strong,
|
||||
.version-card-support span {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.version-card-support strong {
|
||||
color: var(--theme-39352f);
|
||||
font-size: var(--font-size-meta);
|
||||
}
|
||||
|
||||
.version-card-support span {
|
||||
color: var(--theme-7a746c);
|
||||
font-size: var(--font-size-caption);
|
||||
}
|
||||
|
||||
.version-card-support span.success {
|
||||
color: var(--theme-2f6b3f);
|
||||
}
|
||||
|
||||
.version-card-support span.error {
|
||||
color: var(--theme-9f2d20);
|
||||
}
|
||||
|
||||
.version-card-note {
|
||||
align-self: start;
|
||||
margin: 0;
|
||||
color: var(--theme-7a746c);
|
||||
font-size: var(--font-size-caption);
|
||||
line-height: var(--line-height-body);
|
||||
}
|
||||
|
||||
.version-card-note-slot {
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.version-card-note.error {
|
||||
color: var(--theme-9f2d20);
|
||||
}
|
||||
|
||||
.client-api-panel {
|
||||
@@ -1295,6 +1517,14 @@ textarea:focus-visible {
|
||||
grid-template-rows: repeat(3, auto);
|
||||
}
|
||||
|
||||
.version-management-layout > .version-product-card {
|
||||
grid-template-rows: auto auto auto minmax(0, 1fr) auto;
|
||||
}
|
||||
|
||||
.version-management-layout > .version-product-card > .panel-action-row {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.kernel-layout > .panel > .panel-heading {
|
||||
min-height: 0;
|
||||
height: auto;
|
||||
@@ -1399,10 +1629,10 @@ textarea:focus-visible {
|
||||
}
|
||||
|
||||
.button-row.version-action-row {
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
grid-auto-flow: row;
|
||||
grid-auto-columns: unset;
|
||||
grid-auto-rows: 38px;
|
||||
grid-auto-rows: 40px;
|
||||
align-items: stretch;
|
||||
gap: 10px;
|
||||
}
|
||||
@@ -1421,30 +1651,23 @@ textarea:focus-visible {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.software-update-details {
|
||||
align-content: start;
|
||||
}
|
||||
|
||||
.software-update-details .panel-detail-row {
|
||||
grid-template-columns: 120px minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.software-update-details .panel-detail-row dt {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.software-update-actions {
|
||||
grid-template-columns: repeat(auto-fit, minmax(min(120px, 100%), 1fr));
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
grid-auto-flow: row;
|
||||
grid-auto-rows: 38px;
|
||||
grid-auto-rows: 40px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.software-update-actions > button:last-child:nth-child(3) {
|
||||
grid-column: 1 / -1;
|
||||
}
|
||||
|
||||
.software-update-actions > button,
|
||||
.version-action-row > button {
|
||||
width: 100%;
|
||||
height: 38px;
|
||||
min-height: 38px;
|
||||
height: 40px;
|
||||
min-height: 40px;
|
||||
gap: 7px;
|
||||
padding: 0 10px;
|
||||
line-height: normal;
|
||||
overflow-wrap: normal;
|
||||
@@ -1452,11 +1675,11 @@ textarea:focus-visible {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.software-update-panel .panel-heading p {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
@container workspace (min-width: 840px) {
|
||||
.version-page-header {
|
||||
grid-template-columns: minmax(260px, 0.8fr) minmax(360px, 1.2fr);
|
||||
}
|
||||
|
||||
.home-page {
|
||||
grid-template-columns: minmax(320px, 0.8fr) minmax(0, 1.2fr);
|
||||
grid-auto-rows: 1fr;
|
||||
@@ -1502,45 +1725,58 @@ textarea:focus-visible {
|
||||
|
||||
.version-management-layout {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
grid-template-rows: auto minmax(0, 1fr);
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.version-management-layout > .version-source-panel {
|
||||
grid-column: 1 / -1;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.version-management-layout > .panel {
|
||||
height: 100%;
|
||||
align-self: stretch;
|
||||
grid-template-rows: auto minmax(0, 1fr) auto;
|
||||
row-gap: clamp(12px, 2vh, 22px);
|
||||
}
|
||||
|
||||
.version-management-layout > .version-source-panel {
|
||||
height: auto;
|
||||
grid-template-rows: none;
|
||||
row-gap: 8px;
|
||||
}
|
||||
|
||||
.version-management-layout > .software-update-panel,
|
||||
.version-management-layout > .version-panel {
|
||||
grid-row: 2;
|
||||
}
|
||||
|
||||
.home-page > .home-layout > .control-panel > .panel-detail-grid,
|
||||
.version-management-layout > .panel > .panel-detail-grid,
|
||||
.home-page > .client-api-panel > .client-api-grid {
|
||||
align-content: space-evenly;
|
||||
}
|
||||
|
||||
.home-page > .home-layout > .control-panel > .panel-action-row,
|
||||
.version-management-layout > .panel > .panel-action-row {
|
||||
.home-page > .home-layout > .control-panel > .panel-action-row {
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@container workspace (max-width: 700px) {
|
||||
.version-page-header {
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.version-page-title p {
|
||||
overflow: visible;
|
||||
text-overflow: clip;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.version-product-heading {
|
||||
align-items: flex-start;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.version-product-heading .state-pill {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.version-core-values {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.version-core-values .version-value-block {
|
||||
padding: 14px 18px;
|
||||
}
|
||||
|
||||
.version-core-values .version-value-block + .version-value-block {
|
||||
border-top: 1px solid var(--theme-e3e1db);
|
||||
border-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.info-list {
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
|
||||
Reference in new issue
Block a user