Studio: 增量/覆盖保存、节点切换与绑定编辑弹窗
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -6,7 +6,7 @@ import {
|
||||
buildWorkflowVariableLabelMap,
|
||||
getWorkflowVariableLabel,
|
||||
} from './edit/workflowVariableLabels';
|
||||
import { resolveBoundCharacterName, canUndoRun, canRerollRun, canAdvanceNextStep, getRunControls, hasRunControl } from './studioRunUtils';
|
||||
import { resolveBoundCharacterName, canUndoRun, canRerollRun, canAdvanceNextStep, getRunControls, hasRunControl, shouldShowFirstExport, shouldShowSaveAppendOverwrite, canSaveWorldbookEntry, canSwitchToNode } from './studioRunUtils';
|
||||
import useCharacterStore from '../../Store/SideBarLeft/CharacterSlice';
|
||||
|
||||
import StudioContextBlockPopup from './StudioContextBlockPopup';
|
||||
@@ -499,6 +499,8 @@ function StudioRunPage() {
|
||||
fetchProject,
|
||||
selectRun,
|
||||
advanceRun,
|
||||
saveWorldbookRun,
|
||||
switchRunNode,
|
||||
sendRunMessage,
|
||||
undoRun,
|
||||
rerollRun,
|
||||
@@ -735,10 +737,10 @@ function StudioRunPage() {
|
||||
};
|
||||
|
||||
const handleNextStep = async () => {
|
||||
if (!hasRunControl(activeNodeState?.skillId, 'nextStep', skillTemplates)) return;
|
||||
if (runAdvancing || runMessaging || !canAdvanceNextStep(activeNodeState)) {
|
||||
return;
|
||||
}
|
||||
clearConfirmPending();
|
||||
const run = await advanceRun({});
|
||||
if (run) {
|
||||
setChatInput('');
|
||||
@@ -746,6 +748,41 @@ function StudioRunPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleSaveIncremental = async () => {
|
||||
if (!hasRunControl(activeNodeState?.skillId, 'incrementalSave', skillTemplates)) return;
|
||||
if (runAdvancing || runMessaging || !canSaveWorldbookEntry(activeNodeState)) return;
|
||||
clearConfirmPending();
|
||||
await saveWorldbookRun('incremental');
|
||||
};
|
||||
|
||||
const handleSaveOverwrite = async () => {
|
||||
if (!hasRunControl(activeNodeState?.skillId, 'overwriteSave', skillTemplates)) return;
|
||||
if (runAdvancing || runMessaging || !canSaveWorldbookEntry(activeNodeState)) return;
|
||||
clearConfirmPending();
|
||||
await saveWorldbookRun('overwrite');
|
||||
};
|
||||
|
||||
const handleSaveIncrementalClick = () => {
|
||||
if (runAdvancing || runMessaging || !canSaveWorldbookEntry(activeNodeState)) return;
|
||||
requestConfirmAction('saveIncremental');
|
||||
};
|
||||
|
||||
const handleSaveOverwriteClick = () => {
|
||||
if (runAdvancing || runMessaging || !canSaveWorldbookEntry(activeNodeState)) return;
|
||||
requestConfirmAction('saveOverwrite');
|
||||
};
|
||||
|
||||
const handleGraphNodeSelect = useCallback(
|
||||
async (nodeId) => {
|
||||
if (!runSessionEntered || !currentRun || nodeId === currentRun.currentNodeId) return;
|
||||
const nodeState = currentRun.nodeStates?.find((n) => n.nodeId === nodeId);
|
||||
if (!canSwitchToNode(nodeState)) return;
|
||||
clearConfirmPending();
|
||||
await switchRunNode(nodeId);
|
||||
},
|
||||
[runSessionEntered, currentRun, switchRunNode, clearConfirmPending]
|
||||
);
|
||||
|
||||
const handleDeleteRun = async (runId) => {
|
||||
const name = runDisplayName(runs.find((r) => r.id === runId) || {});
|
||||
if (!window.confirm(`确定删除运行「${name}」?此操作不可撤销。`)) return;
|
||||
@@ -772,7 +809,17 @@ function StudioRunPage() {
|
||||
const inSession = runSessionEntered && !!currentRun;
|
||||
const showToolCarousel =
|
||||
inSession && runControls.includes('questions') && toolQuestions.length > 0;
|
||||
const showNextStep = inSession && runControls.includes('nextStep');
|
||||
const showNextStep =
|
||||
inSession && shouldShowFirstExport(activeNodeState);
|
||||
const showSaveIncremental =
|
||||
inSession &&
|
||||
hasRunControl(activeNodeState?.skillId, 'incrementalSave', skillTemplates) &&
|
||||
shouldShowSaveAppendOverwrite(activeNodeState);
|
||||
const showSaveOverwrite =
|
||||
inSession &&
|
||||
hasRunControl(activeNodeState?.skillId, 'overwriteSave', skillTemplates) &&
|
||||
shouldShowSaveAppendOverwrite(activeNodeState);
|
||||
const canSaveEntry = canSaveWorldbookEntry(activeNodeState);
|
||||
const showUndo = runControls.includes('undo');
|
||||
const showReroll = runControls.includes('reroll');
|
||||
const canUndo = canUndoRun(activeNodeState);
|
||||
@@ -840,6 +887,8 @@ function StudioRunPage() {
|
||||
nodeStates={graphNodeStates}
|
||||
currentNodeId={graphCurrentNodeId}
|
||||
variant="sidebar"
|
||||
onNodeSelect={runSessionEntered ? handleGraphNodeSelect : undefined}
|
||||
canSelectNode={canSwitchToNode}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1049,6 +1098,39 @@ function StudioRunPage() {
|
||||
<p className="studio-run-stub-note">确认左侧目前产物无误后进入下一节点</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(showSaveIncremental || showSaveOverwrite) && (
|
||||
<div className="studio-run-right-section studio-run-save-actions">
|
||||
<h2 className="studio-run-section-title">世界书保存</h2>
|
||||
<div className="studio-run-turn-actions__row">
|
||||
{showSaveIncremental ? (
|
||||
<ConfirmTurnButton
|
||||
actionKey="saveIncremental"
|
||||
pendingKey={confirmPending}
|
||||
label="增量保存"
|
||||
className="studio-run-turn-btn studio-run-turn-btn--append"
|
||||
disabled={runAdvancing || runMessaging || !canSaveEntry}
|
||||
title={canSaveEntry ? '追加为新世界书条目' : '请先生成当前步骤产物'}
|
||||
onConfirm={handleSaveIncremental}
|
||||
onRequestConfirm={handleSaveIncrementalClick}
|
||||
/>
|
||||
) : null}
|
||||
{showSaveOverwrite ? (
|
||||
<ConfirmTurnButton
|
||||
actionKey="saveOverwrite"
|
||||
pendingKey={confirmPending}
|
||||
label="覆盖保存"
|
||||
className="studio-run-turn-btn studio-run-turn-btn--overwrite"
|
||||
disabled={runAdvancing || runMessaging || !canSaveEntry}
|
||||
title={canSaveEntry ? '覆盖上次写入的条目' : '请先生成当前步骤产物'}
|
||||
onConfirm={handleSaveOverwrite}
|
||||
onRequestConfirm={handleSaveOverwriteClick}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
<p className="studio-run-stub-note">增量保存会新建条目;覆盖保存会更新本步骤上次写入的条目。切换步骤请使用左侧节点图。</p>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user