Gum gum-tool-file-watch
Reference guide for Gum's FileWatch system. Load this when working on file watching, external file change detection, IgnoreNextChangeUntil, FileWatchManager, FileWatchLogic, FileChangeReactionLogic, or reloading assets/elements when files change on disk.
git clone https://github.com/vchelaru/Gum
T=$(mktemp -d) && git clone --depth=1 https://github.com/vchelaru/Gum "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.claude/skills/gum-tool-file-watch" ~/.claude/skills/vchelaru-gum-gum-tool-file-watch && rm -rf "$T"
.claude/skills/gum-tool-file-watch/SKILL.mdGum Tool File Watch System Reference
Architecture
Three cooperating classes handle the full pipeline:
(FileWatchManager
): OwnsGum/Plugins/InternalPlugins/FileWatchPlugin/FileWatchManager.cs
instances, queues changed files, manages the ignore list, and exposesFileSystemWatcher
.Flush()
(FileWatchLogic
): Determines which directories to watch by scanning all project elements for referenced files. CallsGum/Plugins/InternalPlugins/FileWatchPlugin/FileWatchLogic.cs
on project load/unload/variable change.EnableWithDirectories()
(FileChangeReactionLogic
): Dispatches a queued file to the correct reload handler based on file extension.Gum/Managers/FileChangeReactionLogic.cs
MainFileWatchPlugin (Gum/Plugins/InternalPlugins/FileWatchPlugin/MainFileWatchPlugin.cs) is the plugin entry point; it wires events and updates the File Watch UI panel.
Change Pipeline
FileSystemWatcher event (background thread) ↓ FileWatchManager.HandleFileSystemChange() - Checks ignore list (count-based and time-based) - Verifies file's directory is being watched - Adds to ChangedFilesWaitingForFlush, records LastFileChange ↓ PeriodicUiTimer (2s interval, Program.cs) - Calls FileWatchManager.Flush() every 2 seconds ↓ Flush() early-outs if TimeToNextFlush > 0 (waits 2s after last change) ↓ FileChangeReactionLogic.ReactToFileChanged(file) per queued file ↓ Extension-specific reload (texture, element, project, font, CSV, behavior...)
A second
PeriodicUiTimer at 200ms drives the File Watch debug panel UI only — it does not trigger flushes.
Watched Directories
FileWatchLogic.GetFileWatchRootDirectories() builds the watch set by:
- Collecting all files referenced by every screen, component, and standard element via
ObjectFinder.Self.GetFilesReferencedBy() - Adding the gum project's own directory
- Adding localization and font-character-file directories if configured
Deduplication: If directory A is already a root of directory B, B is not added separately. Subdirectories of a watched root are covered automatically (
IncludeSubdirectories = true).
RefreshRootDirectory() is called on project load and whenever a variable that IsFile == true changes value.
Ignore Mechanism
IgnoreNextChangeUntil(FilePath, DateTime?) suppresses the next detected change for a file until the given time. Default is 5 seconds from now.
When to call it: Any time Gum itself writes a file to disk, to prevent the watcher from triggering a reload of the file it just saved.
Callers:
— element/project savesFileCommands.cs
— full project save (ignores .gumx and all element files)ProjectManager.cs
— font generation (ignores .bmfc, .fnt, and .png pages)FontManager.cs
— animation saveAnimationCollectionViewModelManager.cs
— sprite sheet editsTextureCoordinateSelectionPlugin
A separate
changesToIgnore dictionary supports count-based ignoring (decrement on each event), but it is rarely used; the time-based timedChangesToIgnore is the primary mechanism.
ReactToFileChanged Extension Dispatch
| Extension | Action |
|---|---|
, , , | Refresh wireframe if referenced by selected element |
| Reload animation chain if referenced by selected element |
| Reload font (also looks up page PNGs) |
, , | Reload element from disk, refresh tree + wireframe |
| Reload entire project |
| Print warning — Gum does not support runtime reload of animation collections |
| Reload behavior definition |
, | Reload localization file (RESX also matches satellites via ) |
Debug UI Panel
The File Watch tab (hidden by default, toggled via View > Show File Watch) shows live state from
FileWatchManager. It is driven by a 200ms PeriodicUiTimer in MainFileWatchPlugin.HandleRefreshDisplayTimerElapsed() and displays:
- Which directories are being watched
- Files queued in
(up to 15)ChangedFilesWaitingForFlush - Countdown to next flush
- Currently active ignores with their remaining ignore time
FileWatchViewModel (FileWatchPlugin/FileWatchViewModel.cs) is the data-bound VM; FileWatchControl.xaml is the view.
Non-Obvious Behaviors
Double-event prevention for Gum XML files: When
FileSystemWatcher fires a Created event for .gumx/.gusx/.gutx/.gucx/.ganx/.behx files, it is ignored. These formats trigger both Changed and Created on save; only Changed is processed to avoid duplicates. Non-Gum files (e.g., PNG) do process Created.
Rename for PNG, CSV, and RESX:
HandleRename routes renames for .png, .csv, and .resx. Many editors (Vim, JetBrains, some VS Code modes) use an atomic-save pattern — write to a temp file, then rename it over the target — so rename events must be handled for these types to avoid silently missing external edits.
Delete does nothing:
HandleFileSystemDelete has no implementation — file deletions are not reacted to.
Flush debounce is cumulative:
TimeToNextFlush = (LastFileChange + 2s) - Now. Every new file change resets LastFileChange, pushing the flush window out by another 2 seconds. Rapid successive changes delay flushing until things settle.
prevents re-entry but not concurrent queuing: The background IsFlushing
FileSystemWatcher thread can still add to ChangedFilesWaitingForFlush while a flush is in progress (the lock protects the queue). Files added during a flush are picked up on the next flush cycle.
FileWatchManager is a singleton: Registered in
Gum/Services/Builder.cs as both FileWatchManager and IFileWatchManager.
Key Files
| File | Purpose |
|---|---|
| Core watcher, queue, ignore list, flush |
| Computes watched directories, enables/disables watcher |
| Dispatches flushed files to reload handlers |
| Plugin entry point; wires events; drives debug UI |
| UI-thread-safe periodic timer used for both flush and display |
(lines ~144–157) | Creates the 2s flush timer and calls |
| Calls before saving elements |
| Calls before saving project |
| Calls before generating fonts |