WIP: UI编辑器自动分图层切图标 #304

Draft
k88936 wants to merge 100 commits from feat/ui-editor-auto-seperation into master
Showing only changes of commit 5844afa14d - Show all commits
@@ -524,6 +524,40 @@ function spriteResourceValidationError(sprite: SpriteAsset) {
return border.ok ? null : border.message;
}
/**
* Apply the SpriteAsset resource checks without acquiring the editor mutation
* guard. Workflow adapters use this while a State lock is already held so
* resource insertion and component backfill can be committed atomically.
*/
export function addSpriteAssetsToState(
current: State,
assets: readonly SpriteAsset[],
): UiEditorOperationResult<State> {
const unique = new Map<SpriteAssetId, SpriteAsset>();
for (const asset of assets) {
const candidate =
unique.get(asset.asset_id) ?? current.sprite_assets[asset.asset_id];
if (candidate && !sameResource(candidate, asset)) {
return { ok: false, reason: 'duplicate' };
}
unique.set(asset.asset_id, asset);
}
const invalidSprite = [...unique.values()]
.map((asset) => ({ asset, error: spriteResourceValidationError(asset) }))
.find((item) => item.error);
if (invalidSprite?.error) {
return {
ok: false,
reason: `invalid:${invalidSprite.asset.asset_id}${invalidSprite.error}`,
};
}
const next = cloneState(current);
for (const asset of unique.values()) {
next.sprite_assets[asset.asset_id] = structuredClone(asset);
}
return { ok: true, value: next };
}
function fontResourceValidationError(font: FontAsset) {
if (font.asset_id.trim().length === 0) return '缺少字体 ID';
if (font.path.trim().length === 0) return '缺少字体路径';
@@ -782,32 +816,9 @@ export function useUiEditorState(initialState: State = EMPTY_UI_EDITOR_STATE) {
const blocked = guard();
if (blocked) return blocked;
const current = stateRef.current;
const unique = new Map<SpriteAssetId, SpriteAsset>();
for (const asset of assets) {
const candidate =
unique.get(asset.asset_id) ?? current.sprite_assets[asset.asset_id];
if (candidate && !sameResource(candidate, asset)) {
return { ok: false, reason: 'duplicate' };
}
unique.set(asset.asset_id, asset);
}
const invalidSprite = [...unique.values()]
.map((asset) => ({
asset,
error: spriteResourceValidationError(asset),
}))
.find((item) => item.error);
if (invalidSprite?.error) {
return {
ok: false,
reason: `invalid:${invalidSprite.asset.asset_id}${invalidSprite.error}`,
};
}
const next = cloneState(current);
for (const asset of unique.values()) {
next.sprite_assets[asset.asset_id] = structuredClone(asset);
}
commit(next);
const result = addSpriteAssetsToState(current, assets);
if (!result.ok) return result;
commit(result.value);
return { ok: true, value: undefined };
},
[commit, guard],