'use client';
import React from 'react';
import {
AlertCircle,
CheckCircle2,
Circle,
FileJson,
FileOutput,
FileText,
Image as ImageIcon,
Link2,
ListChecks,
Loader2,
PanelRightOpen,
X,
} from 'lucide-react';
import { ScrollArea } from '@/components/ui/scroll-area';
import { appStatusLabel } from '@/lib/i18n/common';
import { pickAppText } from '@/lib/i18n/core';
import { useAppI18n } from '@/lib/i18n/provider';
import type {
SessionProgressArtifactView,
SessionProgressStepView,
SessionProgressView,
} from '@/lib/session-progress';
import type { ProcessArtifact, ProcessRunStatus } from '@/types';
function formatShortTime(value: string, locale: 'zh-CN' | 'en-US') {
const date = new Date(value);
if (Number.isNaN(date.getTime())) return value;
return new Intl.DateTimeFormat(locale, {
hour: '2-digit',
minute: '2-digit',
}).format(date);
}
function statusTone(status: ProcessRunStatus) {
if (status === 'done') return 'text-[#2F8D50] bg-[#E3F1E7] border-[#B8D9C2]';
if (status === 'running') return 'text-[#2F6FCA] bg-[#E7EEF9] border-[#B8CBE8]';
if (status === 'error') return 'text-[#8A3A2D] bg-[#F0E5E1] border-[#D9BDB4]';
if (status === 'cancelled') return 'text-[#6A5E58] bg-[#ECE8E5] border-[#D8D2CE]';
return 'text-[#6A5E58] bg-[#F0ECE9] border-[#D8D2CE]';
}
function StepMarker({ step, index }: { step: SessionProgressStepView; index: number }) {
if (step.status === 'done') {
return (
);
}
if (step.status === 'running') {
return (
{index + 1}
);
}
if (step.status === 'error') {
return (
);
}
return (
);
}
function artifactIcon(type: ProcessArtifact['artifact_type']) {
if (type === 'json') return ;
if (type === 'image') return ;
if (type === 'link') return ;
if (type === 'markdown' || type === 'text') return ;
return ;
}
function ProgressHeader({ view }: { view: SessionProgressView }) {
const { locale } = useAppI18n();
const percent = view.progress.percent;
return (
{view.title}
{appStatusLabel(view.status, locale)}
{pickAppText(locale, '更新于', 'Updated')} {formatShortTime(view.updatedAt, locale)}
{view.progress.label}
{percent !== null && {percent}%}
{view.summary && (
{view.summary}
)}
);
}
function StepList({ steps }: { steps: SessionProgressStepView[] }) {
const { locale } = useAppI18n();
return (
{pickAppText(locale, '运行步骤', 'Run Steps')}
{pickAppText(locale, `${steps.length} 步`, `${steps.length} steps`)}
{steps.map((step, index) => (
{index < steps.length - 1 && }
{index + 1}. {step.title}
{step.actorName} · {formatShortTime(step.updatedAt, locale)}
{appStatusLabel(step.status, locale)}
{step.description && (
{step.description}
)}
{step.status === 'running' && (
{pickAppText(locale, '正在处理', 'In progress')}
)}
))}
);
}
function ArtifactRow({ artifact }: { artifact: SessionProgressArtifactView }) {
return (
{artifactIcon(artifact.type)}
{artifact.title}
{artifact.actorName} · {artifact.typeLabel}
{artifact.preview}
);
}
function ArtifactSection({ view }: { view: SessionProgressView }) {
const { locale } = useAppI18n();
return (
{pickAppText(locale, '生成内容', 'Generated Content')}
{pickAppText(locale, `${view.artifacts.length} 个`, `${view.artifacts.length} items`)}
{view.artifactTypeSummaries.length > 0 ? (
{view.artifactTypeSummaries.map((item) => (
{artifactIcon(item.type)}
{item.label}
{item.count}
))}
) : (
{pickAppText(locale, '暂时还没有生成内容。', 'No generated content yet.')}
)}
{view.artifacts.map((artifact) => (
))}
);
}
function ProgressPanel({
view,
onClose,
}: {
view: SessionProgressView;
onClose?: () => void;
}) {
const { locale } = useAppI18n();
return (
{pickAppText(locale, '当前会话的运行进度', 'Current Session Progress')}
{pickAppText(locale, '任务列表会自动刷新', 'Task updates refresh automatically')}
{onClose && (
)}
);
}
export function CurrentSessionProgressSidebar({ view }: { view: SessionProgressView }) {
const { locale } = useAppI18n();
const [mobileOpen, setMobileOpen] = React.useState(false);
return (
<>
{mobileOpen && (
)}
>
);
}