import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import useAppLayoutStore from '../../Store/AppLayoutSlice'; import useStudioStore from '../../Store/Studio/StudioSlice'; import { buildWorkflowVariableLabelMap, getWorkflowVariableLabel, } from './edit/workflowVariableLabels'; import { resolveBoundCharacterName, canUndoRun, canRerollRun, canAdvanceNextStep, getRunControls, hasRunControl, shouldShowFirstExport, shouldShowSaveAppendOverwrite, canSaveWorldbookEntry, canSwitchToNode, isInitBindCompleted } from './studioRunUtils'; import useCharacterStore from '../../Store/SideBarLeft/CharacterSlice'; import StudioContextBlockPopup from './StudioContextBlockPopup'; import StudioInsertionPopup from './StudioInsertionPopup'; import StudioRunChat from './StudioRunChat'; import StudioRunNodeGraph from './StudioRunNodeGraph'; import './StudioRunPage.css'; const RUN_STATUS_LABEL = { pending: '待开始', running: '运行中', paused: '已暂停', completed: '已完成', failed: '失败', cancelled: '已取消', }; const RUN_LIST_COLLAPSE_THRESHOLD = 4; const RUN_ITEM_HEIGHT = 56; const CONFIRM_ACTION_TIMEOUT_MS = 4000; function ConfirmTurnButton({ actionKey, pendingKey, label, className, disabled, title, onConfirm, onRequestConfirm, }) { const isPending = pendingKey === actionKey; return ( ); } function formatRunTime(iso) { if (!iso) return ''; try { const d = new Date(iso); return d.toLocaleString('zh-CN', { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', }); } catch { return iso; } } function runDisplayName(run) { if (run.title?.trim()) return run.title.trim(); return formatRunTime(run.createdAt) || '未命名运行'; } function InitBindGuidanceForm({ node, runAdvancing, onSubmit, compact = false }) { const displayParams = node?.displayParams || []; const [formValues, setFormValues] = useState({}); const [fieldErrors, setFieldErrors] = useState({}); useEffect(() => { const initial = {}; displayParams.forEach((dp) => { initial[dp.key] = ''; }); setFormValues(initial); setFieldErrors({}); }, [node?.nodeId, displayParams]); const handleChange = (key, value) => { setFormValues((prev) => ({ ...prev, [key]: value })); setFieldErrors((prev) => ({ ...prev, [key]: null })); }; const handleSubmit = (e) => { e.preventDefault(); const errors = {}; displayParams.forEach((dp) => { if (dp.required && !(formValues[dp.key] || '').trim()) { errors[dp.key] = `请填写${dp.label}`; } }); if (Object.keys(errors).length > 0) { setFieldErrors(errors); return; } onSubmit(formValues); }; if (displayParams.length === 0) { return
当前步骤未定义引导字段
; } return (

填写以下信息后将创建角色卡与世界书,并绑定到当前 Studio 项目。

{displayParams.map((dp) => ( ))}
); } function ToolOptionsCarousel({ options, index, onPrev, onNext, runAdvancing }) { const current = options[index]; const [selectedOption, setSelectedOption] = useState(null); const [copyHint, setCopyHint] = useState(''); useEffect(() => { setSelectedOption(null); setCopyHint(''); }, [index, current?.question]); if (!options.length || !current) return null; const handleSelect = (opt) => { setSelectedOption(opt); setCopyHint(''); }; const handleCopy = async () => { if (!selectedOption) return; try { await navigator.clipboard.writeText(selectedOption); setCopyHint('已复制到剪贴板'); setTimeout(() => setCopyHint(''), 2000); } catch { setCopyHint('复制失败,请手动选择文本'); } }; return (
模型提问 {index + 1} / {options.length}

{current.question}

{(current.options || []).map((opt) => ( ))}
{selectedOption ? (
复制以下内容到输入框