({ projects, connected, selectedProjectId, artifactsByProject, discussionMode, onToggleDiscussion, onSelect, onResume, onPause, onRestart, onDelete, onQuickSubmit }: Props)
| 42 | const [refPapersInput, setRefPapersInput] = useState(''); |
| 43 | |
| 44 | const [showPaths, setShowPaths] = useState(false); |
| 45 | const [codebasesPath, setCodebasesPath] = useState(''); |
| 46 | const [datasetsPath, setDatasetsPath] = useState(''); |
| 47 | const [checkpointsPath, setCheckpointsPath] = useState(''); |
| 48 | const { t, locale } = useLocale(); |
| 49 | |
| 50 | const modeInfo: Record<SubmitMode, { label: string; icon: string; placeholder: string; desc: string }> = { |
| 51 | lab: { |
| 52 | label: t('project.mode.lab'), |
| 53 | icon: '🔬', |
| 54 | placeholder: t('project.placeholder.lab'), |
| 55 | desc: t('project.mode.lab_desc'), |
| 56 | }, |
| 57 | reproduce: { |
| 58 | label: t('project.mode.reproduce'), |
| 59 | icon: '📄', |
| 60 | placeholder: t('project.placeholder.reproduce'), |
| 61 | desc: t('project.mode.reproduce_desc'), |
| 62 | }, |
| 63 | }; |
| 64 | |
| 65 | const info = modeInfo[mode]; |
| 66 | const [showModeHelp, setShowModeHelp] = useState(false); |
| 67 | |
| 68 | const submit = () => { |
| 69 | const text = topicInput.trim(); |
| 70 | if (!text) return; |
| 71 | const angles = mode === 'lab' |
| 72 | ? anglesInput.split(/[,,、;;]/).map(s => s.trim()).filter(Boolean) |
| 73 | : []; |
| 74 | const paths: { codebases?: string; datasets?: string; checkpoints?: string } = {}; |
| 75 | if (codebasesPath.trim()) paths.codebases = codebasesPath.trim(); |
| 76 | if (datasetsPath.trim()) paths.datasets = datasetsPath.trim(); |
| 77 | if (checkpointsPath.trim()) paths.checkpoints = checkpointsPath.trim(); |
| 78 | onQuickSubmit(text, mode, angles, refPapersInput.trim(), paths); |
| 79 | setTopicInput(''); |
| 80 | setAnglesInput(''); |
| 81 | setRefPapersInput(''); |
| 82 | }; |
| 83 | |
| 84 | const onKey = (e: React.KeyboardEvent) => { |
| 85 | if (e.key === 'Enter' && !e.shiftKey && !e.nativeEvent.isComposing) { e.preventDefault(); submit(); } |
| 86 | }; |
| 87 | |
| 88 | const sorted = [...projects].sort((a, b) => { |
| 89 | const o: Record<string, number> = { running: 0, queued: 1, interrupted: 2, new: 3, completed: 4 }; |
| 90 | return (o[a.status] ?? 5) - (o[b.status] ?? 5); |
| 91 | }); |
| 92 | |
| 93 | const running = projects.filter(p => p.status === 'running').length; |
| 94 | const interrupted = projects.filter(p => p.status === 'interrupted').length; |
| 95 | |
| 96 | const goLabel = t('project.go_submit'); |
| 97 | |
| 98 | const toggleExpand = (projectId: string, e: React.MouseEvent) => { |
| 99 | e.stopPropagation(); |
| 100 | setExpandedId(prev => prev === projectId ? null : projectId); |
| 101 | }; |
nothing calls this directly
no test coverage detected