Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion studio/web/src/components/MonitorDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,7 @@ export function EvalMetricsPanel({ state, connected, taskId }: {
} finally {
setRunning(false)
}
}, [pid, vid, taskId, selected, load])
}, [pid, vid, taskId, selected, load, t])

const latestByKey = useMemo(() => {
const out: Partial<Record<EvalMetricKey, { result: EvalMetricResult; value: number | null; state?: EvalMetricState }>> = {}
Expand Down
14 changes: 12 additions & 2 deletions studio/web/src/pages/project/Overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* Live 训练进度 (step/total/ETA) 不实装 —— 需 SSE/monitor state 整合,留 follow-up。
* "复制配置开新版本" / "调小 batch 重训" 需新后端 API,渲染为占位按钮 toast 提示。
*/
import { useEffect, useMemo, useState, type ReactNode } from 'react'
import { useCallback, useEffect, useMemo, useState, type ReactNode } from 'react'
import { useTranslation } from 'react-i18next'
import { useNavigate, useOutletContext } from 'react-router-dom'
import {
Expand All @@ -33,6 +33,7 @@ import { arBucket } from '../../lib/aspectRatio'
import { computePixelHist } from '../../lib/pixelBins'
import { useProjectCtx } from '../../context/ProjectContext'
import { useToast } from '../../components/Toast'
import { useEventStream } from '../../lib/useEventStream'

type OverviewTab = 'details' | 'tasks' | 'output'

Expand Down Expand Up @@ -1102,7 +1103,7 @@ export default function ProjectOverview() {

// 项目全部 task(最近优先)— 右栏 Recent runs + banner / Output 数据源
const [projTasks, setProjTasks] = useState<Task[]>([])
useEffect(() => {
const loadProjectTasks = useCallback(() => {
let cancelled = false
void api.listQueue()
.then((items) => {
Expand All @@ -1115,6 +1116,15 @@ export default function ProjectOverview() {
.catch(() => { if (!cancelled) setProjTasks([]) })
return () => { cancelled = true }
}, [project.id])
useEffect(() => loadProjectTasks(), [loadProjectTasks])

useEventStream((evt) => {
if (evt.type === 'train_loop_started'
|| evt.type === 'auto_epoch_backup_written'
|| evt.type === 'task_state_changed') {
loadProjectTasks()
}
})

const latestTask = useMemo(
() => projTasks.find((tk) => tk.version_id === selectedVid) ?? null,
Expand Down
5 changes: 4 additions & 1 deletion studio/web/src/pages/project/steps/PreprocessInpaint.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,10 @@ export default function PreprocessInpaintPage() {
() => images.find((im) => im.name === activeName) ?? null,
[images, activeName],
)
const activeHistory = activeName ? (historyByImage[activeName] ?? []) : []
const activeHistory = useMemo(
() => activeName ? (historyByImage[activeName] ?? []) : [],
[activeName, historyByImage],
)
const activeRedo = activeName ? (redoByImage[activeName] ?? []) : []
const activePaintStrokes = useMemo(
() => activeHistory.filter((h) => h.kind === 'paint').map((h) => h.stroke),
Expand Down
13 changes: 11 additions & 2 deletions studio/web/src/pages/queue/DataJobsPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,18 @@ export default function DataJobsPanel({
paused: t('status.paused'), scheduled: t('status.scheduled'),
}
return (
<button
<div
key={task.id}
role="button"
tabIndex={0}
onClick={() => navigate(`/queue/${task.id}`)}
onKeyDown={(e) => {
if (e.target !== e.currentTarget) return
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault()
navigate(`/queue/${task.id}`)
}
}}
title={t('queue.taskDetailTooltip')}
className={`card card-hover block overflow-hidden text-left p-0 cursor-pointer ${task.status === 'running' ? 'border border-accent bg-accent-soft' : 'border border-subtle bg-surface'}`}
data-testid={`job-row-${task.id}`}
Expand Down Expand Up @@ -188,7 +197,7 @@ export default function DataJobsPanel({
)}
</div>
</div>
</button>
</div>
)
}

Expand Down
28 changes: 26 additions & 2 deletions studio/web/src/pages/tools/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,27 @@ const EMPTY: Secrets = {
}
}

/** Keep the settings screen compatible with older secrets payloads.
*
* New sections are added over time, while an already running backend or a
* test fixture can still return the previous shape. The UI must not crash
* during that short mismatch. Secrets are only two levels deep, so a
* shallow merge per section is sufficient and preserves server values.
*/
function withSecretsDefaults(value: Secrets): Secrets {
const merged = { ...EMPTY, ...value } as Record<string, unknown>
for (const key of Object.keys(EMPTY) as (keyof Secrets)[]) {
const fallback = EMPTY[key]
const actual = value[key]
if (fallback && actual
&& typeof fallback === 'object' && !Array.isArray(fallback)
&& typeof actual === 'object' && !Array.isArray(actual)) {
merged[key as string] = { ...fallback, ...actual }
}
}
return merged as unknown as Secrets
}

const textInputClass = 'w-full px-2 py-1 outline-none rounded-sm bg-sunken border border-subtle text-sm text-fg-primary focus:border-accent'

const MODEL_DESCRIPTION_KEYS: Record<string, string> = {
Expand All @@ -242,7 +263,7 @@ export default function SettingsPage() {
// 本组件 mount/unmount(抽屉开关)不再触发重拉。`server` 别名保留是为了让下方
// 大段表单代码改动最小。
const {
secrets: server,
secrets: rawServer,
secretsError,
setSecrets: setServer,
catalog,
Expand All @@ -251,6 +272,10 @@ export default function SettingsPage() {
downloadBusy,
startDownload,
} = useSettingsData()
const server = useMemo(
() => (rawServer ? withSecretsDefaults(rawServer) : null),
[rawServer],
)
const [draft, setDraft] = useState<Secrets>(EMPTY)
const [error, setError] = useState<string | null>(null)
const [saving, setSaving] = useState(false)
Expand Down Expand Up @@ -2189,4 +2214,3 @@ function XformersSection() {
</details>
)
}

Loading