feat(translate): add run preflight and focus execution UX

This commit is contained in:
2026-07-16 07:52:52 +03:00
parent 8e2f393267
commit 20105f51c0
13 changed files with 421 additions and 12 deletions

View File

@@ -8,9 +8,9 @@
<!-- @RELATION DEPENDS_ON -> [TranslationRunResult] -->
<!-- @RELATION DEPENDS_ON -> [TranslateMetricsRoutesModule] -->
<!-- @RELATION BINDS_TO -> [translationRunStore] -->
<!-- @UX_STATE Idle — no run active, run cards visible -->
<!-- @UX_STATE Running — TranslationRunProgress bar visible, cards show active state -->
<!-- @UX_STATE Completed — run history list with expandable details, summary metrics bar -->
<!-- @UX_STATE Idle — run estimate and launch choices are the only primary content. -->
<!-- @UX_STATE Running — TranslationRunProgress is the sole primary content; setup and history are hidden. -->
<!-- @UX_STATE Completed — result remains primary; history and cumulative metrics are collapsed secondary content. -->
<!-- @UX_STATE Error — error banner above run history -->
<!-- @UX_FEEDBACK Toast on status change / run error / run complete -->
<!-- @UX_FEEDBACK BulkReplace modal button in recent runs header -->
@@ -61,6 +61,7 @@
let metricsLoading = $state(false);
let metricsError = $state(null);
let wasRunActive = false;
let secondaryDetailsOpen = $state(false);
// Confirmation dialog state
let showConfirmDialog = $state(false);
@@ -124,6 +125,7 @@
});
$effect(() => {
if (isRunActive) secondaryDetailsOpen = false;
if (wasRunActive && !isRunActive) loadJobMetrics();
wasRunActive = isRunActive;
});
@@ -199,12 +201,13 @@
<!-- Status display + transition -->
<div class="flex items-center gap-3 mb-4 p-3 bg-surface-muted rounded-lg">
<span class="text-sm text-text-muted">{_t.translate?.config?.status}:</span>
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium {statusClass}">
{getJobStatusLabel(status)}
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium {isRunActive ? 'bg-primary-light text-primary' : statusClass}">
{isRunActive ? (_t.translate?.config?.running || 'Running') : getJobStatusLabel(status)}
</span>
</div>
<div class="space-y-4">
{#if !isRunActive}
<!-- Run evidence: real source scope, local Lingua distribution, and cost before execution -->
<div class="border border-border rounded-lg bg-surface-card p-4" aria-busy={preflightLoading}>
<div class="flex flex-wrap items-center justify-between gap-3">
@@ -303,6 +306,7 @@
</div>
</div>
</div>
{/if}
{#if runError}
<div class="bg-destructive-light border border-destructive-light rounded-lg p-3">
@@ -319,6 +323,24 @@
/>
{/if}
{#if !isRunActive}
<!-- Secondary context: it must not compete with the next launch or an active run. -->
<div class="rounded-lg border border-border bg-surface-card">
<button
type="button"
onclick={() => secondaryDetailsOpen = !secondaryDetailsOpen}
aria-expanded={secondaryDetailsOpen}
class="flex w-full items-center justify-between gap-3 px-4 py-3 text-left text-sm font-medium text-text hover:bg-surface-muted"
>
<span>{_t.translate?.run?.secondary_details || 'History and job metrics'}</span>
<span class="text-xs font-normal text-text-muted">
{secondaryDetailsOpen
? (_t.translate?.run?.hide_details || 'Hide details')
: (_t.translate?.run?.show_details || 'Show details')}
</span>
</button>
{#if secondaryDetailsOpen}
<div class="border-t border-border p-4 space-y-4">
<!-- Summary metrics bar -->
{#if jobMetrics}
<div class="bg-surface-card border border-border rounded-lg p-4">
@@ -401,7 +423,7 @@
{/if}
{#if completedRuns.length > 0}
<div class="mt-4">
<div>
<div class="flex items-center justify-between mb-2">
<h4 class="text-sm font-medium text-text">{_t.translate?.config?.recent_runs}</h4>
<button
@@ -489,6 +511,10 @@
{/if}
</div>
</div>
{/if}
</div>
{/if}
</div>
{/if}
</div>

View File

@@ -527,6 +527,7 @@
"preflight_rows_sec": "rows/s",
"preflight_languages": "Source language distribution",
"preflight_lingua_slow": "Lingua exceeded the performance threshold. The run will continue without local language detection.",
"secondary_details": "History and job metrics",
"loading": "Loading run status...",
"loading_result": "Loading result...",
"result_title": "Run Result",

View File

@@ -528,6 +528,7 @@
"preflight_rows_sec": "строк/с",
"preflight_languages": "Распределение исходных языков",
"preflight_lingua_slow": "Lingua превысила порог производительности. Перевод продолжится без локального определения языка.",
"secondary_details": "История и метрики задания",
"loading": "Загрузка статуса запуска...",
"loading_result": "Загрузка результата...",
"result_title": "Результат запуска",