DynamoDS/DYN-10515: As a Dynamo user, I want my graph to AutoSave#17117
Open
eamiri wants to merge 9 commits into
Open
DynamoDS/DYN-10515: As a Dynamo user, I want my graph to AutoSave#17117eamiri wants to merge 9 commits into
eamiri wants to merge 9 commits into
Conversation
There was a problem hiding this comment.
See the ticket for this pull request: https://jira.autodesk.com/browse/DYN-10515
Add EnableAutoSave property to PreferenceSettings with default false, backing field, and PublicAPI entries. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Subscribe to each workspace's PropertyChanged event and trigger a debounced save to the original file when HasUnsavedChanges becomes true, gated on PreferenceSettings.EnableAutoSave and a non-empty FileName. Per-workspace ActionDebouncer coalesces rapid edits into a single save after 30s of inactivity; saves route through the existing SaveAs(Guid, ...) entry point with SaveContext.AutoSave so extension listeners can distinguish AutoSave events. Debouncers are created on workspace add (and for the initial workspace in the constructor), cancelled/disposed on workspace remove, and all remaining debouncers are disposed during shutdown.
Wire EnableAutoSave preference to a ToggleButton in the Preferences > Backup expander via a new AutoSaveIsChecked property on PreferencesViewModel. Include the user-facing resource strings in the primary and en-US resx files so the build compiles; other locales follow in TASK 5. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add PreferencesViewEnableAutoSave and PreferencesViewAutoSaveTooltip entries to all 13 localized resx siblings as English placeholders. Translation team will localize in a follow-up pass.
…DYN-10515) Adds NUnit coverage for: - EnableAutoSave default value (false) and XML round-trip - AutoSave fires for dirty saved workspaces with SaveContext.AutoSave - AutoSave is a no-op for untitled workspaces and when disabled - Debouncer is disposed when a workspace is removed Exposes the AutoSave debouncer dictionaries and TriggerAutoSave as internal to keep tests deterministic without the 30s debounce delay. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Contributor
There was a problem hiding this comment.
Pull request overview
Implements an opt-in “AutoSave” feature for Dynamo graphs (VSCode-style), allowing saved workspaces to be automatically written back to their canonical .dyn file after inactivity, while preserving the existing backup mechanism. This is primarily done in the WPF layer (DynamoViewModel) and surfaced via a new Preferences toggle.
Changes:
- Added
SaveContext.AutoSaveand updated PublicAPI tracking for the new enum value. - Added
PreferenceSettings.EnableAutoSave(persisted, defaultfalse) plus Preferences UI toggle + localized strings. - Implemented per-workspace AutoSave scheduling in
DynamoViewModeland added/updated tests and settings fixtures.
Reviewed changes
Copilot reviewed 25 out of 25 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| test/settings/DynamoSettings-NewSettings.xml | Adds <EnableAutoSave> to the “new settings” fixture for import/copy settings coverage. |
| test/DynamoCoreWpf3Tests/WorkspaceSaving.cs | Adds WPF-layer tests around AutoSave triggering/context and workspace removal behavior. |
| test/DynamoCoreTests/Configuration/PreferenceSettingsTests.cs | Adds unit tests for AutoSave default value and XML round-tripping. |
| src/DynamoCoreWpf/Views/Menu/PreferencesView.xaml | Adds an AutoSave toggle to Preferences → General → Backup UI. |
| src/DynamoCoreWpf/ViewModels/Menu/PreferencesViewModel.cs | Adds AutoSaveIsChecked bound to PreferenceSettings.EnableAutoSave. |
| src/DynamoCoreWpf/ViewModels/Core/DynamoViewModel.cs | Implements AutoSave subscription, debouncing, triggering, and shutdown cleanup. |
| src/DynamoCoreWpf/PublicAPI.Unshipped.txt | Tracks new public API surface for PreferencesViewModel.AutoSaveIsChecked. |
| src/DynamoCoreWpf/Properties/Resources.resx | Adds AutoSave label/tooltip strings. |
| src/DynamoCoreWpf/Properties/Resources.en-US.resx | Adds AutoSave label/tooltip strings (en-US). |
| src/DynamoCoreWpf/Properties/Resources.cs-CZ.resx | Adds AutoSave label/tooltip keys (localized file placeholder values). |
| src/DynamoCoreWpf/Properties/Resources.de-DE.resx | Adds AutoSave label/tooltip keys (localized file placeholder values). |
| src/DynamoCoreWpf/Properties/Resources.en-GB.resx | Adds AutoSave label/tooltip keys (localized file placeholder values). |
| src/DynamoCoreWpf/Properties/Resources.es-ES.resx | Adds AutoSave label/tooltip keys (localized file placeholder values). |
| src/DynamoCoreWpf/Properties/Resources.fr-FR.resx | Adds AutoSave label/tooltip keys (localized file placeholder values). |
| src/DynamoCoreWpf/Properties/Resources.it-IT.resx | Adds AutoSave label/tooltip keys (localized file placeholder values). |
| src/DynamoCoreWpf/Properties/Resources.ja-JP.resx | Adds AutoSave label/tooltip keys (localized file placeholder values). |
| src/DynamoCoreWpf/Properties/Resources.ko-KR.resx | Adds AutoSave label/tooltip keys (localized file placeholder values). |
| src/DynamoCoreWpf/Properties/Resources.pl-PL.resx | Adds AutoSave label/tooltip keys (localized file placeholder values). |
| src/DynamoCoreWpf/Properties/Resources.pt-BR.resx | Adds AutoSave label/tooltip keys (localized file placeholder values). |
| src/DynamoCoreWpf/Properties/Resources.ru-RU.resx | Adds AutoSave label/tooltip keys (localized file placeholder values). |
| src/DynamoCoreWpf/Properties/Resources.zh-CN.resx | Adds AutoSave label/tooltip keys (localized file placeholder values). |
| src/DynamoCoreWpf/Properties/Resources.zh-TW.resx | Adds AutoSave label/tooltip keys (localized file placeholder values). |
| src/DynamoCore/PublicAPI.Unshipped.txt | Tracks new public APIs (SaveContext.AutoSave, PreferenceSettings.EnableAutoSave). |
| src/DynamoCore/Graph/ModelBase.cs | Adds SaveContext.AutoSave enum value. |
| src/DynamoCore/Configuration/PreferenceSettings.cs | Adds persisted EnableAutoSave preference with default false. |
Comment on lines
+4776
to
+4780
| foreach (var debouncer in autoSaveDebouncers.Values) | ||
| { | ||
| debouncer.Dispose(); | ||
| } | ||
| autoSaveDebouncers.Clear(); |
| return; | ||
| } | ||
|
|
||
| SaveAs(workspaceGuid, workspace.FileName, isBackup: false, SaveContext.AutoSave); |
Comment on lines
+1761
to
+1765
| workspace.HasUnsavedChanges = true; | ||
| Assert.IsTrue(workspace.HasUnsavedChanges); | ||
|
|
||
| ViewModel.TriggerAutoSave(workspace.Guid); | ||
|
|
Comment on lines
+1866
to
+1869
| ViewModel.Model.RemoveWorkspace(customNodeWorkspace); | ||
|
|
||
| Assert.IsFalse(ViewModel.autoSaveDebouncers.ContainsKey(workspaceGuid)); | ||
| Assert.IsFalse(ViewModel.autoSaveHandlers.ContainsKey(workspaceGuid)); |
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.



