@@ -252,28 +252,13 @@ export const useBuildsStore = create<BuildsState>()(
252252 setBuild : ( buildId : string , patch : Partial < Build > , baseBuild ?: Build ) => {
253253 set ( ( state ) => {
254254 let targetBuild = state . builds [ buildId ] ;
255+ let isNew = false ;
255256
256257 if ( ! targetBuild ) {
257258 if ( baseBuild ) {
258259 // Copy-on-Write: Initialize with baseBuild + patch
259260 targetBuild = { ...baseBuild , ...patch } ;
260-
261- // Ensure it's tracked in the character list (Union survival)
262- const charId = targetBuild . characterId ;
263- if ( ! state . characterToBuildIds [ charId ] ) {
264- // Initialize from preset ordering so other builds aren't lost
265- const preset = getCachedPreset ( state . activePresetId ) ;
266- const presetIds = preset ?. characterBuilds ?. [ charId ] ;
267- state . characterToBuildIds [ charId ] = presetIds
268- ? [ ...presetIds ]
269- : [ ] ;
270- }
271- if ( ! state . characterToBuildIds [ charId ] . includes ( buildId ) ) {
272- state . characterToBuildIds [ charId ] . push ( buildId ) ;
273- }
274-
275- // Proceed to register it
276- state . builds [ buildId ] = targetBuild ;
261+ isNew = true ;
277262 } else {
278263 console . warn (
279264 `Build ${ buildId } not found and no baseBuild provided`
@@ -298,6 +283,40 @@ export const useBuildsStore = create<BuildsState>()(
298283 // Ensure id and characterId cannot be changed
299284 targetBuild . id = buildId ; // Enforce ID consistency
300285
286+ // Check if the result still matches the preset version (no-op guard).
287+ // This prevents marking a build as "modified" when no actual data changed
288+ // (e.g. clicking the name input without typing, or editing back to original).
289+ const preset = getCachedPreset ( state . activePresetId ) ;
290+ const presetBuild = preset ?. builds [ buildId ] ;
291+ if ( presetBuild && areBuildsEqual ( targetBuild , presetBuild ) ) {
292+ if ( isNew ) {
293+ // No actual change from preset — skip copy-on-write entirely
294+ return ;
295+ }
296+ // User reverted all changes — auto-revert by removing local override
297+ delete state . builds [ buildId ] ;
298+ delete state . validationErrors [ buildId ] ;
299+ return ;
300+ }
301+
302+ if ( isNew ) {
303+ // Ensure it's tracked in the character list (Union survival)
304+ const charId = targetBuild . characterId ;
305+ if ( ! state . characterToBuildIds [ charId ] ) {
306+ // Initialize from preset ordering so other builds aren't lost
307+ const presetIds = preset ?. characterBuilds ?. [ charId ] ;
308+ state . characterToBuildIds [ charId ] = presetIds
309+ ? [ ...presetIds ]
310+ : [ ] ;
311+ }
312+ if ( ! state . characterToBuildIds [ charId ] . includes ( buildId ) ) {
313+ state . characterToBuildIds [ charId ] . push ( buildId ) ;
314+ }
315+
316+ // Register the new local override
317+ state . builds [ buildId ] = targetBuild ;
318+ }
319+
301320 // Re-validate
302321 state . validationErrors [ buildId ] =
303322 getBuildValidationErrors ( targetBuild ) ;
0 commit comments