From c7d9dbda6bf728429105cff74ee4b1327030d956 Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Thu, 18 Jun 2026 10:44:28 +0000 Subject: [PATCH] feat(ui): Home editorial header + status line (north-star redesign) Assisted-by: Claude:claude-opus-4-8 [Claude Code] Signed-off-by: Ettore Di Giacinto --- .../http/react-ui/public/locales/de/home.json | 13 ++++ .../http/react-ui/public/locales/en/home.json | 13 ++++ .../http/react-ui/public/locales/es/home.json | 13 ++++ .../http/react-ui/public/locales/id/home.json | 13 ++++ .../http/react-ui/public/locales/it/home.json | 13 ++++ .../http/react-ui/public/locales/ko/home.json | 13 ++++ .../react-ui/public/locales/zh-CN/home.json | 13 ++++ core/http/react-ui/src/App.css | 35 +++++++-- core/http/react-ui/src/pages/Home.jsx | 78 +++++++------------ 9 files changed, 147 insertions(+), 57 deletions(-) diff --git a/core/http/react-ui/public/locales/de/home.json b/core/http/react-ui/public/locales/de/home.json index a79b388d1..0c8f29119 100644 --- a/core/http/react-ui/public/locales/de/home.json +++ b/core/http/react-ui/public/locales/de/home.json @@ -6,6 +6,19 @@ }, "resourceGpu": "GPU", "resourceRam": "RAM", + "greeting": { + "morning": "Good morning", + "afternoon": "Good afternoon", + "evening": "Good evening", + "night": "Working late" + }, + "statusLine": { + "modelsLoaded_one": "{{count}} model loaded", + "modelsLoaded_other": "{{count}} models loaded", + "noModelsLoaded": "No models loaded", + "nodes_one": "{{count}} node", + "nodes_other": "{{count}} nodes" + }, "assistant": { "title": "LocalAI per Chat verwalten", "description": "Modelle installieren, Backends wechseln, Konfigurationen bearbeiten und Status prüfen — durch Gespräche mit LocalAI.", diff --git a/core/http/react-ui/public/locales/en/home.json b/core/http/react-ui/public/locales/en/home.json index ddfdee071..331220a9f 100644 --- a/core/http/react-ui/public/locales/en/home.json +++ b/core/http/react-ui/public/locales/en/home.json @@ -6,6 +6,19 @@ }, "resourceGpu": "GPU", "resourceRam": "RAM", + "greeting": { + "morning": "Good morning", + "afternoon": "Good afternoon", + "evening": "Good evening", + "night": "Working late" + }, + "statusLine": { + "modelsLoaded_one": "{{count}} model loaded", + "modelsLoaded_other": "{{count}} models loaded", + "noModelsLoaded": "No models loaded", + "nodes_one": "{{count}} node", + "nodes_other": "{{count}} nodes" + }, "assistant": { "title": "Manage LocalAI by chatting", "description": "Install models, switch backends, edit configs and check status by talking to LocalAI.", diff --git a/core/http/react-ui/public/locales/es/home.json b/core/http/react-ui/public/locales/es/home.json index 00a651f5b..0c700c364 100644 --- a/core/http/react-ui/public/locales/es/home.json +++ b/core/http/react-ui/public/locales/es/home.json @@ -6,6 +6,19 @@ }, "resourceGpu": "GPU", "resourceRam": "RAM", + "greeting": { + "morning": "Good morning", + "afternoon": "Good afternoon", + "evening": "Good evening", + "night": "Working late" + }, + "statusLine": { + "modelsLoaded_one": "{{count}} model loaded", + "modelsLoaded_other": "{{count}} models loaded", + "noModelsLoaded": "No models loaded", + "nodes_one": "{{count}} node", + "nodes_other": "{{count}} nodes" + }, "assistant": { "title": "Administra LocalAI chateando", "description": "Instala modelos, cambia backends, edita configuraciones y consulta el estado hablando con LocalAI.", diff --git a/core/http/react-ui/public/locales/id/home.json b/core/http/react-ui/public/locales/id/home.json index 87fbaeaab..ea8f22c10 100644 --- a/core/http/react-ui/public/locales/id/home.json +++ b/core/http/react-ui/public/locales/id/home.json @@ -6,6 +6,19 @@ }, "resourceGpu": "GPU", "resourceRam": "RAM", + "greeting": { + "morning": "Good morning", + "afternoon": "Good afternoon", + "evening": "Good evening", + "night": "Working late" + }, + "statusLine": { + "modelsLoaded_one": "{{count}} model loaded", + "modelsLoaded_other": "{{count}} models loaded", + "noModelsLoaded": "No models loaded", + "nodes_one": "{{count}} node", + "nodes_other": "{{count}} nodes" + }, "assistant": { "title": "Kelola LocalAI melalui obrolan", "description": "Instal model, ganti backend, edit konfigurasi dan periksa status dengan berbicara pada LocalAI.", diff --git a/core/http/react-ui/public/locales/it/home.json b/core/http/react-ui/public/locales/it/home.json index 733709a10..a67b66517 100644 --- a/core/http/react-ui/public/locales/it/home.json +++ b/core/http/react-ui/public/locales/it/home.json @@ -6,6 +6,19 @@ }, "resourceGpu": "GPU", "resourceRam": "RAM", + "greeting": { + "morning": "Buongiorno", + "afternoon": "Buon pomeriggio", + "evening": "Buonasera", + "night": "Al lavoro fino a tardi" + }, + "statusLine": { + "modelsLoaded_one": "{{count}} modello caricato", + "modelsLoaded_other": "{{count}} modelli caricati", + "noModelsLoaded": "Nessun modello caricato", + "nodes_one": "{{count}} nodo", + "nodes_other": "{{count}} nodi" + }, "assistant": { "title": "Gestisci LocalAI chattando", "description": "Installa modelli, cambia backend, modifica configurazioni e controlla lo stato parlando con LocalAI.", diff --git a/core/http/react-ui/public/locales/ko/home.json b/core/http/react-ui/public/locales/ko/home.json index 170343b2a..c93c12851 100644 --- a/core/http/react-ui/public/locales/ko/home.json +++ b/core/http/react-ui/public/locales/ko/home.json @@ -6,6 +6,19 @@ }, "resourceGpu": "GPU", "resourceRam": "RAM", + "greeting": { + "morning": "Good morning", + "afternoon": "Good afternoon", + "evening": "Good evening", + "night": "Working late" + }, + "statusLine": { + "modelsLoaded_one": "{{count}} model loaded", + "modelsLoaded_other": "{{count}} models loaded", + "noModelsLoaded": "No models loaded", + "nodes_one": "{{count}} node", + "nodes_other": "{{count}} nodes" + }, "assistant": { "title": "채팅으로 LocalAI 관리", "description": "LocalAI와 대화하여 모델을 설치하고, 백엔드를 전환하고, 구성을 편집하고, 상태를 확인하세요.", diff --git a/core/http/react-ui/public/locales/zh-CN/home.json b/core/http/react-ui/public/locales/zh-CN/home.json index b17554a04..4f7517cf4 100644 --- a/core/http/react-ui/public/locales/zh-CN/home.json +++ b/core/http/react-ui/public/locales/zh-CN/home.json @@ -6,6 +6,19 @@ }, "resourceGpu": "GPU", "resourceRam": "RAM", + "greeting": { + "morning": "Good morning", + "afternoon": "Good afternoon", + "evening": "Good evening", + "night": "Working late" + }, + "statusLine": { + "modelsLoaded_one": "{{count}} model loaded", + "modelsLoaded_other": "{{count}} models loaded", + "noModelsLoaded": "No models loaded", + "nodes_one": "{{count}} node", + "nodes_other": "{{count}} nodes" + }, "assistant": { "title": "通过聊天管理 LocalAI", "description": "通过与 LocalAI 对话来安装模型、切换后端、编辑配置和查看状态。", diff --git a/core/http/react-ui/src/App.css b/core/http/react-ui/src/App.css index b21da1941..b02e88849 100644 --- a/core/http/react-ui/src/App.css +++ b/core/http/react-ui/src/App.css @@ -5619,16 +5619,41 @@ select.input { /* Home page */ .home-page { - flex: 1; display: flex; flex-direction: column; - align-items: center; - justify-content: center; - max-width: 52rem; + gap: var(--space-section); + max-width: var(--page-max-medium); margin: 0 auto; - padding: var(--spacing-2xl) var(--spacing-xl); + padding: var(--space-section) var(--spacing-lg); width: 100%; +} +.home-header { + display: flex; + align-items: flex-end; + justify-content: space-between; + gap: var(--spacing-lg); + flex-wrap: wrap; +} +.home-eyebrow { + font-family: var(--font-mono); + font-size: var(--text-xs); + text-transform: uppercase; + letter-spacing: 0.14em; + color: var(--color-eyebrow); +} +.home-greeting { + font-family: var(--font-serif); + font-size: clamp(2rem, 1.4rem + 3vw, var(--text-4xl)); + font-weight: 440; + letter-spacing: -0.02em; + line-height: 1.05; + margin: var(--spacing-xs) 0 0; +} +.home-status-line { + display: flex; + align-items: center; gap: var(--spacing-md); + flex-wrap: wrap; } .home-hero { text-align: center; diff --git a/core/http/react-ui/src/pages/Home.jsx b/core/http/react-ui/src/pages/Home.jsx index 2a23c1417..f3a4957d7 100644 --- a/core/http/react-ui/src/pages/Home.jsx +++ b/core/http/react-ui/src/pages/Home.jsx @@ -12,12 +12,9 @@ import HomeConnect from '../components/HomeConnect' import { useResources } from '../hooks/useResources' import { fileToBase64, backendControlApi, systemApi, modelsApi, mcpApi, nodesApi } from '../utils/api' import { API_CONFIG } from '../utils/config' - -function formatBytes(bytes) { - if (!bytes || bytes === 0) return null - const gb = bytes / (1024 * 1024 * 1024) - return gb >= 1 ? `${gb.toFixed(1)} GB` : `${(bytes / (1024 * 1024)).toFixed(0)} MB` -} +import { greetingKey } from '../utils/greeting' +import StatusPill from '../components/StatusPill' +import { staggerStyle } from '../hooks/useStagger' export default function Home() { const navigate = useNavigate() @@ -287,62 +284,39 @@ export default function Home() { const hasModels = modelsLoading || configuredModels.length > 0 const loadedCount = loadedModels.length - // Resource display + // Resource display - folded into the editorial status line. const resType = resources?.type const usagePct = resources?.aggregate?.usage_percent ?? resources?.ram?.usage_percent ?? 0 - const pctColor = usagePct > 90 ? 'var(--color-error)' : usagePct > 70 ? 'var(--color-warning)' : 'var(--color-success)' - - // Cluster resource display (distributed mode) - const clusterUsagePct = clusterData?.totalMem > 0 ? ((clusterData.usedMem / clusterData.totalMem) * 100) : 0 - const clusterPctColor = clusterUsagePct > 90 ? 'var(--color-error)' : clusterUsagePct > 70 ? 'var(--color-warning)' : 'var(--color-success)' return (
{hasModels ? ( <> - {/* Hero with logo */} -
- {branding.instanceName} -
- - {/* Resource monitor - prominent placement */} - {distributedMode && clusterData && clusterData.totalMem > 0 ? ( -
-
- - {clusterData.isGPU ? t('cluster.vram') : t('cluster.ram')} - - {formatBytes(clusterData.usedMem)} / {formatBytes(clusterData.totalMem)} - -
-
-
-
-
- - {t('cluster.nodesOnline', { healthy: clusterData.healthyCount, total: clusterData.totalCount })} -
+ {/* Editorial header */} +
+
+ {branding.instanceName} +

{t(`greeting.${greetingKey()}`)}

- ) : !distributedMode && resources ? ( -
-
- - {resType === 'gpu' ? t('resourceGpu') : t('resourceRam')} - - {usagePct.toFixed(0)}% - -
-
-
+ 0 ? 'healthy' : 'idle'} + label={loadedCount > 0 ? t('statusLine.modelsLoaded', { count: loadedCount }) : t('statusLine.noModelsLoaded')} + /> + {distributedMode && clusterData && ( + 0 ? 'healthy' : 'error'} + label={t('statusLine.nodes', { count: clusterData.totalCount })} /> -
+ )} + {!distributedMode && resources && ( + +