UI编辑器一批细节优化 #278
@@ -16,6 +16,7 @@ import { PreviewWorkspace } from './components/preview/PreviewWorkspace';
|
||||
|
k88936 marked this conversation as resolved
|
||||
import { RecognitionOverview } from './components/RecognitionOverview';
|
||||
import { ToolNavigation } from './components/ToolNavigation';
|
||||
import { WorkflowActionCard } from './components/WorkflowActionCard';
|
||||
import { WorkflowCompletionModal } from './components/WorkflowCompletionModal';
|
||||
|
k88936 marked this conversation as resolved
kdletters
commented
完成通知弹窗打开时,页面级 keydown 监听仍会处理 Ctrl/Cmd+Z/Y:onKeyDown 只排除了可编辑元素,没有排除当前 role=dialog/弹窗内焦点。因此用户在弹窗的关闭按钮上按 Ctrl+Z(或 Ctrl+Shift+Z/Ctrl+Y)会直接撤销/重做底层 UI 编辑,弹窗声称阻塞工作台交互但状态仍被修改。建议在快捷键处理前检测 event.target 是否位于 role=dialog(或维护 modal-open 状态)并直接 return;同时补充弹窗打开时快捷键不改变编辑状态的测试。 完成通知弹窗打开时,页面级 keydown 监听仍会处理 Ctrl/Cmd+Z/Y:onKeyDown 只排除了可编辑元素,没有排除当前 role=dialog/弹窗内焦点。因此用户在弹窗的关闭按钮上按 Ctrl+Z(或 Ctrl+Shift+Z/Ctrl+Y)会直接撤销/重做底层 UI 编辑,弹窗声称阻塞工作台交互但状态仍被修改。建议在快捷键处理前检测 event.target 是否位于 role=dialog(或维护 modal-open 状态)并直接 return;同时补充弹窗打开时快捷键不改变编辑状态的测试。
|
||||
import { UI_EDITOR_STEPS, type UiEditorStepId } from './model';
|
||||
import {
|
||||
type UiEditorWorkflowProjection,
|
||||
@@ -329,6 +330,10 @@ export default function UiEditorPage({
|
||||
|
k88936 marked this conversation as resolved
kdletters
commented
完成通知弹窗打开时,页面级 keydown 监听仍会处理 Ctrl/Cmd+Z/Y:onKeyDown 只排除了可编辑元素,没有排除当前 role=dialog/弹窗内焦点。因此用户在弹窗的关闭按钮上按 Ctrl+Z(或 Ctrl+Shift+Z/Ctrl+Y)会直接撤销/重做底层 UI 编辑,弹窗声称阻塞工作台交互但状态仍被修改。建议在快捷键处理前检测 event.target 是否位于 role=dialog(或维护 modal-open 状态)并直接 return;同时补充弹窗打开时快捷键不改变编辑状态的测试。 完成通知弹窗打开时,页面级 keydown 监听仍会处理 Ctrl/Cmd+Z/Y:onKeyDown 只排除了可编辑元素,没有排除当前 role=dialog/弹窗内焦点。因此用户在弹窗的关闭按钮上按 Ctrl+Z(或 Ctrl+Shift+Z/Ctrl+Y)会直接撤销/重做底层 UI 编辑,弹窗声称阻塞工作台交互但状态仍被修改。建议在快捷键处理前检测 event.target 是否位于 role=dialog(或维护 modal-open 状态)并直接 return;同时补充弹窗打开时快捷键不改变编辑状态的测试。
kdletters
commented
完成通知弹窗打开时,页面级 keydown 监听仍会处理 Ctrl/Cmd+Z/Y:onKeyDown 只排除了可编辑元素,没有排除当前 role=dialog/弹窗内焦点。因此用户在弹窗的关闭按钮上按 Ctrl+Z(或 Ctrl+Shift+Z/Ctrl+Y)会直接撤销/重做底层 UI 编辑,弹窗声称阻塞工作台交互但状态仍被修改。建议在快捷键处理前检测 event.target 是否位于 role=dialog(或维护 modal-open 状态)并直接 return;同时补充弹窗打开时快捷键不改变编辑状态的测试。 完成通知弹窗打开时,页面级 keydown 监听仍会处理 Ctrl/Cmd+Z/Y:onKeyDown 只排除了可编辑元素,没有排除当前 role=dialog/弹窗内焦点。因此用户在弹窗的关闭按钮上按 Ctrl+Z(或 Ctrl+Shift+Z/Ctrl+Y)会直接撤销/重做底层 UI 编辑,弹窗声称阻塞工作台交互但状态仍被修改。建议在快捷键处理前检测 event.target 是否位于 role=dialog(或维护 modal-open 状态)并直接 return;同时补充弹窗打开时快捷键不改变编辑状态的测试。
|
||||
</div>
|
||||
</div>
|
||||
<EditorDialogs dialogs={session.dialogs} />
|
||||
<WorkflowCompletionModal
|
||||
|
k88936 marked this conversation as resolved
kdletters
commented
完成通知弹窗打开时,页面级 keydown 监听仍会处理 Ctrl/Cmd+Z/Y:onKeyDown 只排除了可编辑元素,没有排除当前 role=dialog/弹窗内焦点。因此用户在弹窗的关闭按钮上按 Ctrl+Z(或 Ctrl+Shift+Z/Ctrl+Y)会直接撤销/重做底层 UI 编辑,弹窗声称阻塞工作台交互但状态仍被修改。建议在快捷键处理前检测 event.target 是否位于 role=dialog(或维护 modal-open 状态)并直接 return;同时补充弹窗打开时快捷键不改变编辑状态的测试。 完成通知弹窗打开时,页面级 keydown 监听仍会处理 Ctrl/Cmd+Z/Y:onKeyDown 只排除了可编辑元素,没有排除当前 role=dialog/弹窗内焦点。因此用户在弹窗的关闭按钮上按 Ctrl+Z(或 Ctrl+Shift+Z/Ctrl+Y)会直接撤销/重做底层 UI 编辑,弹窗声称阻塞工作台交互但状态仍被修改。建议在快捷键处理前检测 event.target 是否位于 role=dialog(或维护 modal-open 状态)并直接 return;同时补充弹窗打开时快捷键不改变编辑状态的测试。
|
||||
notice={session.workflow.completionNotice}
|
||||
|
k88936 marked this conversation as resolved
kdletters
commented
完成通知弹窗打开时,页面级 keydown 监听仍会处理 Ctrl/Cmd+Z/Y:onKeyDown 只排除了可编辑元素,没有排除当前 role=dialog/弹窗内焦点。因此用户在弹窗的关闭按钮上按 Ctrl+Z(或 Ctrl+Shift+Z/Ctrl+Y)会直接撤销/重做底层 UI 编辑,弹窗声称阻塞工作台交互但状态仍被修改。建议在快捷键处理前检测 event.target 是否位于 role=dialog(或维护 modal-open 状态)并直接 return;同时补充弹窗打开时快捷键不改变编辑状态的测试。 完成通知弹窗打开时,页面级 keydown 监听仍会处理 Ctrl/Cmd+Z/Y:onKeyDown 只排除了可编辑元素,没有排除当前 role=dialog/弹窗内焦点。因此用户在弹窗的关闭按钮上按 Ctrl+Z(或 Ctrl+Shift+Z/Ctrl+Y)会直接撤销/重做底层 UI 编辑,弹窗声称阻塞工作台交互但状态仍被修改。建议在快捷键处理前检测 event.target 是否位于 role=dialog(或维护 modal-open 状态)并直接 return;同时补充弹窗打开时快捷键不改变编辑状态的测试。
|
||||
onClose={session.workflow.dismissCompletionNotice}
|
||||
|
k88936 marked this conversation as resolved
kdletters
commented
完成通知弹窗打开时,页面级 keydown 监听仍会处理 Ctrl/Cmd+Z/Y:onKeyDown 只排除了可编辑元素,没有排除当前 role=dialog/弹窗内焦点。因此用户在弹窗的关闭按钮上按 Ctrl+Z(或 Ctrl+Shift+Z/Ctrl+Y)会直接撤销/重做底层 UI 编辑,弹窗声称阻塞工作台交互但状态仍被修改。建议在快捷键处理前检测 event.target 是否位于 role=dialog(或维护 modal-open 状态)并直接 return;同时补充弹窗打开时快捷键不改变编辑状态的测试。 完成通知弹窗打开时,页面级 keydown 监听仍会处理 Ctrl/Cmd+Z/Y:onKeyDown 只排除了可编辑元素,没有排除当前 role=dialog/弹窗内焦点。因此用户在弹窗的关闭按钮上按 Ctrl+Z(或 Ctrl+Shift+Z/Ctrl+Y)会直接撤销/重做底层 UI 编辑,弹窗声称阻塞工作台交互但状态仍被修改。建议在快捷键处理前检测 event.target 是否位于 role=dialog(或维护 modal-open 状态)并直接 return;同时补充弹窗打开时快捷键不改变编辑状态的测试。
|
||||
/>
|
||||
|
k88936 marked this conversation as resolved
kdletters
commented
完成通知弹窗打开时,页面级 keydown 监听仍会处理 Ctrl/Cmd+Z/Y:onKeyDown 只排除了可编辑元素,没有排除当前 role=dialog/弹窗内焦点。因此用户在弹窗的关闭按钮上按 Ctrl+Z(或 Ctrl+Shift+Z/Ctrl+Y)会直接撤销/重做底层 UI 编辑,弹窗声称阻塞工作台交互但状态仍被修改。建议在快捷键处理前检测 event.target 是否位于 role=dialog(或维护 modal-open 状态)并直接 return;同时补充弹窗打开时快捷键不改变编辑状态的测试。 完成通知弹窗打开时,页面级 keydown 监听仍会处理 Ctrl/Cmd+Z/Y:onKeyDown 只排除了可编辑元素,没有排除当前 role=dialog/弹窗内焦点。因此用户在弹窗的关闭按钮上按 Ctrl+Z(或 Ctrl+Shift+Z/Ctrl+Y)会直接撤销/重做底层 UI 编辑,弹窗声称阻塞工作台交互但状态仍被修改。建议在快捷键处理前检测 event.target 是否位于 role=dialog(或维护 modal-open 状态)并直接 return;同时补充弹窗打开时快捷键不改变编辑状态的测试。
|
||||
<ThemedModal
|
||||
open={returnConfirmOpen}
|
||||
onClose={() => setReturnConfirmOpen(false)}
|
||||
|
||||
|
k88936 marked this conversation as resolved
kdletters
commented
完成通知弹窗打开时,页面级 keydown 监听仍会处理 Ctrl/Cmd+Z/Y:onKeyDown 只排除了可编辑元素,没有排除当前 role=dialog/弹窗内焦点。因此用户在弹窗的关闭按钮上按 Ctrl+Z(或 Ctrl+Shift+Z/Ctrl+Y)会直接撤销/重做底层 UI 编辑,弹窗声称阻塞工作台交互但状态仍被修改。建议在快捷键处理前检测 event.target 是否位于 role=dialog(或维护 modal-open 状态)并直接 return;同时补充弹窗打开时快捷键不改变编辑状态的测试。 完成通知弹窗打开时,页面级 keydown 监听仍会处理 Ctrl/Cmd+Z/Y:onKeyDown 只排除了可编辑元素,没有排除当前 role=dialog/弹窗内焦点。因此用户在弹窗的关闭按钮上按 Ctrl+Z(或 Ctrl+Shift+Z/Ctrl+Y)会直接撤销/重做底层 UI 编辑,弹窗声称阻塞工作台交互但状态仍被修改。建议在快捷键处理前检测 event.target 是否位于 role=dialog(或维护 modal-open 状态)并直接 return;同时补充弹窗打开时快捷键不改变编辑状态的测试。
kdletters
commented
完成通知弹窗打开时,页面级 keydown 监听仍会处理 Ctrl/Cmd+Z/Y:onKeyDown 只排除了可编辑元素,没有排除当前 role=dialog/弹窗内焦点。因此用户在弹窗的关闭按钮上按 Ctrl+Z(或 Ctrl+Shift+Z/Ctrl+Y)会直接撤销/重做底层 UI 编辑,弹窗声称阻塞工作台交互但状态仍被修改。建议在快捷键处理前检测 event.target 是否位于 role=dialog(或维护 modal-open 状态)并直接 return;同时补充弹窗打开时快捷键不改变编辑状态的测试。 完成通知弹窗打开时,页面级 keydown 监听仍会处理 Ctrl/Cmd+Z/Y:onKeyDown 只排除了可编辑元素,没有排除当前 role=dialog/弹窗内焦点。因此用户在弹窗的关闭按钮上按 Ctrl+Z(或 Ctrl+Shift+Z/Ctrl+Y)会直接撤销/重做底层 UI 编辑,弹窗声称阻塞工作台交互但状态仍被修改。建议在快捷键处理前检测 event.target 是否位于 role=dialog(或维护 modal-open 状态)并直接 return;同时补充弹窗打开时快捷键不改变编辑状态的测试。
|
||||
@@ -10,7 +10,10 @@ import {
|
||||
} from '../../features/ui-editor/importAdapter';
|
||||
import { applyMergeResult } from '../../features/ui-editor/merge';
|
||||
import { applyRecognitionResult } from '../../features/ui-editor/recognition';
|
||||
import type { StageStatusField } from '../../features/ui-editor/stageStatusOverview';
|
||||
import {
|
||||
getStageStatusOverview,
|
||||
type StageStatusField,
|
||||
} from '../../features/ui-editor/stageStatusOverview';
|
||||
import { collectUiNodeIds } from '../../features/ui-editor/treeUtils';
|
||||
import type { BindingDTO } from '../../features/ui-editor/types/BindingDTO';
|
||||
import type { ChildrenDisplayMode } from '../../features/ui-editor/types/ChildrenDisplayMode';
|
||||
@@ -54,6 +57,10 @@ import {
|
||||
prerequisiteIssuesForStep,
|
||||
type UiEditorPrerequisiteIssue,
|
||||
} from './components/WorkflowChecks';
|
||||
import {
|
||||
appendWorkflowCheckPrompt,
|
||||
type WorkflowCompletionNotice,
|
||||
} from './components/workflowCompletionNotice';
|
||||
import {
|
||||
type PendingResourceRemoval,
|
||||
removalHasDownstreamReferences,
|
||||
@@ -248,6 +255,8 @@ export function useUiEditorSession(
|
||||
const [hasSuggested, setHasSuggested] = useState(false);
|
||||
const [hasRecognized, setHasRecognized] = useState(false);
|
||||
const [hasBound, setHasBound] = useState(false);
|
||||
const [completionNotice, setCompletionNotice] =
|
||||
useState<WorkflowCompletionNotice | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setActiveStep(initialStep);
|
||||
@@ -954,6 +963,7 @@ export function useUiEditorSession(
|
||||
|
||||
async function suggestUiDesignSemantics() {
|
||||
if (isSuggesting || isWorkflowBusy) return;
|
||||
setCompletionNotice(null);
|
||||
setSuggestionStatus(null);
|
||||
setIsSuggesting(true);
|
||||
try {
|
||||
@@ -964,12 +974,26 @@ export function useUiEditorSession(
|
||||
);
|
||||
editor.replaceState(applyUiDesignSuggestions(snapshot, suggestions));
|
||||
setHasSuggested(true);
|
||||
setSuggestionStatus(`已应用 ${suggestions.length} 条参考图语义建议。`);
|
||||
const message = appendWorkflowCheckPrompt(
|
||||
`参考图分析完成:已应用 ${suggestions.length} 条参考图语义建议`,
|
||||
);
|
||||
setSuggestionStatus(message);
|
||||
setCompletionNotice({
|
||||
step: 'reference-analysis',
|
||||
outcome: 'success',
|
||||
message,
|
||||
});
|
||||
});
|
||||
} catch (cause) {
|
||||
setSuggestionStatus(
|
||||
const message = appendWorkflowCheckPrompt(
|
||||
cause instanceof Error ? cause.message : String(cause),
|
||||
);
|
||||
setSuggestionStatus(message);
|
||||
setCompletionNotice({
|
||||
step: 'reference-analysis',
|
||||
outcome: 'failure',
|
||||
message,
|
||||
});
|
||||
} finally {
|
||||
setIsSuggesting(false);
|
||||
}
|
||||
@@ -977,6 +1001,7 @@ export function useUiEditorSession(
|
||||
|
||||
async function recognizeUi() {
|
||||
if (isRecognizing || isWorkflowBusy) return;
|
||||
setCompletionNotice(null);
|
||||
setRecognitionStatus(null);
|
||||
setIsRecognizing(true);
|
||||
try {
|
||||
@@ -985,15 +1010,34 @@ export function useUiEditorSession(
|
||||
projectPath,
|
||||
state: snapshot,
|
||||
});
|
||||
editor.replaceState(applyRecognitionResult(snapshot, result));
|
||||
const nextState = applyRecognitionResult(snapshot, result);
|
||||
editor.replaceState(nextState);
|
||||
setHasRecognized(true);
|
||||
setSelectedNodeId(null);
|
||||
setRecognitionStatus(`已替换 ${result.ui_trees.length} 棵界面树。`);
|
||||
const overview = getStageStatusOverview(
|
||||
nextState.ui_trees,
|
||||
'layout_status',
|
||||
);
|
||||
const message = appendWorkflowCheckPrompt(
|
||||
`界面结构识别完成:已替换 ${result.ui_trees.length} 棵界面树,待检查 ${overview.needsAttention} 项(必须修复 ${overview.blocked} 项)`,
|
||||
);
|
||||
setRecognitionStatus(message);
|
||||
setCompletionNotice({
|
||||
step: 'structure-recognition',
|
||||
outcome: 'success',
|
||||
message,
|
||||
});
|
||||
});
|
||||
} catch (cause) {
|
||||
setRecognitionStatus(
|
||||
const message = appendWorkflowCheckPrompt(
|
||||
cause instanceof Error ? cause.message : String(cause),
|
||||
);
|
||||
setRecognitionStatus(message);
|
||||
setCompletionNotice({
|
||||
step: 'structure-recognition',
|
||||
outcome: 'failure',
|
||||
message,
|
||||
});
|
||||
} finally {
|
||||
setIsRecognizing(false);
|
||||
}
|
||||
@@ -1020,6 +1064,7 @@ export function useUiEditorSession(
|
||||
|
||||
async function bindComponents() {
|
||||
if (isBinding || isWorkflowBusy) return;
|
||||
setCompletionNotice(null);
|
||||
setBindingStatus(null);
|
||||
setIsBinding(true);
|
||||
try {
|
||||
@@ -1047,13 +1092,27 @@ export function useUiEditorSession(
|
||||
history: index < batches.length - 1 ? 'skip' : 'record',
|
||||
});
|
||||
}
|
||||
setBindingStatus(
|
||||
`组件绑定完成(${batches.length}/${batches.length})。`,
|
||||
const message = appendWorkflowCheckPrompt(
|
||||
`视觉素材绑定完成:已处理 ${batches.length}/${batches.length} 个批次`,
|
||||
);
|
||||
setBindingStatus(message);
|
||||
setHasBound(true);
|
||||
setCompletionNotice({
|
||||
step: 'visual-binding',
|
||||
outcome: 'success',
|
||||
message,
|
||||
});
|
||||
});
|
||||
} catch (cause) {
|
||||
setBindingStatus(cause instanceof Error ? cause.message : String(cause));
|
||||
const message = appendWorkflowCheckPrompt(
|
||||
cause instanceof Error ? cause.message : String(cause),
|
||||
);
|
||||
setBindingStatus(message);
|
||||
setCompletionNotice({
|
||||
step: 'visual-binding',
|
||||
outcome: 'failure',
|
||||
message,
|
||||
});
|
||||
} finally {
|
||||
setIsBinding(false);
|
||||
}
|
||||
@@ -1318,6 +1377,7 @@ export function useUiEditorSession(
|
||||
isBinding,
|
||||
hasBound,
|
||||
bindingStatus,
|
||||
completionNotice,
|
||||
bindComponents,
|
||||
requestStepChange,
|
||||
continueToNextStep: () => {
|
||||
@@ -1325,6 +1385,7 @@ export function useUiEditorSession(
|
||||
},
|
||||
confirmStepChange,
|
||||
cancelStepChange: () => setPendingWorkflowStepChange(null),
|
||||
dismissCompletionNotice: () => setCompletionNotice(null),
|
||||
},
|
||||
dialogs: {
|
||||
projectPath,
|
||||
|
||||
完成通知弹窗打开时,页面级 keydown 监听仍会处理 Ctrl/Cmd+Z/Y:onKeyDown 只排除了可编辑元素,没有排除当前 role=dialog/弹窗内焦点。因此用户在弹窗的关闭按钮上按 Ctrl+Z(或 Ctrl+Shift+Z/Ctrl+Y)会直接撤销/重做底层 UI 编辑,弹窗声称阻塞工作台交互但状态仍被修改。建议在快捷键处理前检测 event.target 是否位于 role=dialog(或维护 modal-open 状态)并直接 return;同时补充弹窗打开时快捷键不改变编辑状态的测试。
完成通知弹窗打开时,页面级 keydown 监听仍会处理 Ctrl/Cmd+Z/Y:onKeyDown 只排除了可编辑元素,没有排除当前 role=dialog/弹窗内焦点。因此用户在弹窗的关闭按钮上按 Ctrl+Z(或 Ctrl+Shift+Z/Ctrl+Y)会直接撤销/重做底层 UI 编辑,弹窗声称阻塞工作台交互但状态仍被修改。建议在快捷键处理前检测 event.target 是否位于 role=dialog(或维护 modal-open 状态)并直接 return;同时补充弹窗打开时快捷键不改变编辑状态的测试。