claude-code-arcane 2.3.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 (103) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +54 -6
  3. package/agents/game/ue-blueprint-specialist.md +125 -0
  4. package/agents/game/ue-gas-specialist.md +133 -0
  5. package/agents/game/ue-umg-specialist.md +131 -0
  6. package/dist/cli.js +5 -0
  7. package/docs/SKILLS-CATALOG.md +42 -3
  8. package/docs/USER-GUIDE.md +2 -2
  9. package/hooks/detect-unreal.sh +90 -0
  10. package/package.json +1 -1
  11. package/profiles/unreal-dev.yaml +97 -0
  12. package/rules/gamedev/unreal-code.md +125 -0
  13. package/skills/install-mcp/SKILL.md +135 -46
  14. package/skills/install-mcp/references/unreal-operations.md +55 -0
  15. package/skills/ue-actor-component-architecture/SKILL.md +509 -0
  16. package/skills/ue-actor-component-architecture/references/actor-lifecycle.md +301 -0
  17. package/skills/ue-actor-component-architecture/references/component-types.md +592 -0
  18. package/skills/ue-ai-navigation/SKILL.md +526 -0
  19. package/skills/ue-ai-navigation/references/behavior-tree-patterns.md +392 -0
  20. package/skills/ue-ai-navigation/references/behavior-tree-workflow.md +107 -0
  21. package/skills/ue-ai-navigation/references/custom-bttask.md +104 -0
  22. package/skills/ue-ai-navigation/references/eqs-reference.md +423 -0
  23. package/skills/ue-animation-system/SKILL.md +521 -0
  24. package/skills/ue-animation-system/references/anim-notify-reference.md +486 -0
  25. package/skills/ue-animation-system/references/locomotion-setup.md +550 -0
  26. package/skills/ue-async-threading/SKILL.md +433 -0
  27. package/skills/ue-async-threading/references/thread-safety-guide.md +213 -0
  28. package/skills/ue-async-threading/references/threading-patterns.md +345 -0
  29. package/skills/ue-audio-system/SKILL.md +527 -0
  30. package/skills/ue-audio-system/references/audio-setup-patterns.md +700 -0
  31. package/skills/ue-blueprints/SKILL.md +134 -0
  32. package/skills/ue-blueprints/references/communication.md +82 -0
  33. package/skills/ue-character-movement/SKILL.md +475 -0
  34. package/skills/ue-character-movement/references/cmc-extension-patterns.md +335 -0
  35. package/skills/ue-character-movement/references/movement-pipeline.md +189 -0
  36. package/skills/ue-cpp-foundations/SKILL.md +533 -0
  37. package/skills/ue-cpp-foundations/references/components-and-gc.md +107 -0
  38. package/skills/ue-cpp-foundations/references/container-patterns.md +544 -0
  39. package/skills/ue-cpp-foundations/references/delegate-patterns.md +497 -0
  40. package/skills/ue-cpp-foundations/references/gameplay-classes-cpp.md +136 -0
  41. package/skills/ue-cpp-foundations/references/property-specifiers.md +306 -0
  42. package/skills/ue-data-assets-tables/SKILL.md +527 -0
  43. package/skills/ue-data-assets-tables/references/asset-loading-patterns.md +306 -0
  44. package/skills/ue-data-assets-tables/references/data-driven-design.md +378 -0
  45. package/skills/ue-editor-tools/SKILL.md +523 -0
  46. package/skills/ue-editor-tools/references/detail-customization-patterns.md +395 -0
  47. package/skills/ue-editor-tools/references/editor-module-setup.md +480 -0
  48. package/skills/ue-game-features/SKILL.md +477 -0
  49. package/skills/ue-game-features/references/experience-system.md +222 -0
  50. package/skills/ue-game-features/references/game-feature-patterns.md +308 -0
  51. package/skills/ue-gameplay-abilities/SKILL.md +518 -0
  52. package/skills/ue-gameplay-abilities/references/ability-task-reference.md +391 -0
  53. package/skills/ue-gameplay-abilities/references/gameplay-effect-reference.md +397 -0
  54. package/skills/ue-gameplay-abilities/references/gas-setup-patterns.md +369 -0
  55. package/skills/ue-gameplay-framework/SKILL.md +466 -0
  56. package/skills/ue-gameplay-framework/references/framework-class-map.md +243 -0
  57. package/skills/ue-input-system/SKILL.md +394 -0
  58. package/skills/ue-input-system/references/cpp-setup.md +131 -0
  59. package/skills/ue-input-system/references/enhanced-input-workflow.md +117 -0
  60. package/skills/ue-input-system/references/input-action-reference.md +731 -0
  61. package/skills/ue-mass-entity/SKILL.md +476 -0
  62. package/skills/ue-mass-entity/references/mass-entity-patterns.md +319 -0
  63. package/skills/ue-mass-entity/references/mass-fragment-reference.md +119 -0
  64. package/skills/ue-materials-rendering/SKILL.md +526 -0
  65. package/skills/ue-materials-rendering/references/material-parameter-reference.md +216 -0
  66. package/skills/ue-materials-rendering/references/post-process-settings.md +383 -0
  67. package/skills/ue-module-build-system/SKILL.md +487 -0
  68. package/skills/ue-module-build-system/references/build-cs-reference.md +418 -0
  69. package/skills/ue-module-build-system/references/common-build-errors.md +381 -0
  70. package/skills/ue-naming-conventions/SKILL.md +142 -0
  71. package/skills/ue-naming-conventions/references/asset-name-modifiers.md +233 -0
  72. package/skills/ue-naming-conventions/references/content-structure.md +83 -0
  73. package/skills/ue-networking-replication/SKILL.md +452 -0
  74. package/skills/ue-networking-replication/references/replication-patterns.md +526 -0
  75. package/skills/ue-networking-replication/references/rpc-decision-guide.md +393 -0
  76. package/skills/ue-niagara-effects/SKILL.md +531 -0
  77. package/skills/ue-niagara-effects/references/niagara-data-interfaces.md +491 -0
  78. package/skills/ue-niagara-effects/references/niagara-parameter-types.md +153 -0
  79. package/skills/ue-niagara-effects/references/niagara-workflow.md +115 -0
  80. package/skills/ue-packaging/SKILL.md +116 -0
  81. package/skills/ue-physics-collision/SKILL.md +423 -0
  82. package/skills/ue-physics-collision/references/collision-channel-setup.md +218 -0
  83. package/skills/ue-physics-collision/references/trace-patterns.md +499 -0
  84. package/skills/ue-procedural-generation/SKILL.md +505 -0
  85. package/skills/ue-procedural-generation/references/pcg-node-reference.md +381 -0
  86. package/skills/ue-procedural-generation/references/procedural-mesh-patterns.md +940 -0
  87. package/skills/ue-project-context/SKILL.md +305 -0
  88. package/skills/ue-sequencer-cinematics/SKILL.md +506 -0
  89. package/skills/ue-sequencer-cinematics/references/sequencer-patterns.md +629 -0
  90. package/skills/ue-serialization-savegames/SKILL.md +524 -0
  91. package/skills/ue-serialization-savegames/references/save-system-architecture.md +705 -0
  92. package/skills/ue-state-trees/SKILL.md +489 -0
  93. package/skills/ue-state-trees/references/state-tree-mass-integration.md +184 -0
  94. package/skills/ue-state-trees/references/state-tree-patterns.md +321 -0
  95. package/skills/ue-testing-debugging/SKILL.md +520 -0
  96. package/skills/ue-testing-debugging/references/automation-test-patterns.md +387 -0
  97. package/skills/ue-testing-debugging/references/profiling-commands.md +357 -0
  98. package/skills/ue-ui-umg-slate/SKILL.md +431 -0
  99. package/skills/ue-ui-umg-slate/references/common-ui-setup.md +484 -0
  100. package/skills/ue-ui-umg-slate/references/widget-types.md +487 -0
  101. package/skills/ue-world-level-streaming/SKILL.md +518 -0
  102. package/skills/ue-world-level-streaming/references/streaming-patterns.md +414 -0
  103. package/templates/gamedev/unreal-project-context.md +107 -0
