mirror of
https://github.com/nexu-io/open-design.git
synced 2026-05-31 19:04:39 +07:00
feat(web): drop elapsed timer and duplicate estimate from generation preview
The "usually 2–5 minutes" estimate showed twice (lead footnote + meta row) and the elapsed counter added little signal, so remove both: delete the meta row, stop falling back to the estimate footnote in the generating lead (render the lead only when live narration exists), and drop the now unused elapsed timer/util. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
4ca4df0cab
commit
876c0de2e6
4 changed files with 3 additions and 53 deletions
|
|
@ -257,20 +257,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
font-size: 12px;
|
||||
color: var(--text-faint);
|
||||
}
|
||||
|
||||
.metaDivider {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.retry {
|
||||
margin-top: 4px;
|
||||
min-height: 34px;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { useT } from '../i18n';
|
||||
import type { GenerationPreviewModel } from '../runtime/generation-preview';
|
||||
import { formatGenerationElapsed } from '../runtime/generation-preview';
|
||||
import { Icon } from './Icon';
|
||||
import styles from './GenerationPreviewStage.module.css';
|
||||
|
||||
|
|
@ -12,19 +10,9 @@ type Props = {
|
|||
|
||||
export function GenerationPreviewStage({ model, onRetry }: Props) {
|
||||
const t = useT();
|
||||
const [now, setNow] = useState(() => Date.now());
|
||||
|
||||
const generating = model.phase === 'generating';
|
||||
|
||||
useEffect(() => {
|
||||
if (!generating) return undefined;
|
||||
const id = window.setInterval(() => setNow(Date.now()), 1000);
|
||||
return () => window.clearInterval(id);
|
||||
}, [generating, model.startedAt]);
|
||||
|
||||
const elapsedSec = Math.max(0, Math.round((now - model.startedAt) / 1000));
|
||||
const elapsedLabel = formatGenerationElapsed(elapsedSec);
|
||||
|
||||
const stepLabels: Record<GenerationPreviewModel['steps'][number]['id'], string> = {
|
||||
understand: t('generationPreview.stepUnderstand'),
|
||||
generate: t('generationPreview.stepGenerate'),
|
||||
|
|
@ -47,7 +35,7 @@ export function GenerationPreviewStage({ model, onRetry }: Props) {
|
|||
? t('generationPreview.stoppedLead')
|
||||
: model.phase === 'awaiting-input'
|
||||
? t('generationPreview.awaitingLead')
|
||||
: model.activityLabel || t('generationPreview.footnote');
|
||||
: model.activityLabel;
|
||||
|
||||
const markIcon =
|
||||
model.phase === 'failed' ? 'close' : model.phase === 'stopped' ? 'stop' : 'sparkles';
|
||||
|
|
@ -69,11 +57,11 @@ export function GenerationPreviewStage({ model, onRetry }: Props) {
|
|||
<Icon name={markIcon} size={24} />
|
||||
</div>
|
||||
<h1 className={styles.title}>{title}</h1>
|
||||
{showSubstatus ? null : (
|
||||
{!showSubstatus && lead ? (
|
||||
<p className={styles.lead} data-live={generating && Boolean(model.activityLabel)}>
|
||||
{lead}
|
||||
</p>
|
||||
)}
|
||||
) : null}
|
||||
<div
|
||||
className={styles.progress}
|
||||
data-active={generating}
|
||||
|
|
@ -118,17 +106,6 @@ export function GenerationPreviewStage({ model, onRetry }: Props) {
|
|||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
{generating ? (
|
||||
<div className={styles.meta}>
|
||||
<span data-testid="generation-preview-elapsed">
|
||||
{t('generationPreview.elapsed', { elapsed: elapsedLabel })}
|
||||
</span>
|
||||
<span className={styles.metaDivider} aria-hidden>
|
||||
·
|
||||
</span>
|
||||
<span>{t('generationPreview.estimate')}</span>
|
||||
</div>
|
||||
) : null}
|
||||
{model.phase === 'failed' && onRetry ? (
|
||||
<button
|
||||
type="button"
|
||||
|
|
|
|||
|
|
@ -249,14 +249,6 @@ export function generationPreviewProgress(steps: GenerationPreviewStep[]): numbe
|
|||
return Math.max(8, Math.min(steps.some((step) => step.status === 'failed') ? 72 : 92, Math.round(score * 100)));
|
||||
}
|
||||
|
||||
export function formatGenerationElapsed(seconds: number): string {
|
||||
const safe = Math.max(0, Math.floor(seconds));
|
||||
if (safe < 60) return `${safe}s`;
|
||||
const minutes = Math.floor(safe / 60);
|
||||
const remainder = safe % 60;
|
||||
return remainder > 0 ? `${minutes}m ${remainder}s` : `${minutes}m`;
|
||||
}
|
||||
|
||||
function isActiveRunStatus(status: ChatMessage['runStatus']): boolean {
|
||||
return status === 'queued' || status === 'running';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import { describe, expect, it } from 'vitest';
|
|||
import {
|
||||
buildGenerationPreviewState,
|
||||
derivePrototypeGenerationSteps,
|
||||
formatGenerationElapsed,
|
||||
workspaceHasPreviewSurface,
|
||||
} from '../../src/runtime/generation-preview';
|
||||
import type { AgentEvent, ChatMessage } from '../../src/types';
|
||||
|
|
@ -309,8 +308,4 @@ describe('generation preview helpers', () => {
|
|||
).toBeNull();
|
||||
});
|
||||
|
||||
it('formats elapsed durations for the meta row', () => {
|
||||
expect(formatGenerationElapsed(42)).toBe('42s');
|
||||
expect(formatGenerationElapsed(125)).toBe('2m 5s');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue