claude-code-arcane 2.2.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +104 -55
  3. package/agents/career/career-strategist.md +88 -0
  4. package/agents/career/cv-reviewer.md +81 -0
  5. package/agents/career/hiring-manager.md +68 -0
  6. package/agents/career/mock-interviewer.md +76 -0
  7. package/agents/freelance/client-screener.md +84 -0
  8. package/agents/freelance/discovery-call.md +65 -0
  9. package/agents/freelance/pipeline-strategist.md +99 -0
  10. package/agents/freelance/proposal-reviewer.md +82 -0
  11. package/agents/game/ue-blueprint-specialist.md +125 -0
  12. package/agents/game/ue-gas-specialist.md +133 -0
  13. package/agents/game/ue-umg-specialist.md +131 -0
  14. package/dist/cli.js +57 -13
  15. package/docs/RELEASE-SETUP.md +1 -1
  16. package/docs/SKILLS-CATALOG.md +67 -4
  17. package/docs/USER-GUIDE.md +77 -72
  18. package/docs/agent-hierarchy.md +21 -1
  19. package/docs/presentations/build_arcane_deck.py +2 -2
  20. package/docs/presentations/build_arcane_deck_12.py +2 -2
  21. package/hooks/detect-unreal.sh +90 -0
  22. package/package.json +2 -2
  23. package/profiles/freelance.yaml +43 -0
  24. package/profiles/job-hunt.yaml +5 -2
  25. package/profiles/unreal-dev.yaml +97 -0
  26. package/rules/drafter-reviewer.md +16 -3
  27. package/rules/freelance-guardrails.md +53 -0
  28. package/rules/gamedev/unreal-code.md +125 -0
  29. package/skills/career-registry/SKILL.md +95 -0
  30. package/skills/career-registry/scripts/career_registry.py +488 -0
  31. package/skills/client-screen/SKILL.md +83 -0
  32. package/skills/client-screen/references/red-flags.md +95 -0
  33. package/skills/cover-letter/SKILL.md +4 -3
  34. package/skills/cv-tailor/SKILL.md +11 -2
  35. package/skills/freelance-deliver/SKILL.md +90 -0
  36. package/skills/freelance-hunt/SKILL.md +126 -0
  37. package/skills/freelance-hunt/references/templates/Connects.md +39 -0
  38. package/skills/freelance-hunt/references/templates/Contrato.md +60 -0
  39. package/skills/freelance-hunt/references/templates/Propuesta.md +66 -0
  40. package/skills/freelance-kickoff/SKILL.md +94 -0
  41. package/skills/freelance-pipeline/SKILL.md +109 -0
  42. package/skills/freelance-pipeline/scripts/import_history.py +425 -0
  43. package/skills/freelance-profile/SKILL.md +93 -0
  44. package/skills/freelance-proposal/SKILL.md +112 -0
  45. package/skills/freelance-scan/SKILL.md +196 -0
  46. package/skills/freelance-scan/references/dedup.md +35 -0
  47. package/skills/freelance-scan/references/platforms.md +167 -0
  48. package/skills/freelance-scan/scripts/freelance_search.py +1077 -0
  49. package/skills/install-mcp/SKILL.md +135 -46
  50. package/skills/install-mcp/references/unreal-operations.md +55 -0
  51. package/skills/interview-prep/SKILL.md +11 -3
  52. package/skills/job-aplicar/SKILL.md +5 -1
  53. package/skills/job-hunt/SKILL.md +17 -0
  54. package/skills/job-outcome/SKILL.md +15 -1
  55. package/skills/job-upskill/SKILL.md +10 -2
  56. package/skills/ue-actor-component-architecture/SKILL.md +509 -0
  57. package/skills/ue-actor-component-architecture/references/actor-lifecycle.md +301 -0
  58. package/skills/ue-actor-component-architecture/references/component-types.md +592 -0
  59. package/skills/ue-ai-navigation/SKILL.md +526 -0
  60. package/skills/ue-ai-navigation/references/behavior-tree-patterns.md +392 -0
  61. package/skills/ue-ai-navigation/references/behavior-tree-workflow.md +107 -0
  62. package/skills/ue-ai-navigation/references/custom-bttask.md +104 -0
  63. package/skills/ue-ai-navigation/references/eqs-reference.md +423 -0
  64. package/skills/ue-animation-system/SKILL.md +521 -0
  65. package/skills/ue-animation-system/references/anim-notify-reference.md +486 -0
  66. package/skills/ue-animation-system/references/locomotion-setup.md +550 -0
  67. package/skills/ue-async-threading/SKILL.md +433 -0
  68. package/skills/ue-async-threading/references/thread-safety-guide.md +213 -0
  69. package/skills/ue-async-threading/references/threading-patterns.md +345 -0
  70. package/skills/ue-audio-system/SKILL.md +527 -0
  71. package/skills/ue-audio-system/references/audio-setup-patterns.md +700 -0
  72. package/skills/ue-blueprints/SKILL.md +134 -0
  73. package/skills/ue-blueprints/references/communication.md +82 -0
  74. package/skills/ue-character-movement/SKILL.md +475 -0
  75. package/skills/ue-character-movement/references/cmc-extension-patterns.md +335 -0
  76. package/skills/ue-character-movement/references/movement-pipeline.md +189 -0
  77. package/skills/ue-cpp-foundations/SKILL.md +533 -0
  78. package/skills/ue-cpp-foundations/references/components-and-gc.md +107 -0
  79. package/skills/ue-cpp-foundations/references/container-patterns.md +544 -0
  80. package/skills/ue-cpp-foundations/references/delegate-patterns.md +497 -0
  81. package/skills/ue-cpp-foundations/references/gameplay-classes-cpp.md +136 -0
  82. package/skills/ue-cpp-foundations/references/property-specifiers.md +306 -0
  83. package/skills/ue-data-assets-tables/SKILL.md +527 -0
  84. package/skills/ue-data-assets-tables/references/asset-loading-patterns.md +306 -0
  85. package/skills/ue-data-assets-tables/references/data-driven-design.md +378 -0
  86. package/skills/ue-editor-tools/SKILL.md +523 -0
  87. package/skills/ue-editor-tools/references/detail-customization-patterns.md +395 -0
  88. package/skills/ue-editor-tools/references/editor-module-setup.md +480 -0
  89. package/skills/ue-game-features/SKILL.md +477 -0
  90. package/skills/ue-game-features/references/experience-system.md +222 -0
  91. package/skills/ue-game-features/references/game-feature-patterns.md +308 -0
  92. package/skills/ue-gameplay-abilities/SKILL.md +518 -0
  93. package/skills/ue-gameplay-abilities/references/ability-task-reference.md +391 -0
  94. package/skills/ue-gameplay-abilities/references/gameplay-effect-reference.md +397 -0
  95. package/skills/ue-gameplay-abilities/references/gas-setup-patterns.md +369 -0
  96. package/skills/ue-gameplay-framework/SKILL.md +466 -0
  97. package/skills/ue-gameplay-framework/references/framework-class-map.md +243 -0
  98. package/skills/ue-input-system/SKILL.md +394 -0
  99. package/skills/ue-input-system/references/cpp-setup.md +131 -0
  100. package/skills/ue-input-system/references/enhanced-input-workflow.md +117 -0
  101. package/skills/ue-input-system/references/input-action-reference.md +731 -0
  102. package/skills/ue-mass-entity/SKILL.md +476 -0
  103. package/skills/ue-mass-entity/references/mass-entity-patterns.md +319 -0
  104. package/skills/ue-mass-entity/references/mass-fragment-reference.md +119 -0
  105. package/skills/ue-materials-rendering/SKILL.md +526 -0
  106. package/skills/ue-materials-rendering/references/material-parameter-reference.md +216 -0
  107. package/skills/ue-materials-rendering/references/post-process-settings.md +383 -0
  108. package/skills/ue-module-build-system/SKILL.md +487 -0
  109. package/skills/ue-module-build-system/references/build-cs-reference.md +418 -0
  110. package/skills/ue-module-build-system/references/common-build-errors.md +381 -0
  111. package/skills/ue-naming-conventions/SKILL.md +142 -0
  112. package/skills/ue-naming-conventions/references/asset-name-modifiers.md +233 -0
  113. package/skills/ue-naming-conventions/references/content-structure.md +83 -0
  114. package/skills/ue-networking-replication/SKILL.md +452 -0
  115. package/skills/ue-networking-replication/references/replication-patterns.md +526 -0
  116. package/skills/ue-networking-replication/references/rpc-decision-guide.md +393 -0
  117. package/skills/ue-niagara-effects/SKILL.md +531 -0
  118. package/skills/ue-niagara-effects/references/niagara-data-interfaces.md +491 -0
  119. package/skills/ue-niagara-effects/references/niagara-parameter-types.md +153 -0
  120. package/skills/ue-niagara-effects/references/niagara-workflow.md +115 -0
  121. package/skills/ue-packaging/SKILL.md +116 -0
  122. package/skills/ue-physics-collision/SKILL.md +423 -0
  123. package/skills/ue-physics-collision/references/collision-channel-setup.md +218 -0
  124. package/skills/ue-physics-collision/references/trace-patterns.md +499 -0
  125. package/skills/ue-procedural-generation/SKILL.md +505 -0
  126. package/skills/ue-procedural-generation/references/pcg-node-reference.md +381 -0
  127. package/skills/ue-procedural-generation/references/procedural-mesh-patterns.md +940 -0
  128. package/skills/ue-project-context/SKILL.md +305 -0
  129. package/skills/ue-sequencer-cinematics/SKILL.md +506 -0
  130. package/skills/ue-sequencer-cinematics/references/sequencer-patterns.md +629 -0
  131. package/skills/ue-serialization-savegames/SKILL.md +524 -0
  132. package/skills/ue-serialization-savegames/references/save-system-architecture.md +705 -0
  133. package/skills/ue-state-trees/SKILL.md +489 -0
  134. package/skills/ue-state-trees/references/state-tree-mass-integration.md +184 -0
  135. package/skills/ue-state-trees/references/state-tree-patterns.md +321 -0
  136. package/skills/ue-testing-debugging/SKILL.md +520 -0
  137. package/skills/ue-testing-debugging/references/automation-test-patterns.md +387 -0
  138. package/skills/ue-testing-debugging/references/profiling-commands.md +357 -0
  139. package/skills/ue-ui-umg-slate/SKILL.md +431 -0
  140. package/skills/ue-ui-umg-slate/references/common-ui-setup.md +484 -0
  141. package/skills/ue-ui-umg-slate/references/widget-types.md +487 -0
  142. package/skills/ue-world-level-streaming/SKILL.md +518 -0
  143. package/skills/ue-world-level-streaming/references/streaming-patterns.md +414 -0
  144. package/templates/gamedev/unreal-project-context.md +107 -0
