From 2cf3183e786c42afb291038ff89f26e0f21234f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Wed, 19 Aug 2026 13:14:44 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=BC=BAUI=E7=BC=96=E8=BE=91=E5=99=A8?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E9=AB=98=E4=BA=AE=E5=8A=A8=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 概览重复定位时重新触发状态提示动画 为待审与阻塞状态提供差异化呼吸高亮 补充状态高亮令牌的回归测试 --- apps/ai-game-creator-shell/src/styles.css | 96 +++++++++++++++++++ .../components/Inspector/InspectorSidebar.tsx | 29 ++++-- .../src/view/ui-editor/useUiEditorPage.ts | 12 ++- .../tests/uiEditorPage.test.ts | 11 ++- 4 files changed, 138 insertions(+), 10 deletions(-) diff --git a/apps/ai-game-creator-shell/src/styles.css b/apps/ai-game-creator-shell/src/styles.css index c9f977db6..1bbd532f5 100644 --- a/apps/ai-game-creator-shell/src/styles.css +++ b/apps/ai-game-creator-shell/src/styles.css @@ -23,6 +23,102 @@ textarea { font: inherit; } +@keyframes ui-editor-status-attention-arrive { + 0%, + 100% { + transform: scale(1); + } + + 16%, + 50% { + transform: scale(1.012); + } + + 32%, + 68% { + transform: scale(0.996); + } +} + +@keyframes ui-editor-status-attention-ring { + 0%, + 100% { + opacity: 0; + transform: scale(0.96); + } + + 16%, + 48% { + opacity: 0.78; + transform: scale(1.01); + } + + 32%, + 68% { + opacity: 0.2; + transform: scale(1.025); + } +} + +@keyframes ui-editor-status-attention-breathe { + 0%, + 100% { + opacity: 0.32; + transform: scale(0.995); + } + + 50% { + opacity: 0.7; + transform: scale(1.018); + } +} + +.ui-editor-status-attention { + position: relative; + z-index: 0; + isolation: isolate; + border: 2px solid rgb(var(--ui-editor-status-attention-rgb) / 88%); + background: rgb(var(--ui-editor-status-attention-rgb) / 8%); + box-shadow: + 0 0 0 4px rgb(var(--ui-editor-status-attention-rgb) / 16%), + 0 8px 20px rgb(var(--ui-editor-status-attention-rgb) / 12%); + animation: ui-editor-status-attention-arrive 820ms + cubic-bezier(0.2, 0.8, 0.2, 1); +} + +.ui-editor-status-attention::after { + position: absolute; + z-index: -1; + inset: -6px; + border: 2px solid rgb(var(--ui-editor-status-attention-rgb) / 85%); + border-radius: 12px; + content: ''; + pointer-events: none; + animation: + ui-editor-status-attention-ring 820ms ease-out, + ui-editor-status-attention-breathe 1.8s ease-in-out 820ms infinite; +} + +.ui-editor-status-attention--review { + --ui-editor-status-attention-rgb: 245 158 11; +} + +.ui-editor-status-attention--blocked { + --ui-editor-status-attention-rgb: 239 68 68; +} + +@media (prefers-reduced-motion: reduce) { + .ui-editor-status-attention, + .ui-editor-status-attention::after { + animation: none; + } + + .ui-editor-status-attention::after { + opacity: 0.7; + transform: scale(1.01); + } +} + .client-auth-shell { display: grid; min-height: 100vh; diff --git a/apps/ai-game-creator-shell/src/view/ui-editor/components/Inspector/InspectorSidebar.tsx b/apps/ai-game-creator-shell/src/view/ui-editor/components/Inspector/InspectorSidebar.tsx index af8ab0c13..f5e14b2ab 100644 --- a/apps/ai-game-creator-shell/src/view/ui-editor/components/Inspector/InspectorSidebar.tsx +++ b/apps/ai-game-creator-shell/src/view/ui-editor/components/Inspector/InspectorSidebar.tsx @@ -300,7 +300,10 @@ function NodeInspector({ keepChildrenUnchanged: boolean; onKeepChildrenUnchangedChange: (value: boolean) => void; onMetadataChange: UiEditorPageController['setNodeMetadata']; - highlightedStatusField: StageStatusField | null; + highlightedStatusField: { + field: StageStatusField; + requestId: number; + } | null; onTransformChange: UiEditorPageController['setNodeTransform']; onLayoutChange: UiEditorPageController['setNodeLayout']; sprites: UiEditorPageController['sprites']; @@ -406,7 +409,11 @@ function NodeInspector({ label="布局状态" value={node.metadata.layout_status} disabled={isReadOnly} - highlighted={highlightedStatusField === 'layout_status'} + highlight={ + highlightedStatusField?.field === 'layout_status' + ? highlightedStatusField + : null + } onChange={(layout_status) => { if (!isReadOnly) onMetadataChange({ layout_status }); }} @@ -415,7 +422,11 @@ function NodeInspector({ label="组件状态" value={node.metadata.components_status} disabled={isReadOnly} - highlighted={highlightedStatusField === 'components_status'} + highlight={ + highlightedStatusField?.field === 'components_status' + ? highlightedStatusField + : null + } onChange={(components_status) => { if (!isReadOnly) onMetadataChange({ components_status }); }} @@ -779,13 +790,13 @@ function NodeStageSelect({ label, value, disabled, - highlighted, + highlight, onChange, }: { label: string; value: SelectedNode['metadata']['layout_status']; disabled: boolean; - highlighted: boolean; + highlight: { requestId: number } | null; onChange: (value: SelectedNode['metadata']['layout_status']) => void; }) { const readOnly = useInspectorReadOnly(); @@ -801,11 +812,15 @@ function NodeStageSelect({ ? value.NeedReview : value.Blocked : null; + const attentionTone = kind === 'Blocked' ? 'blocked' : 'review'; return (