From e2e9243415880074153d5e184e55c4c710dce835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Thu, 24 Sep 2026 16:00:13 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8B=92=E7=BB=9D=E4=B8=8D=E6=98=AF=E4=BA=8C?= =?UTF-8?q?=E5=85=83=E7=BB=84=E7=9A=84=E6=A0=91=E5=81=8F=E7=A7=BB=E8=BE=93?= =?UTF-8?q?=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit src/features/ui-editor/stateTransition.ts 的 set-tree-offset 分支增加 min.length !== 2 判据:空数组会让 every 空真通过,缺坐标时 max 会算出 NaN 污染渲染消费的偏移 tests/uiEditorState.test.ts 补「[12] 与 [] 都判 invalid」用例 --- .../src/features/ui-editor/stateTransition.ts | 3 ++- .../tests/uiEditorState.test.ts | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/apps/ai-game-creator-shell/src/features/ui-editor/stateTransition.ts b/apps/ai-game-creator-shell/src/features/ui-editor/stateTransition.ts index c4338e0ca..a559e74ed 100644 --- a/apps/ai-game-creator-shell/src/features/ui-editor/stateTransition.ts +++ b/apps/ai-game-creator-shell/src/features/ui-editor/stateTransition.ts @@ -93,7 +93,8 @@ export function applyUiEditorCommand( if (!tree) return { ok: false, reason: 'missing' }; if (command.type === 'set-tree-offset') { - if (!command.min.every(Number.isFinite)) + // 只校验元素有限不够:`[]` 会让 every 空真通过,缺第二个坐标时 max 会算出 NaN。 + if (command.min.length !== 2 || !command.min.every(Number.isFinite)) return { ok: false, reason: 'invalid' }; const size = imageLogicalSize(next, command.treeId); if (!size) return { ok: false, reason: 'invalid' }; diff --git a/apps/ai-game-creator-shell/tests/uiEditorState.test.ts b/apps/ai-game-creator-shell/tests/uiEditorState.test.ts index 7a117eb69..37c8ba33d 100644 --- a/apps/ai-game-creator-shell/tests/uiEditorState.test.ts +++ b/apps/ai-game-creator-shell/tests/uiEditorState.test.ts @@ -138,6 +138,23 @@ describe('useUiEditorState', () => { } }); + it('rejects a tree offset that is not a two-element tuple', () => { + const state: State = { + ...structuredClone(EMPTY_UI_EDITOR_STATE), + ui_design_images: { page: image('Page') }, + ui_trees: [{ src_ui_design: 'page', root: pageRoot('root') }], + }; + for (const min of [[12], []] as unknown as [number, number][]) { + expect( + applyUiEditorCommand(state, { + type: 'set-tree-offset', + treeId: 'page', + min, + }), + ).toEqual({ ok: false, reason: 'invalid' }); + } + }); + it('reports persistable state invariant failures before save', () => { const state = structuredClone(EMPTY_UI_EDITOR_STATE); state.ui_trees = [{ src_ui_design: 'missing', root: pageRoot('root') }];