Closes https://jira.autodesk.com/browse/DYN-10515
h1. AutoSave Graphs Implementation Plan
h2. Overview
Add VSCode-style AutoSave to Dynamo: when a user edits a graph that has already been saved to disk, changes are automatically written back to the canonical file after a debounce delay (default 30 s of inactivity). The feature is opt-in via a toggle in Preferences > Backup settings. The existing backup mechanism continues to run unchanged alongside AutoSave.
h2. Current State Analysis
h3. Key Discoveries
h2. Desired End State
h2. What We're NOT Doing
h2. Implementation Approach
AutoSave will be implemented entirely in the WPF layer ({{DynamoViewModel}}). When a workspace is opened or becomes active, {{DynamoViewModel}} subscribes to its {{PropertyChanged}} event. When {{HasUnsavedChanges}} becomes {{true}} for a workspace with a non-empty {{FileName}} and {{EnableAutoSave}} is {{true}}, a per-workspace {{ActionDebouncer}} fires the existing {{SaveAs(Guid, string, bool, SaveContext)}} pipeline after 30 seconds. On workspace close or removal, the debouncer is cancelled and disposed.
This approach reuses existing infrastructure ({{ActionDebouncer}}, {{InternalSaveAs}}, {{SaveAs(Guid, ...)}}) with no new patterns and no changes to {{DynamoCore}}'s threading model.
h2.
h3. TASK 1: Add {{SaveContext.AutoSave}} enum value [HIGH PRIORITY]
Status: DONE
Milestone: Extension listeners can distinguish AutoSave events and skip expensive work by checking {{saveContext == SaveContext.AutoSave}}.
Validation: Build succeeds with no exhaustive-switch warnings; {{grep -r "SaveContext.AutoSave" src/}} returns the new declaration.
Requirements from spec:
Files to Modify:
h2.
h3. TASK 2: Add {{EnableAutoSave}} to {{PreferenceSettings}} [HIGH PRIORITY]
Status: DONE
Milestone: {{EnableAutoSave}} persists across Dynamo restarts (round-trips through XML serialization) and defaults to {{false}}.
Validation: {{dotnet test src/DynamoCoreTests/DynamoCoreTests.csproj --filter "Name~PreferenceSettings"}} — all existing tests pass; new round-trip test passes.
Requirements from spec:
Files to Modify:
** {{Dynamo.Configuration.PreferenceSettings.EnableAutoSave.get -> bool}}
** {{Dynamo.Configuration.PreferenceSettings.EnableAutoSave.set -> void}}
h2.
h3. TASK 3: Implement AutoSave debouncer in {{DynamoViewModel}} [HIGH PRIORITY]
Status: DONE
Milestone: With {{EnableAutoSave = true}}, editing a saved graph and waiting 30 seconds results in the file being written to {{workspace.FileName}}; {{HasUnsavedChanges}} becomes {{false}}; no user prompt appears on close.
Validation:
Requirements from spec:
Files to Modify:
Implementation Details:
h2.
h3. TASK 4: Add AutoSave toggle to Preferences UI [MEDIUM PRIORITY]
Status: DONE
Milestone: The Preferences window shows an AutoSave toggle in the Backup settings expander; toggling it updates {{PreferenceSettings.EnableAutoSave}} and the change persists across restarts.
{code:csharp}public bool AutoSaveIsChecked
{
get { return preferenceSettings.EnableAutoSave; }
set { preferenceSettings.EnableAutoSave = value; RaisePropertyChanged(nameof(AutoSaveIsChecked)); }
}
{code}
** {{Dynamo.ViewModels.PreferencesViewModel.AutoSaveIsChecked.get -> bool}}
** {{Dynamo.ViewModels.PreferencesViewModel.AutoSaveIsChecked.set -> void}}
Validation: Launch Dynamo sandbox, open Preferences, verify toggle appears in Backup section, toggle on/off, close and re-open Preferences — verify value persists.
Requirements from spec:
Files to Modify:
h2.
h3. TASK 5: Add localization strings [MEDIUM PRIORITY]
Status: DONE
Milestone: All user-facing AutoSave strings compile; the toggle label displays correctly in English at runtime.
** {{PreferencesViewEnableAutoSave}} = {{"Enable AutoSave"}}
** {{PreferencesViewAutoSaveTooltip}} = {{"Automatically save your graph to its original file after a period of inactivity. Only applies to graphs that have already been saved to disk."}}
Validation: Build succeeds with no missing resource key warnings; UI displays "Enable AutoSave" next to the toggle.
Requirements from spec:
Files to Modify:
h2.
h3. TASK 6: Add tests [MEDIUM PRIORITY]
Status: DONE
Milestone: All new test cases pass; existing preference and save tests continue to pass.
** {{WhenDefaultSettingsThenAutoSaveIsDisabled}} — assert {{EnableAutoSave == false}} (pattern: line 70)
** {{WhenSettingsSavedAndLoadedThenAutoSaveRoundTrips}} — mutate {{EnableAutoSave = true}}, save/reload, assert {{true}} persists (pattern: lines 105-159)
** {{WhenAutoSaveEnabledAndWorkspaceIsDirtyAndHasFileNameThenSaveIsCalledAfterDebounce}} — enable AutoSave, create saved temp workspace, set {{HasUnsavedChanges = true}}, trigger debounce, assert file modified and {{HasUnsavedChanges == false}}
** {{WhenAutoSaveEnabledAndWorkspaceHasNoFileNameThenSaveIsNotCalled}} — untitled workspace, set {{HasUnsavedChanges = true}}, assert no save event fires
** {{WhenAutoSaveEnabledAndSaveFiredThenSaveContextIsAutoSave}} — capture {{WorkspaceSaving}} event arg, assert {{saveContext == SaveContext.AutoSave}}
** {{WhenAutoSaveDisabledAndWorkspaceIsDirtyThenSaveIsNotCalled}} — disable AutoSave, dirty workspace, assert no save after debounce window
** {{WhenAutoSaveEnabledAndWorkspaceClosedBeforeDebounceElapsedThenSaveIsNotCalled}} — verify debouncer cancel on workspace remove prevents stale write
Validation: {{dotnet test src/DynamoCoreTests/DynamoCoreTests.csproj --filter "Name
AutoSave"}} and {{dotnet test test/DynamoCoreWpf3Tests/ --filter "NameAutoSave"}} — all new tests pass.Requirements from spec:
Files to Modify:
h2.
h2. Testing Strategy
h3. Unit Tests
h3. Integration Tests
h3. Manual Testing Steps
Enable AutoSave in Preferences > Backup section
Open an existing saved graph, add a node — title bar shows unsaved indicator
Wait 30 seconds without editing — verify title bar clears and file on disk timestamp updated
Open an untitled graph, add a node, wait 30 seconds — verify no unexpected file write
Disable AutoSave in Preferences, make an edit — verify no automatic save occurs
Close a graph after AutoSave has fired — verify no "Unsaved changes" dialog appears
Close a graph within the 30 s debounce window (before AutoSave fires) — verify the "Unsaved changes" dialog still appears correctly
h2. Performance Considerations
h2. Migration Notes