OmniVoice-Studio / frontend /src /components /CheckpointBanner.jsx
Lê Phi Nam
Deploy to HF Space
94004a2
Raw
History Blame Contribute Delete
2.88 kB
import React from 'react';
import { CheckCircle, ArrowRight, X, Sparkles, Languages, Mic } from 'lucide-react';
import { Button } from '../ui';
import './Misc.css';
/**
* Phase 4.3 — between-stage checkpoint banner.
*
* The dub pipeline has three natural review points (post-ASR, post-translate,
* post-generate). Each one is a chance for the user to spot a mistake before
* it compounds into the next stage. This banner makes that review window
* explicit instead of silently leaving the user on the segment editor with
* no cue about what to do next.
*
* Render it above the segment table. Pass `onContinue` to advance the
* pipeline directly from the banner's CTA (translate, generate, etc).
*/
const STAGE_CONFIG = {
asr: {
icon: Mic,
accent: '#b8bb26',
title: 'Transcripts ready',
cta: 'Translate',
ctaIcon: Languages,
hint: 'Fix any ASR errors now — tight diction saves TTS attempts later.',
},
translate: {
icon: Languages,
accent: '#83a598',
title: 'Translations ready',
cta: 'Generate dub',
ctaIcon: Sparkles,
hint: 'Skim the target text. Over-length lines get speed-boosted; you can also edit directly.',
},
done: {
icon: CheckCircle,
accent: '#8ec07c',
title: 'Dub complete',
cta: null,
hint: 'Review timing and sync ratios. Tweak any line and hit "Regen changed" for a fast partial redo.',
},
};
export default function CheckpointBanner({ stage, count, onContinue, onDismiss, continueLoading }) {
const cfg = STAGE_CONFIG[stage];
if (!cfg) return null;
const Icon = cfg.icon;
const CtaIcon = cfg.ctaIcon;
return (
<div
className="checkpoint-banner ckpt-banner"
style={{ borderLeft: `2px solid ${cfg.accent}` }}
role="status"
>
<Icon size={14} color={cfg.accent} className="ckpt-icon" />
<div className="ckpt-body">
<div className="ckpt-head">
<span className="ckpt-title">
{cfg.title}
</span>
{typeof count === 'number' && (
<span className="ckpt-count">
{count} segment{count === 1 ? '' : 's'}
</span>
)}
</div>
<span className="ckpt-hint">
{cfg.hint}
</span>
</div>
{cfg.cta && onContinue && (
<Button
variant="subtle"
size="sm"
onClick={onContinue}
loading={continueLoading}
leading={CtaIcon ? <CtaIcon size={10} /> : null}
trailing={<ArrowRight size={10} />}
>
{cfg.cta}
</Button>
)}
{onDismiss && (
<Button
variant="ghost"
size="sm"
onClick={onDismiss}
title="Dismiss — won't reappear for this stage until reload"
iconSize="sm"
>
<X size={10} />
</Button>
)}
</div>
);
}