@@ -0,0 +1,395 @@
1
+ # Detail Customization Patterns
2
+
3
+ Reference for common `IDetailCustomization` and `IPropertyTypeCustomization` patterns with Slate widget examples. All patterns are based on the real UE source interfaces in `Engine/Source/Editor/PropertyEditor/Public/`.
4
+
5
+ ---
6
+
7
+ ## IDetailLayoutBuilder Key Methods
8
+
9
+ From `DetailLayoutBuilder.h` (`IDetailLayoutBuilder`):
10
+
11
+ ```cpp
12
+ // Edit or create a category
13
+ IDetailCategoryBuilder& EditCategory(
14
+ FName CategoryName,
15
+ const FText& NewLocalizedDisplayName = FText::GetEmpty(),
16
+ ECategoryPriority::Type CategoryType = ECategoryPriority::Default);
17
+
18
+ // Hide an entire category
19
+ void HideCategory(FName CategoryName);
20
+
21
+ // Get a property handle by member name — use GET_MEMBER_NAME_CHECKED for safety
22
+ TSharedRef<IPropertyHandle> GetProperty(
23
+ const FName PropertyPath,
24
+ const UStruct* ClassOutermost = nullptr,
25
+ FName InstanceName = NAME_None) const;
26
+
27
+ // Hide a specific property
28
+ void HideProperty(const TSharedPtr<IPropertyHandle> PropertyHandle);
29
+
30
+ // Get the objects currently being customized
31
+ void GetObjectsBeingCustomized(TArray<TWeakObjectPtr<UObject>>& OutObjects) const;
32
+
33
+ // Force rebuild of the entire detail layout
34
+ void ForceRefreshDetails();
35
+
36
+ // Add a property to its auto-detected category (preserves original category)
37
+ IDetailPropertyRow& AddPropertyToCategory(TSharedPtr<IPropertyHandle> InPropertyHandle);
38
+ ```
39
+
40
+ ---
41
+
42
+ ## Category Priority Reference
43
+
44
+ `ECategoryPriority` controls sort order in the details panel (lower value = higher position):
45
+
46
+ | Enum Value | Sort Position |
47
+ |---|---|
48
+ | `Variable` | Highest (top) |
49
+ | `Transform` | Near top |
50
+ | `Important` | High |
51
+ | `TypeSpecific` | Middle |
52
+ | `Default` | Standard |
53
+ | `Uncommon` | Bottom |
54
+
55
+ ```cpp
56
+ IDetailCategoryBuilder& CoreCat =
57
+ DetailBuilder.EditCategory("Core", FText::GetEmpty(), ECategoryPriority::Important);
58
+ IDetailCategoryBuilder& AdvancedCat =
59
+ DetailBuilder.EditCategory("Advanced", FText::GetEmpty(), ECategoryPriority::Uncommon);
60
+ ```
61
+
62
+ ---
63
+
64
+ ## IDetailPropertyRow — Fluent Modifier API
65
+
66
+ From `IDetailPropertyRow.h`. Methods return `IDetailPropertyRow&` for chaining:
67
+
68
+ ```cpp
69
+ IDetailCategoryBuilder& Category = DetailBuilder.EditCategory("Settings");
70
+ IDetailPropertyRow& Row = Category.AddProperty(
71
+ DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UMyClass, MyFloat)));
72
+
73
+ Row.DisplayName(FText::FromString("Speed"))
74
+ .ToolTip(FText::FromString("Movement speed in cm/s"))
75
+ .ShowPropertyButtons(false) // hide reset-to-default arrow
76
+ .EditCondition(
77
+ TAttribute<bool>::Create([this]() { return bIsEnabled; }),
78
+ FOnBooleanValueChanged::CreateLambda([this](bool bNewValue)
79
+ {
80
+ bIsEnabled = bNewValue;
81
+ })
82
+ )
83
+ .IsEnabled(TAttribute<bool>::Create([]() { return true; }))
84
+ .Visibility(TAttribute<EVisibility>::Create([]()
85
+ {
86
+ return EVisibility::Visible;
87
+ }));
88
+ ```
89
+
90
+ ---
91
+
92
+ ## Custom Widget Row Patterns
93
+
94
+ ### Full Custom Widget Row
95
+
96
+ ```cpp
97
+ Category.AddCustomRow(FText::FromString("SearchFilter"))
98
+ [
99
+ SNew(SHorizontalBox)
100
+ + SHorizontalBox::Slot()
101
+ .FillWidth(1.f)
102
+ .VAlign(VAlign_Center)
103
+ [
104
+ SNew(STextBlock)
105
+ .Text(FText::FromString("Custom Label"))
106
+ .Font(IDetailLayoutBuilder::GetDetailFont())
107
+ ]
108
+ + SHorizontalBox::Slot()
109
+ .AutoWidth()
110
+ .Padding(FMargin(4.f, 0.f))
111
+ [
112
+ SNew(SButton)
113
+ .Text(FText::FromString("Action"))
114
+ .OnClicked(FOnClicked::CreateLambda([]()
115
+ {
116
+ return FReply::Handled();
117
+ }))
118
+ ]
119
+ ];
120
+ ```
121
+
122
+ ### NameContent / ValueContent Split Row
123
+
124
+ The detail panel uses a two-column layout. Use `NameContent()` and `ValueContent()` to align with standard property rows:
125
+
126
+ ```cpp
127
+ Category.AddCustomRow(FText::FromString("MyCustomProp"))
128
+ .NameContent()
129
+ [
130
+ SNew(STextBlock)
131
+ .Text(FText::FromString("Custom Property"))
132
+ .Font(IDetailLayoutBuilder::GetDetailFont())
133
+ ]
134
+ .ValueContent()
135
+ .MinDesiredWidth(125.f)
136
+ .MaxDesiredWidth(400.f)
137
+ [
138
+ SNew(SEditableTextBox)
139
+ .Text_Lambda([]() { return FText::FromString("value"); })
140
+ .OnTextCommitted_Lambda([](const FText& NewText, ETextCommit::Type CommitType)
141
+ {
142
+ // handle commit
143
+ })
144
+ ];
145
+ ```
146
+
147
+ ### Override Default Property Widget While Keeping Children
148
+
149
+ ```cpp
150
+ TSharedRef<IPropertyHandle> Handle =
151
+ DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UMyClass, MyProp));
152
+ IDetailPropertyRow& Row = Category.AddProperty(Handle);
153
+ TSharedPtr<SWidget> DefaultNameWidget;
154
+ TSharedPtr<SWidget> DefaultValueWidget;
155
+ Row.GetDefaultWidgets(DefaultNameWidget, DefaultValueWidget);
156
+
157
+ Row.CustomWidget(/*bShowChildren=*/true)
158
+ .NameContent()
159
+ [
160
+ DefaultNameWidget.ToSharedRef()
161
+ ]
162
+ .ValueContent()
163
+ [
164
+ SNew(SHorizontalBox)
165
+ + SHorizontalBox::Slot().FillWidth(1.f)
166
+ [
167
+ DefaultValueWidget.ToSharedRef()
168
+ ]
169
+ + SHorizontalBox::Slot().AutoWidth().Padding(4.f, 0.f)
170
+ [
171
+ SNew(SButton)
172
+ .Text(FText::FromString("..."))
173
+ ]
174
+ ];
175
+ ```
176
+
177
+ ---
178
+
179
+ ## IPropertyTypeCustomization — Struct Header Patterns
180
+
181
+ From `IPropertyTypeCustomization.h`. `CustomizeHeader` sets up the collapsed/inline row; `CustomizeChildren` sets up the expanded rows.
182
+
183
+ ### Compact Inline Display
184
+
185
+ Show all struct fields inline in the header, no expansion needed:
186
+
187
+ ```cpp
188
+ void FMyVectorCustomization::CustomizeHeader(
189
+ TSharedRef<IPropertyHandle> PropertyHandle,
190
+ FDetailWidgetRow& HeaderRow,
191
+ IPropertyTypeCustomizationUtils& CustomizationUtils)
192
+ {
193
+ // Get child handles for X, Y, Z
194
+ TSharedPtr<IPropertyHandle> XHandle = PropertyHandle->GetChildHandle(
195
+ GET_MEMBER_NAME_CHECKED(FMyVector, X));
196
+ TSharedPtr<IPropertyHandle> YHandle = PropertyHandle->GetChildHandle(
197
+ GET_MEMBER_NAME_CHECKED(FMyVector, Y));
198
+
199
+ HeaderRow
200
+ .NameContent()
201
+ [
202
+ PropertyHandle->CreatePropertyNameWidget()
203
+ ]
204
+ .ValueContent()
205
+ .MinDesiredWidth(200.f)
206
+ [
207
+ SNew(SHorizontalBox)
208
+ + SHorizontalBox::Slot().Padding(2.f)
209
+ [
210
+ XHandle->CreatePropertyValueWidget()
211
+ ]
212
+ + SHorizontalBox::Slot().Padding(2.f)
213
+ [
214
+ YHandle->CreatePropertyValueWidget()
215
+ ]
216
+ ];
217
+ }
218
+
219
+ void FMyVectorCustomization::CustomizeChildren(
220
+ TSharedRef<IPropertyHandle> PropertyHandle,
221
+ IDetailChildrenBuilder& ChildBuilder,
222
+ IPropertyTypeCustomizationUtils& CustomizationUtils)
223
+ {
224
+ // Leave empty to suppress expanded child rows
225
+ }
226
+ ```
227
+
228
+ ### Summary Text in Header, Children in Expansion
229
+
230
+ ```cpp
231
+ void FMyRangeCustomization::CustomizeHeader(
232
+ TSharedRef<IPropertyHandle> PropertyHandle,
233
+ FDetailWidgetRow& HeaderRow,
234
+ IPropertyTypeCustomizationUtils& CustomizationUtils)
235
+ {
236
+ TSharedPtr<IPropertyHandle> MinHandle =
237
+ PropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FMyRange, Min));
238
+ TSharedPtr<IPropertyHandle> MaxHandle =
239
+ PropertyHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FMyRange, Max));
240
+
241
+ HeaderRow
242
+ .NameContent()
243
+ [
244
+ PropertyHandle->CreatePropertyNameWidget()
245
+ ]
246
+ .ValueContent()
247
+ [
248
+ SNew(STextBlock)
249
+ .Text_Lambda([MinHandle, MaxHandle]()
250
+ {
251
+ float MinVal = 0.f, MaxVal = 0.f;
252
+ MinHandle->GetValue(MinVal);
253
+ MaxHandle->GetValue(MaxVal);
254
+ return FText::Format(
255
+ FText::FromString("[{0} .. {1}]"),
256
+ FText::AsNumber(MinVal),
257
+ FText::AsNumber(MaxVal));
258
+ })
259
+ .Font(IPropertyTypeCustomizationUtils::GetRegularFont())
260
+ ];
261
+ }
262
+
263
+ void FMyRangeCustomization::CustomizeChildren(
264
+ TSharedRef<IPropertyHandle> PropertyHandle,
265
+ IDetailChildrenBuilder& ChildBuilder,
266
+ IPropertyTypeCustomizationUtils& CustomizationUtils)
267
+ {
268
+ uint32 NumChildren = 0;
269
+ PropertyHandle->GetNumChildren(NumChildren);
270
+ for (uint32 i = 0; i < NumChildren; ++i)
271
+ {
272
+ ChildBuilder.AddProperty(PropertyHandle->GetChildHandle(i).ToSharedRef());
273
+ }
274
+ }
275
+ ```
276
+
277
+ ---
278
+
279
+ ## IPropertyHandle — Reading and Writing Values
280
+
281
+ `IPropertyHandle` provides type-safe access to property values across one or more selected objects:
282
+
283
+ ```cpp
284
+ TSharedRef<IPropertyHandle> Handle =
285
+ DetailBuilder.GetProperty(GET_MEMBER_NAME_CHECKED(UMyClass, Speed));
286
+
287
+ // Read
288
+ float SpeedValue = 0.f;
289
+ FPropertyAccess::Result Result = Handle->GetValue(SpeedValue);
290
+ if (Result == FPropertyAccess::MultipleValues)
291
+ {
292
+ // multiple objects with different values selected
293
+ }
294
+
295
+ // Write — automatically handles transactions and PostEditChange
296
+ Handle->SetValue(150.f);
297
+
298
+ // Get as display string
299
+ FString DisplayStr;
300
+ Handle->GetValueAsDisplayString(DisplayStr);
301
+
302
+ // Enumerate per-object values
303
+ Handle->EnumerateRawData([](void* RawData, const int32 DataIndex, const int32 NumDatas) -> bool
304
+ {
305
+ float* SpeedPtr = static_cast<float*>(RawData);
306
+ *SpeedPtr = 100.f;
307
+ return true; // continue iteration
308
+ });
309
+
310
+ // Notify post-change
311
+ Handle->NotifyPostChange(EPropertyChangeType::ValueSet);
312
+ ```
313
+
314
+ ---
315
+
316
+ ## Fonts
317
+
318
+ Always use `IDetailLayoutBuilder` or `IPropertyTypeCustomizationUtils` static helpers for fonts to stay visually consistent with the details panel:
319
+
320
+ ```cpp
321
+ FAppStyle::GetFontStyle(TEXT("PropertyWindow.NormalFont")) // regular body
322
+ FAppStyle::GetFontStyle(TEXT("PropertyWindow.BoldFont")) // bold label
323
+ FAppStyle::GetFontStyle(TEXT("PropertyWindow.ItalicFont")) // italic/dimmed
324
+
325
+ // Shorthand statics:
326
+ IDetailLayoutBuilder::GetDetailFont()
327
+ IDetailLayoutBuilder::GetDetailFontBold()
328
+ IDetailLayoutBuilder::GetDetailFontItalic()
329
+ IPropertyTypeCustomizationUtils::GetRegularFont()
330
+ IPropertyTypeCustomizationUtils::GetBoldFont()
331
+ ```
332
+
333
+ ---
334
+
335
+ ## Registration Reference
336
+
337
+ All registrations go in `StartupModule`; all unregistrations in `ShutdownModule`.
338
+
339
+ ```cpp
340
+ // In StartupModule
341
+ FPropertyEditorModule& PropertyModule =
342
+ FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
343
+
344
+ // Class customization
345
+ PropertyModule.RegisterCustomClassLayout(
346
+ UMyClass::StaticClass()->GetFName(),
347
+ FOnGetDetailCustomizationInstance::CreateStatic(
348
+ &FMyClassCustomization::MakeInstance));
349
+
350
+ // Struct / property type customization
351
+ PropertyModule.RegisterCustomPropertyTypeLayout(
352
+ FMyStruct::StaticStruct()->GetFName(),
353
+ FOnGetPropertyTypeCustomizationInstance::CreateStatic(
354
+ &FMyStructCustomization::MakeInstance));
355
+
356
+ // Notify the editor that customizations changed
357
+ PropertyModule.NotifyCustomizationModuleChanged();
358
+
359
+ // In ShutdownModule
360
+ if (FModuleManager::Get().IsModuleLoaded("PropertyEditor"))
361
+ {
362
+ FPropertyEditorModule& PM =
363
+ FModuleManager::GetModuleChecked<FPropertyEditorModule>("PropertyEditor");
364
+ PM.UnregisterCustomClassLayout(UMyClass::StaticClass()->GetFName());
365
+ PM.UnregisterCustomPropertyTypeLayout(FMyStruct::StaticStruct()->GetFName());
366
+ }
367
+ ```
368
+
369
+ ---
370
+
371
+ ## Instanced Property Type Layout (Scoped)
372
+
373
+ Register a property type customization for a specific details panel instance only, not globally. Useful when you want different presentation in different contexts:
374
+
375
+ ```cpp
376
+ void FMyClassCustomization::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
377
+ {
378
+ // Register a type customization only within this details panel
379
+ DetailBuilder.RegisterInstancedCustomPropertyTypeLayout(
380
+ FMyStruct::StaticStruct()->GetFName(),
381
+ FOnGetPropertyTypeCustomizationInstance::CreateStatic(
382
+ &FMyStructInContextCustomization::MakeInstance));
383
+ }
384
+ ```
385
+
386
+ ---
387
+
388
+ ## Common Patterns Checklist
389
+
390
+ - Use `GET_MEMBER_NAME_CHECKED(ClassName, MemberName)` — compile-time property name validation
391
+ - Always store `TWeakPtr<IDetailLayoutBuilder>` if calling `ForceRefreshDetails()` asynchronously
392
+ - For `SNew` lambdas that reference `DetailBuilder`, capture by `TWeakPtr` to avoid dangling references
393
+ - Use `IDetailCategoryBuilder::AddProperty` for standard rows, `AddCustomRow` for fully custom Slate rows
394
+ - `bShowChildren = true` in `CustomWidget()` to show child properties while overriding the parent row's display
395
+ - Call `PropertyModule.NotifyCustomizationModuleChanged()` after registrations so any open detail panels refresh