[editor] add YAML file conflict detection for concurrent edits#847
Open
marnovdm wants to merge 7 commits intoesphome:mainfrom
Open
[editor] add YAML file conflict detection for concurrent edits#847marnovdm wants to merge 7 commits intoesphome:mainfrom
marnovdm wants to merge 7 commits intoesphome:mainfrom
Conversation
Prevents data loss when editing the same YAML file in multiple browser tabs or with external editors by detecting conflicts and providing clear resolution options. **API Changes (src/api/files.ts):** - Add FileWithMtime interface for mtime-aware responses - Add getFileWithMtime() to extract X-File-Mtime header from GET requests - Add writeFileWithMtime() to send mtime parameter and detect 409 conflicts - Refactor existing getFile() and writeFile() to use new functions (backward compatible) **Editor Changes (src/editor/esphome-editor.ts):** - Add fileMtime property to track file modification time - Update file loading to use getFileWithMtime() and store mtime - Modify save logic to send mtime for conflict validation - Add conflict detection dialog with Material Web Components - Implement conflict resolution with "Cancel" and "Overwrite" options - Handle 409 conflict responses by showing dialog - Handle 404 file deleted errors appropriately **How It Works:** 1. When file is loaded, backend sends X-File-Mtime header 2. Frontend stores mtime and sends it back on save 3. Backend validates mtime; returns 409 if file was modified 4. Frontend shows dialog with two options: - Cancel: Close dialog, keep editor open for reference - Overwrite: Force save, bypassing conflict check **Backward Compatibility:** - All existing code using getFile() and writeFile() continues to work - Gracefully handles backends without mtime support - No breaking changes to API Closes #[issue-number]
16 tasks
Follows project pattern of keeping fetchApiBase private with public wrappers. Adds fetchApiResponse for cases needing header access. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
So after opening a feature request for this (https://github.com/orgs/esphome/discussions/3456) I thought about it some more and figured it would be a good way to test Claude Code, which I'm currently testing for work. Using the awesome https://github.com/obra/superpowers this was pretty easy to do. I've reviewed everything manually and tested it and think it works well. I've years of experience doing Linux/DevOps and some fullstack development with Python as well, but I'm by no means a professional software developer so I'm open to any and all feedback. Claude described the PR below.
Screenshot
Summary
Implements client-side conflict detection for the ESPHome dashboard's YAML editor to prevent data loss when the same file is edited simultaneously in multiple browser tabs or by external editors.
Problem Solved:
When users edit a YAML configuration file in multiple browser tabs or with external editors, changes can silently overwrite each other, causing data loss. This PR adds conflict detection and resolution UI to handle these scenarios gracefully.
Changes
Modified Files
src/api/files.ts- API layer with mtime-aware functionssrc/editor/esphome-editor.ts- Editor component with conflict detectionImplementation Details
API Layer (
src/api/files.ts):New exports:
FileWithMtimeinterface: Type for mtime-aware file responsesgetFileWithMtime(): Fetches file content + X-File-Mtime headerwriteFileWithMtime(): Sends mtime parameter, detects 409 conflictsRefactored:
getFile(): Now usesgetFileWithMtime()internally (backward compatible)writeFile(): Now useswriteFileWithMtime()internally (backward compatible)Editor Component (
src/editor/esphome-editor.ts):State:
fileMtime: string | null: Tracks current file's modification timeFile Loading:
getFileWithMtime()to fetch content and mtimeSave Logic:
Conflict Resolution UI:
--alert-error-colorfor destructive actionUser Experience
Normal Save (No Conflict)
Conflict Detected
External Editor Conflict
Technical Details
Data Flow
File Load:
File Save (Normal):
File Save (Conflict):
Force Save (Overwrite):
Note on Force Override:
The frontend achieves force override by omitting the
mtimeparameter entirely, rather than sending aforce=truequery parameter. This approach ensures zero version coupling between frontend and backend:The backend implements a
forceparameter for potential future use (e.g., when implementing a diff viewer where we want to differentiate "no mtime available" vs "user explicitly chose to force override after seeing the diff"). This design allows future enhancements without breaking backward compatibility.Error Handling
Edge Cases Handled
force=true(zero version coupling)_saveFile(forceOrEvent)close()calls for Material Web ComponentsBackward Compatibility
✅ Fully backward compatible:
getFile()/writeFile()works unchangedTesting
Manual Testing Scenarios
Test 1: Multi-tab conflict
Test 2: External editor
Test 3: Sequential edits (no conflict)
Test 4: New file
Test 5: secrets.yaml (existing file)
Test 6: secrets.yaml (non-existent)
UI/UX Considerations
Dialog Design Decisions:
--alert-error-color(danger)Alternative Considered:
Integration
Requires backend PR:
This frontend PR must be deployed alongside the companion backend PR that:
Can be deployed independently:
Yes - gracefully degrades with old backend (no validation, but no errors).
Performance Impact
Related PRs
Checklist
--alert-error-color)