# Gum Tool Copy Paste

> Gum Copy/Paste Reference

- Skill: `vchelaru/gum-tool-copy-paste` (Agent Skill)
- Install (CLI): `npx skillmds@latest add vchelaru/gum-tool-copy-paste`
- Raw SKILL.md: https://api.skillmd.com/api/skills/vchelaru/gum-tool-copy-paste/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: vchelaru (https://skillmd.com/u/vchelaru)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/vchelaru/gum-tool-copy-paste

---


# Gum Copy/Paste Reference

## In-Memory, Not System Clipboard

Gum's copy/paste does **not** use the system clipboard. Copied data lives in `CopyPasteLogic.CopiedData` (an in-memory instance). `MonoGameGum/Clipboard/ClipboardImplementation.cs` is a separate text-only clipboard used elsewhere in the app — not part of this system.

## CopyType Dispatch

`CopyType` drives the entire operation:
- `InstanceOrElement` — copies selected instances within an element, or a whole element if nothing is selected
- `State` — copies the selected state within a state category

Copy, cut, and paste each accept a `CopyType` parameter and bail out early if it doesn't match what's stored.

## TopOrRecursive

Paste offers two modes: `Recursive` (instances + all descendants) vs `Top` (only top-level instances). The UI exposes both as separate "Paste" and "Paste Top Level Instance(s)" menu items.

## Cut Is Immediate

Cut calls `StoreCopiedObject()` (same as copy) then immediately deletes the source instances via `IDeleteLogic.RemoveInstance()`. The deletion happens at cut time, not deferred to paste. This means cutting and then not pasting still destroys the original.

## Multi-Paste Selection Tracking

`_hasChangedSelectionSinceCopy` is the key flag for multi-paste behavior. It starts `false` after a copy and flips `true` when `SelectionChangedMessage` fires (unless the selection change was triggered by the paste itself — `isSelectionCausedByPaste` guards against that false-positive).

During paste, this flag drives parent assignment:
- `false` — re-use the parent from `lastPasteOriginalToParentAssociation` (repeat-paste to same location)
- `true` — attach to current selection (user moved to a new target)

## Paste Creates New Instances, Does Not Clone

`PasteInstanceSaves()` creates fresh `InstanceSave` objects and assigns properties from the copied states. It does **not** simply clone the stored instances. Name uniqueness is enforced via `StringFunctions.MakeStringUnique()`, and an `oldNewNameDictionary` is built to remap parent references throughout the copied hierarchy.

## Base-Element Capture is Filtered, Not Wholesale

`StoreCopiedInstances` captures the source's own selected state into `CopiedStates` AND the default state of every base element in the source's BaseType chain into a **separate** `CopiedBaseElementDefaultStates` list. The base captures exist so the new instance keeps inherited effective values (e.g. `Text="Click Me"` from a base) even after renaming/relocation, where the regular inheritance walk would no longer reach them.

At paste time, base captures are filtered by `CopiedNamesOwnedByReachableStates` — the set of qualified item names owned by any reachable categorized state on the source instance. "Owned" covers three shapes:

1. Local `VariableReferences` rows on the source state targeting this instance — their LHSes are scalar names owned.
2. The matched categorized state of the instance's BaseType (e.g. `TextCategoryState = "Title"` on a Label instance → `Label.Title`, with the BaseType chain walked via `GetStateSaveRecursively`):
   - LHSes from its `VariableReferences` rows are owned scalars.
   - Direct scalar variables on the state are owned (this is what catches the *broader* case where the state sets values directly without a ref — e.g. `Label.Title.Variables["FontSize"] = 28`).
   - If the matched state has any `VariableReferences` row, the qualified list marker `"<instance>.VariableReferences"` is also added so paste drops the base-snapshotted reference row that would otherwise shadow the categorized walk.

Anything in a base capture whose name matches the owned set is **dropped** rather than snapshotted. The new instance picks up the active categorized state's contribution via the normal `GetValueRecursive` / `GetVariableListRecursive` walks instead of carrying the source's base-level orphans.

The walk is implemented in `ElementSaveExtensions.GetItemNamesOwnedByReachableCategorizedStates(StateSave, InstanceSave)`. The shape mirrors `HeadlessErrorChecker`'s `GUM0002` check, though GUM0002 currently only flags the reference-owned subset; broadening the error check to direct-scalar conflicts is a separate piece of work.

Variables and lists on the source's **own** selected state are never filtered — directly-authored values pass through untouched even when they conflict with a reachable reference. `GUM0002` handles surfacing that conflict at the source; copy/paste preserves author intent.

## Undo Integration

Only paste acquires an undo lock (`_undoManager.RequestLock()`). The entire paste — all instances, state variable copies, parent assignments — records as a single undo action. Cut's deletion goes through `IDeleteLogic.RemoveInstance()` which handles its own undo internally.

## State Paste Validation

Pasting a state runs `ValidateStatePaste()` first. It checks that all variables in the copied state exist in the target category and are supported by the target element. Paste is silently blocked (or shows a dialog) if incompatible.

## Plugin Hook

`PluginManager.InstanceAdd()` is called for each newly pasted instance, allowing plugins to react to paste-created instances the same way they react to manually added ones.

## Animation Copy/Paste

`AnimationCopyPasteManager.cs` is a **separate, independent** copy/paste system for animations. It has its own `CopiedData` class, stores a single `AnimationViewModel`, and does not interact with `CopyPasteLogic`.

## Entry Points

All copy/paste is triggered through:
- `HotkeyManager` — routes `Ctrl+C / Ctrl+X / Ctrl+V` → `ICopyPasteLogic.OnCopy / OnCut / OnPaste`
- `ElementTreeViewManager.RightClick` — right-click context menu items

Do not call `CopyPasteLogic` methods directly from outside these entry points.

## Key Files

| File | Purpose |
|------|---------|
| `Tools/Gum.Presentation/Logic/CopyPasteLogic.cs` | All copy/paste orchestration; `CopiedData` nested class defined here |
| `Tools/Gum.Presentation/Logic/ICopyPasteLogic.cs` | Interface |
| `Tools/Gum.Presentation/Commands/EditCommands.cs` | Thin wrappers that delegate to `ICopyPasteLogic` |
| `Tools/Gum.Presentation/Managers/HotkeyManager.cs` | Keyboard entry point |
| `Gum/Plugins/InternalPlugins/TreeView/ElementTreeViewManager.RightClick.cs` | Context menu entry point |
| `Gum/StateAnimationPlugin/Managers/AnimationCopyPasteManager.cs` | Separate animation copy/paste |
| `Tool/Tests/GumToolUnitTests/Logic/CopyPasteLogicTests.cs` | Unit tests |