@@ -0,0 +1,523 @@
1
+ ---
2
+ name: ue-editor-tools
3
+ description: "Editor utility widgets, Blutility, detail customization, property editors, editor subsystems"
4
+ category: "gamedev"
5
+ argument-hint: "[tool o customization]"
6
+ user-invocable: true
7
+ allowed-tools: Read, Glob, Grep, Bash, Write, Edit
8
+ ---
9
+
10
+ # UE Editor Tools
11
+
12
+ You are an expert in extending the Unreal Editor with custom tools and workflows.
13
+
14
+ Ask before writing: this skill edits editor module code, utility widgets and detail customizations. Propose the change and get approval before using Write or Edit.
15
+
16
+ ## Context
17
+
18
+ Read `docs/unreal/project-context.md` for editor module structure, engine version, team workflows, and project-specific conventions before providing guidance.
19
+
20
+ ## Before You Start
21
+
22
+ Ask which area the user needs if not clear:
23
+ - **Editor Utility Widget** — UMG panel run from editor right-click
24
+ - **Blutility** — UAssetActionUtility or UActorActionUtility scripted actions
25
+ - **Detail Customization** — Custom property panel (IDetailCustomization / IPropertyTypeCustomization)
26
+ - **Custom Editor Mode** — Viewport mode with specialized interaction (FEdMode)
27
+ - **Asset Type Actions** — Content Browser integration for a custom asset type
28
+ - **Editor Subsystem** — Editor-lifetime singleton (UEditorSubsystem)
29
+ - **Menu / Toolbar Extension** — UToolMenus additions to main menu, toolbars, context menus
30
+
31
+ ---
32
+
33
+ ## Editor Module Setup
34
+
35
+ All editor-extending code must live in a module with `"Type": "Editor"`. Never put `UnrealEd` / `PropertyEditor` includes in a Runtime module without `#if WITH_EDITOR` guards.
36
+
37
+ ```json
38
+ { "Name": "MyGameEditor", "Type": "Editor", "LoadingPhase": "PostEngineInit" }
39
+ ```
40
+
41
+ ```csharp
42
+ // MyGameEditor.Build.cs — key dependencies
43
+ PrivateDependencyModuleNames.AddRange(new string[] {
44
+ "Core", "CoreUObject", "Engine", "UnrealEd",
45
+ "Slate", "SlateCore", "EditorStyle",
46
+ "PropertyEditor", // IDetailCustomization, IPropertyTypeCustomization
47
+ "EditorSubsystem", // UEditorSubsystem
48
+ "Blutility", // UEditorUtilityWidget, UAssetActionUtility
49
+ "ToolMenus", // UToolMenus
50
+ "AssetTools", // FAssetTypeActions_Base
51
+ "MyGame"
52
+ });
53
+ ```
54
+ Module skeleton — every registration in `StartupModule` must be mirrored in `ShutdownModule`:
55
+ ```cpp
56
+ IMPLEMENT_MODULE(FMyGameEditorModule, MyGameEditor)
57
+
58
+ void FMyGameEditorModule::StartupModule()
59
+ {
60
+ FPropertyEditorModule& PropMod =
61
+ FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
62
+ PropMod.RegisterCustomClassLayout(
63
+ UMyDataAsset::StaticClass()->GetFName(),
64
+ FOnGetDetailCustomizationInstance::CreateStatic(
65
+ &FMyDataAssetCustomization::MakeInstance));
66
+ PropMod.NotifyCustomizationModuleChanged();
67
+
68
+ UToolMenus::RegisterStartupCallback(
69
+ FSimpleMulticastDelegate::FDelegate::CreateRaw(
70
+ this, &FMyGameEditorModule::RegisterMenus));
71
+ }
72
+
73
+ void FMyGameEditorModule::ShutdownModule()
74
+ {
75
+ if (FModuleManager::Get().IsModuleLoaded("PropertyEditor"))
76
+ {
77
+ FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor")
78
+ .UnregisterCustomClassLayout(UMyDataAsset::StaticClass()->GetFName());
79
+ }
80
+ UToolMenus::UnRegisterStartupCallback(this);
81
+ if (UToolMenus* TM = UToolMenus::TryGet()) { TM->UnregisterOwner(this); }
82
+ }
83
+ ```
84
+
85
+ > Full boilerplate with asset type actions, editor modes, and factory: `references/editor-module-setup.md`
86
+
87
+ ---
88
+
89
+ ## Editor Utility Widgets
90
+
91
+ `UEditorUtilityWidget` (from `EditorUtilityWidget.h`) extends `UUserWidget` for editor-only UMG panels. Create as a Blueprint subclass (right-click Content Browser > Editor Utilities > Editor Utility Widget). **Run**: right-click the widget asset > Run Editor Utility Widget. Or subclass in C++:
92
+
93
+ ```cpp
94
+ #pragma once
95
+ #include "EditorUtilityWidget.h"
96
+ #include "MyEditorUtilityWidget.generated.h"
97
+
98
+ UCLASS()
99
+ class MYGAMEEDITOR_API UMyEditorUtilityWidget : public UEditorUtilityWidget
100
+ {
101
+ GENERATED_BODY()
102
+ public:
103
+ UFUNCTION(BlueprintCallable, Category = "My Tools")
104
+ void BatchRenameSelectedAssets(const FString& Prefix);
105
+ };
106
+
107
+ // .cpp
108
+ void UMyEditorUtilityWidget::BatchRenameSelectedAssets(const FString& Prefix)
109
+ {
110
+ for (UObject* Asset : UEditorUtilityLibrary::GetSelectedAssets())
111
+ {
112
+ if (Asset)
113
+ {
114
+ const FString Src = Asset->GetOutermost()->GetName(); // e.g. /Game/Folder/OldName
115
+ UEditorAssetLibrary::RenameAsset(Src, FPaths::GetPath(Src) / Prefix + TEXT("_") + Asset->GetName());
116
+ }
117
+ }
118
+ }
119
+ ```
120
+
121
+ Key `UEditorUtilityLibrary` functions (from `EditorUtilityLibrary.h`):
122
+
123
+ | Function | Purpose |
124
+ |---|---|
125
+ | `GetSelectedAssets()` | Currently selected Content Browser assets |
126
+ | `GetSelectedAssetsOfClass(UClass*)` | Filter selection by class |
127
+ | `UEditorAssetLibrary::DeleteAsset(Path)` | Delete asset; `RenameAsset` for move/rename |
128
+ | `GetSelectionSet()` | Selected level actors |
129
+ | `SyncBrowserToFolders(TArray<FString>)` | Sync content browser view |
130
+
131
+ **Content browser filters**: Use `FARFilter` with `IAssetRegistry::GetAssets` for programmatic asset queries by class, path, or tags.
132
+
133
+ Open a widget programmatically:
134
+
135
+ ```cpp
136
+ GEditor->GetEditorSubsystem<UEditorUtilitySubsystem>()
137
+ ->SpawnAndRegisterTab(LoadObject<UEditorUtilityWidgetBlueprint>(
138
+ nullptr, TEXT("/Game/EditorWidgets/BP_MyTool")));
139
+ ```
140
+
141
+ ---
142
+
143
+ ## Blutility: Scripted Actions
144
+
145
+ ### UAssetActionUtility — Asset Right-Click Actions
146
+
147
+ Any `UFUNCTION(CallInEditor)` on a `UAssetActionUtility` subclass appears in the Content Browser context menu. Set `SupportedClasses` in Class Defaults to filter by asset type.
148
+
149
+ ```cpp
150
+ #pragma once
151
+ #include "AssetActionUtility.h" // Engine/Source/Editor/Blutility/Classes/AssetActionUtility.h
152
+ #include "MyAssetActionUtility.generated.h"
153
+
154
+ UCLASS()
155
+ class MYGAMEEDITOR_API UMyAssetActionUtility : public UAssetActionUtility
156
+ {
157
+ GENERATED_BODY()
158
+ public:
159
+ UFUNCTION(CallInEditor, Category = "My Tools")
160
+ void SetTextureCompressionToUI()
161
+ {
162
+ for (UObject* Asset : UEditorUtilityLibrary::GetSelectedAssets())
163
+ {
164
+ if (UTexture2D* Tex = Cast<UTexture2D>(Asset))
165
+ {
166
+ Tex->CompressionSettings = TC_EditorIcon;
167
+ Tex->MarkPackageDirty();
168
+ Tex->PostEditChange();
169
+ }
170
+ }
171
+ }
172
+ };
173
+ ```
174
+
175
+ ### UActorActionUtility — Actor Right-Click Actions
176
+
177
+ Same pattern for level actors. Both `UAssetActionUtility` and `UActorActionUtility` inherit from `UEditorUtilityObject`, the base class for all Blutility actions.
178
+
179
+ ---
180
+
181
+ ## Detail Customizations
182
+
183
+ ### Class Customization — IDetailCustomization
184
+
185
+ ```cpp
186
+ class FMyDataAssetCustomization : public IDetailCustomization
187
+ {
188
+ public:
189
+ static TSharedRef<IDetailCustomization> MakeInstance();
190
+ virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
191
+ // Override the TSharedPtr overload too — store WeakBuilder for async ForceRefreshDetails
192
+ virtual void CustomizeDetails(const TSharedPtr<IDetailLayoutBuilder>& DetailBuilder) override;
193
+ private:
194
+ TWeakPtr<IDetailLayoutBuilder> WeakBuilder;
195
+ };
196
+ // In CustomizeDetails: EditCategory, AddProperty, AddCustomRow, HideCategory
197
+ // Register: PropMod.RegisterCustomClassLayout(UMyClass::StaticClass()->GetFName(), ...MakeInstance)
198
+ // Unregister in ShutdownModule: PropMod.UnregisterCustomClassLayout(...)
199
+ ```
200
+
201
+ ### Struct Customization — IPropertyTypeCustomization
202
+
203
+ ```cpp
204
+ class FMyStructCustomization : public IPropertyTypeCustomization
205
+ {
206
+ public:
207
+ static TSharedRef<IPropertyTypeCustomization> MakeInstance();
208
+ virtual void CustomizeHeader(TSharedRef<IPropertyHandle> Handle,
209
+ FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& Utils) override;
210
+ virtual void CustomizeChildren(TSharedRef<IPropertyHandle> Handle,
211
+ IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& Utils) override;
212
+ };
213
+ // Register: PropMod.RegisterCustomPropertyTypeLayout(FMyStruct::StaticStruct()->GetFName(), ...MakeInstance)
214
+ ```
215
+
216
+ > Full implementations, Slate row patterns, IPropertyHandle read/write, NameContent/ValueContent: `references/detail-customization-patterns.md`
217
+
218
+ ---
219
+
220
+ ## Custom Editor Modes
221
+
222
+ `FEdMode` (from `EdMode.h`) provides specialized viewport interaction. Register globally; only one active per viewport at a time.
223
+
224
+ ```cpp
225
+ // MyEditorMode.h
226
+ class FMyEditorMode : public FEdMode
227
+ {
228
+ public:
229
+ static const FEditorModeID EM_MyMode;
230
+ FMyEditorMode();
231
+ virtual void Enter() override;
232
+ virtual void Exit() override;
233
+ virtual bool HandleClick(FEditorViewportClient*, HHitProxy*,
234
+ const FViewportClick&) override;
235
+ virtual bool MouseMove(FEditorViewportClient* ViewportClient,
236
+ FViewport* Viewport, int32 X, int32 Y) override;
237
+ // Use View->DeprojectFVector2D (FSceneView) for world-space ray from pixel coords
238
+ virtual void Render(const FSceneView*, FViewport*,
239
+ FPrimitiveDrawInterface*) override;
240
+ virtual bool UsesToolkits() const override { return true; }
241
+ };
242
+
243
+ // MyEditorMode.cpp
244
+ const FEditorModeID FMyEditorMode::EM_MyMode = TEXT("EM_MyEditorMode");
245
+
246
+ FMyEditorMode::FMyEditorMode()
247
+ {
248
+ Info = FEditorModeInfo(EM_MyMode,
249
+ FText::FromString("My Editor Mode"), FSlateIcon(), /*bVisible=*/true);
250
+ }
251
+
252
+ void FMyEditorMode::Enter()
253
+ {
254
+ FEdMode::Enter();
255
+ if (!Toolkit.IsValid())
256
+ {
257
+ Toolkit = MakeShareable(new FMyEditorModeToolkit);
258
+ Toolkit->Init(Owner->GetToolkitHost());
259
+ }
260
+ }
261
+
262
+ void FMyEditorMode::Exit()
263
+ {
264
+ if (Toolkit.IsValid())
265
+ FToolkitManager::Get().CloseToolkit(Toolkit.ToSharedRef());
266
+ FEdMode::Exit();
267
+ }
268
+
269
+ void FMyEditorMode::Render(const FSceneView* View, FViewport* Viewport,
270
+ FPrimitiveDrawInterface* PDI)
271
+ {
272
+ FEdMode::Render(View, Viewport, PDI);
273
+ DrawWireSphere(PDI, FVector::ZeroVector, FLinearColor::Green, 50.f, 16, SDPG_World);
274
+ }
275
+ ```
276
+
277
+ Register/unregister in module lifecycle:
278
+
279
+ ```cpp
280
+ // StartupModule
281
+ FEditorModeRegistry::Get().RegisterMode<FMyEditorMode>(
282
+ FMyEditorMode::EM_MyMode, FText::FromString("My Mode"), FSlateIcon(), true);
283
+ // ShutdownModule
284
+ FEditorModeRegistry::Get().UnregisterMode(FMyEditorMode::EM_MyMode);
285
+ ```
286
+
287
+ ---
288
+
289
+ ## Asset Type Actions
290
+
291
+ `FAssetTypeActions_Base` (from `AssetTypeActions_Base.h`) controls Content Browser appearance and context menu for custom asset types.
292
+
293
+ ```cpp
294
+ class FMyAssetTypeActions : public FAssetTypeActions_Base
295
+ {
296
+ public:
297
+ virtual FText GetName() const override { return FText::FromString("My Asset"); }
298
+ virtual FColor GetTypeColor() const override { return FColor(200, 100, 50); }
299
+ virtual UClass* GetSupportedClass() const override { return UMyAsset::StaticClass(); }
300
+ virtual uint32 GetCategories() override { return EAssetTypeCategories::Misc; }
301
+
302
+ virtual void OpenAssetEditor(const TArray<UObject*>& InObjects,
303
+ TSharedPtr<IToolkitHost> EditWithinLevelEditor) override
304
+ {
305
+ for (UObject* Obj : InObjects)
306
+ if (UMyAsset* Asset = Cast<UMyAsset>(Obj))
307
+ MakeShareable(new FMyAssetEditor)->InitMyEditor(
308
+ EToolkitMode::Standalone, EditWithinLevelEditor, Asset);
309
+ }
310
+ };
311
+ ```
312
+
313
+ Register in `StartupModule`, keep reference, unregister in `ShutdownModule`.
314
+
315
+ ### FAssetEditorToolkit — Tab-Based Asset Editor Window
316
+
317
+ ```cpp
318
+ class FMyAssetEditor : public FAssetEditorToolkit
319
+ {
320
+ public:
321
+ void InitMyEditor(EToolkitMode::Type Mode, TSharedPtr<IToolkitHost> Host, UMyAsset* Asset);
322
+ virtual FName GetToolkitFName() const override { return "MyAssetEditor"; }
323
+ virtual FText GetBaseToolkitName() const override { return INVTEXT("My Asset Editor"); }
324
+ virtual void RegisterTabSpawners(const TSharedRef<FTabManager>& TabMgr) override;
325
+ virtual void UnregisterTabSpawners(const TSharedRef<FTabManager>& TabMgr) override;
326
+ };
327
+ // Call InitAssetEditor() inside InitMyEditor to set up the tab layout via FTabManager::NewLayout
328
+ ```
329
+
330
+ Query open editors programmatically via `IAssetEditorInstance`:
331
+
332
+ ```cpp
333
+ IAssetEditorInstance* Editor = GEditor->GetEditorSubsystem<UAssetEditorSubsystem>()
334
+ ->FindEditorForAsset(MyAsset, /*bFocusIfOpen=*/false);
335
+ if (Editor) Editor->FocusWindow(MyAsset); // CloseWindow(EAssetEditorCloseReason::AssetEditorHostClosed) — no-arg form deprecated 5.3
336
+ ```
337
+
338
+ ### Asset Factories
339
+
340
+ `UFactory` subclasses allow the Content Browser's "Add" menu to create new custom assets:
341
+
342
+ ```cpp
343
+ UCLASS()
344
+ class UMyDataFactory : public UFactory
345
+ {
346
+ GENERATED_BODY()
347
+ public:
348
+ UMyDataFactory()
349
+ {
350
+ SupportedClass = UMyDataAsset::StaticClass();
351
+ bCreateNew = true; // appears in "Add" menu
352
+ bEditAfterNew = true; // opens editor after creation
353
+ }
354
+ virtual UObject* FactoryCreateNew(UClass* InClass, UObject* InParent,
355
+ FName InName, EObjectFlags Flags, UObject* Context,
356
+ FFeedbackContext* Warn) override
357
+ {
358
+ return NewObject<UMyDataAsset>(InParent, InClass, InName, Flags);
359
+ }
360
+ };
361
+ ```
362
+
363
+ ### Custom Thumbnails
364
+
365
+ ```cpp
366
+ UCLASS()
367
+ class UMyThumbnailRenderer : public UThumbnailRenderer
368
+ {
369
+ GENERATED_BODY()
370
+ virtual void Draw(UObject* Object, int32 X, int32 Y, uint32 Width, uint32 Height,
371
+ FRenderTarget* Target, FCanvas* Canvas, bool bAdditionalViewFamily) override;
372
+ };
373
+ // Register in module startup:
374
+ UThumbnailManager::Get().RegisterCustomRenderer(UMyDataAsset::StaticClass(),
375
+ UMyThumbnailRenderer::StaticClass());
376
+ ```
377
+
378
+ ---
379
+
380
+ ## Editor Subsystems
381
+
382
+ `UEditorSubsystem` (from `EditorSubsystem.h`) — editor-lifetime singletons, auto-discovered (no registration needed). Access via `GEditor->GetEditorSubsystem<T>()`.
383
+
384
+ ```cpp
385
+ UCLASS()
386
+ class MYGAMEEDITOR_API UMyEditorSubsystem : public UEditorSubsystem
387
+ {
388
+ GENERATED_BODY()
389
+ public:
390
+ virtual void Initialize(FSubsystemCollectionBase& Collection) override
391
+ {
392
+ Super::Initialize(Collection);
393
+ FEditorDelegates::PostUndoRedo.AddUObject(this, &UMyEditorSubsystem::OnPostUndoRedo);
394
+ }
395
+ virtual void Deinitialize() override
396
+ {
397
+ FEditorDelegates::PostUndoRedo.RemoveAll(this);
398
+ Super::Deinitialize();
399
+ }
400
+ };
401
+ ```
402
+
403
+ Built-in subsystems:
404
+
405
+ | Subsystem | Purpose |
406
+ |---|---|
407
+ | `UEditorActorSubsystem` | Select, spawn, delete level actors |
408
+ | `UEditorAssetSubsystem` | Load, save, duplicate assets |
409
+ | `ULevelEditorSubsystem` | Open, save, manage levels |
410
+ | `UEditorUtilitySubsystem` | Spawn editor utility widget tabs |
411
+
412
+ ---
413
+
414
+ ## Menu and Toolbar Extensions (UToolMenus)
415
+
416
+ UToolMenus (UE5+) replaces `FExtender`. Register inside the startup callback so Slate is ready:
417
+
418
+ ```cpp
419
+ void FMyGameEditorModule::RegisterMenus()
420
+ {
421
+ FToolMenuOwnerScoped OwnerScoped(this);
422
+
423
+ // Main menu
424
+ UToolMenu* Menu = UToolMenus::Get()->ExtendMenu("LevelEditor.MainMenu.Window");
425
+ Menu->FindOrAddSection("MyGame").AddMenuEntry("OpenMyPanel",
426
+ FText::FromString("My Panel"), FText::GetEmpty(),
427
+ FSlateIcon(FAppStyle::GetAppStyleSetName(), "Icons.Settings"),
428
+ FUIAction(FExecuteAction::CreateLambda([]() {
429
+ GEditor->GetEditorSubsystem<UEditorUtilitySubsystem>()
430
+ ->SpawnAndRegisterTab(/* WidgetBP */nullptr);
431
+ })));
432
+
433
+ // Toolbar button
434
+ UToolMenu* TB = UToolMenus::Get()->ExtendMenu(
435
+ "LevelEditor.LevelEditorToolBar.PlayToolBar");
436
+ TB->FindOrAddSection("MyTools").AddEntry(
437
+ FToolMenuEntry::InitToolBarButton("MyBtn",
438
+ FUIAction(FExecuteAction::CreateLambda([]() {})),
439
+ FText::FromString("My Tool"), FText::GetEmpty(),
440
+ FSlateIcon(FAppStyle::GetAppStyleSetName(), "Icons.Toolbar.Settings")));
441
+ }
442
+ ```
443
+
444
+ Always unregister: `UToolMenus::UnRegisterStartupCallback(this)` + `TryGet()->UnregisterOwner(this)`.
445
+
446
+ ### Command Registration — TCommands Pattern
447
+
448
+ ```cpp
449
+ class FMyCommands : public TCommands<FMyCommands>
450
+ {
451
+ public:
452
+ FMyCommands() : TCommands("MyEditor", INVTEXT("My Editor"), NAME_None, FAppStyle::GetAppStyleSetName()) {}
453
+ virtual void RegisterCommands() override;
454
+ TSharedPtr<FUICommandInfo> OpenPanel;
455
+ };
456
+ void FMyCommands::RegisterCommands()
457
+ {
458
+ UI_COMMAND(OpenPanel, "My Panel", "Opens the panel", EUserInterfaceActionType::Button, FInputChord());
459
+ }
460
+ // In StartupModule: FMyCommands::Register(); bind via CommandList->MapAction(...)
461
+ // In ShutdownModule: FMyCommands::Unregister();
462
+ ```
463
+
464
+ ---
465
+
466
+ ## Data Validation
467
+
468
+ ```cpp
469
+ // UEditorValidatorBase — auto-discovered by Editor > Tools > Validate Assets
470
+ UCLASS()
471
+ class UMyValidator : public UEditorValidatorBase
472
+ {
473
+ GENERATED_BODY()
474
+ virtual bool CanValidateAsset_Implementation(const FAssetData& InAssetData, UObject* InObject, FDataValidationContext& InContext) const override
475
+ { return InObject && InObject->IsA<UMyDataAsset>(); }
476
+ virtual EDataValidationResult ValidateLoadedAsset_Implementation(
477
+ const FAssetData& InAssetData, UObject* InAsset, FDataValidationContext& Context) override;
478
+ };
479
+ // Also runnable via commandlet: UnrealEditor-Cmd -run=DataValidation
480
+ ```
481
+
482
+ ---
483
+
484
+ ## Common Mistakes
485
+
486
+ | Mistake | Fix |
487
+ |---|---|
488
+ | Editor headers in Runtime modules | Move to Editor module; use `#if WITH_EDITOR` for runtime-side editor hooks |
489
+ | No `UnregisterCustomClassLayout` in `ShutdownModule` | Always pair register/unregister; crashes Live Coding reload |
490
+ | Raw pointer capture in Slate lambdas | Capture as `TWeakPtr`; pin before use |
491
+ | `LoadingPhase: Default` for editor extension module | Use `PostEngineInit` for modules registering menus or customizations |
492
+ | `UEditorUtilityWidget` referenced from Runtime | Editor-only; will not exist in packaged builds |
493
+ | `ForceRefreshDetails()` on every value change | Use only when layout structure changes; use handles/attributes for values |
494
+ | `RegisterMode` without `UnregisterMode` | Crashes on plugin reload |
495
+
496
+ ---
497
+
498
+ ## Related Skills
499
+
500
+ - `/ue-ui-umg-slate` — Slate widget fundamentals (SNew, TAttribute, FReply, SBox, SHorizontalBox)
501
+ - `/ue-module-build-system` — Editor module `.Build.cs`, LoadingPhase, `WITH_EDITOR` guards
502
+ - `/ue-data-assets-tables` — Custom UDataAsset types that need asset editors and type actions
503
+ - `/ue-cpp-foundations` — UPROPERTY, UFUNCTION, UObject reflection system
504
+
505
+ ---
506
+
507
+ ## Checklist
508
+
509
+ Validate before considering the work done:
510
+
511
+ - [ ] Editor-only code lives in an editor module and is excluded from runtime targets
512
+ - [ ] The tool fails safely on unexpected asset state instead of corrupting data
513
+ - [ ] Bulk operations are undoable or clearly warn before running
514
+ - [ ] Conventions in `docs/unreal/project-context.md` were read and followed
515
+ - [ ] The Common Mistakes section of this skill was reviewed against the change
516
+
517
+ **PASS** — every item holds. **CONCERNS** — the work stands but at least one item is unresolved and worth flagging to the user. **FAIL** — an item is violated in a way that breaks correctness, replication or the frame budget; fix it before handing the work over.
518
+
519
+ ## Next Steps
520
+
521
+ - `/ue-module-build-system` — confirm the editor module is wired correctly
522
+ - `/code-review` — architectural and quality review of the change
523
+ - `/commit` — conventional commit once the change is verified