feat(ui): port retail component book rows
Instantiate gmSpellComponentUI category and component entries from LayoutDesc 0x21000033 instead of synthesizing catalog rows. Preserve localized category titles, authored textures and geometry, live icon/count/desired bindings, selected-object synchronization, and retail's inclusive 0..5000 desired-level contract. Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
parent
15bb61e05d
commit
a1175e6aac
13 changed files with 2395 additions and 60 deletions
|
|
@ -101,6 +101,12 @@ public static class FixtureLoader
|
|||
public static ElementInfo LoadSpellbookInfos()
|
||||
=> LoadInfos("spellbook_21000034.json");
|
||||
|
||||
public static ElementInfo LoadComponentCategoryTemplateInfos()
|
||||
=> LoadInfos("component_category_21000033_10000466.json");
|
||||
|
||||
public static ElementInfo LoadComponentRowTemplateInfos()
|
||||
=> LoadInfos("component_row_21000033_10000467.json");
|
||||
|
||||
public static ImportedLayout LoadPowerbar()
|
||||
=> LayoutImporter.Build(LoadPowerbarInfos(), _ => (0u, 0, 0), null);
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,22 @@ public sealed class RetailLayoutFixtureGenerator
|
|||
"Asheron's Call");
|
||||
using var dats = new DatCollection(datDir, DatAccessType.Read);
|
||||
|
||||
var localStrings = new DatStringResolver(dats);
|
||||
foreach ((string key, string expected) in new[]
|
||||
{
|
||||
("ID_SpellComp_Category_Scarabs", "SCARABS"),
|
||||
("ID_SpellComp_Category_Herbs", "HERBS"),
|
||||
("ID_SpellComp_Category_Gems", "POWDERED GEMS"),
|
||||
("ID_SpellComp_Category_Alchemical", "ALCHEMICAL SUBSTANCES"),
|
||||
("ID_SpellComp_Category_Talismans", "TALISMANS"),
|
||||
("ID_SpellComp_Category_Tapers", "TAPERS"),
|
||||
("ID_SpellComp_Category_Peas", "PEAS"),
|
||||
})
|
||||
{
|
||||
Assert.Equal(expected, localStrings.Resolve(
|
||||
0x23000001u, DatStringResolver.ComputeHash(key)));
|
||||
}
|
||||
|
||||
uint masterDid = (uint)dats.Portal.Header.MasterMapId;
|
||||
Assert.True(dats.Portal.TryGet<EnumIDMap>(masterDid, out var master));
|
||||
Assert.True(master!.ClientEnumToID.TryGetValue(5u, out uint uiMapDid));
|
||||
|
|
@ -102,6 +118,26 @@ public sealed class RetailLayoutFixtureGenerator
|
|||
File.WriteAllText(Path.Combine(FixtureDirectory(), fileName), effectsJson);
|
||||
}
|
||||
|
||||
foreach ((uint rootId, string fileName) in new[]
|
||||
{
|
||||
(0x10000466u,
|
||||
"component_category_21000033_10000466.json"),
|
||||
(0x10000467u,
|
||||
"component_row_21000033_10000467.json"),
|
||||
})
|
||||
{
|
||||
ElementInfo? componentTemplate = LayoutImporter.ImportInfos(
|
||||
dats, 0x21000033u, rootId);
|
||||
Assert.NotNull(componentTemplate);
|
||||
var componentJson = JsonSerializer.Serialize(componentTemplate,
|
||||
new JsonSerializerOptions
|
||||
{
|
||||
IncludeFields = true,
|
||||
WriteIndented = true,
|
||||
});
|
||||
File.WriteAllText(Path.Combine(FixtureDirectory(), fileName), componentJson);
|
||||
}
|
||||
|
||||
foreach (var (layoutId, fileName) in Layouts)
|
||||
{
|
||||
var info = LayoutImporter.ImportInfos(dats, layoutId);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using AcDream.App.Spells;
|
|||
using AcDream.App.UI;
|
||||
using AcDream.App.UI.Layout;
|
||||
using AcDream.Core.Items;
|
||||
using AcDream.Core.Selection;
|
||||
using AcDream.Core.Spells;
|
||||
using System.Numerics;
|
||||
|
||||
|
|
@ -238,6 +239,130 @@ public sealed class SpellbookWindowControllerTests
|
|||
Assert.Empty(VisibleSpellIds(list));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Components_UseAuthoredCategoryAndItemTemplates_WithRetailCountsAndSelection()
|
||||
{
|
||||
ImportedLayout layout = FixtureLoader.LoadSpellbook();
|
||||
var objects = new ClientObjectTable();
|
||||
objects.AddOrUpdate(new ClientObject
|
||||
{
|
||||
ObjectId = 10u,
|
||||
WeenieClassId = 100u,
|
||||
Name = "Copper Scarab",
|
||||
Type = ItemType.SpellComponents,
|
||||
IconId = 0x06000100u,
|
||||
StackSize = 2,
|
||||
ContainerId = 1u,
|
||||
IsComponentPack = true,
|
||||
});
|
||||
objects.AddOrUpdate(new ClientObject
|
||||
{
|
||||
ObjectId = 11u,
|
||||
WeenieClassId = 101u,
|
||||
Name = "Diamond Scarab",
|
||||
Type = ItemType.SpellComponents,
|
||||
IconId = 0x06000101u,
|
||||
StackSize = 1,
|
||||
ContainerId = 1u,
|
||||
IsComponentPack = true,
|
||||
});
|
||||
objects.AddOrUpdate(new ClientObject
|
||||
{
|
||||
ObjectId = 12u,
|
||||
WeenieClassId = 200u,
|
||||
Name = "Amaranth",
|
||||
Type = ItemType.SpellComponents,
|
||||
IconId = 0x06000200u,
|
||||
StackSize = 3,
|
||||
ContainerId = 1u,
|
||||
IsComponentPack = true,
|
||||
});
|
||||
var components = new Dictionary<uint, SpellComponentDescriptor>
|
||||
{
|
||||
[100u] = new(100u, "Copper Scarab", 0u, 0x06000100u),
|
||||
[101u] = new(101u, "Diamond Scarab", 0u, 0x06000101u),
|
||||
[200u] = new(200u, "Amaranth", 1u, 0x06000200u),
|
||||
};
|
||||
Spellbook book = CreateSpellbook();
|
||||
book.SetDesiredComponent(100u, 7u);
|
||||
var selected = new List<uint>();
|
||||
var desired = new List<(uint Component, uint Amount)>();
|
||||
var selection = new SelectionState();
|
||||
|
||||
using SpellbookWindowController controller = Bind(
|
||||
layout,
|
||||
book,
|
||||
objects: objects,
|
||||
components: components,
|
||||
selection: selection,
|
||||
selectObject: objectId =>
|
||||
{
|
||||
selected.Add(objectId);
|
||||
selection.Select(objectId, SelectionChangeSource.Inventory);
|
||||
},
|
||||
setDesiredComponent: (component, amount) => desired.Add((component, amount)),
|
||||
resolveComponentIcon: icon => icon + 0x1000u)!;
|
||||
controller.ShowPage(SpellbookWindowPage.Components);
|
||||
|
||||
UiItemList list = Assert.IsType<UiItemList>(
|
||||
layout.FindElement(SpellbookWindowController.ComponentListId)?.Children.Single());
|
||||
Assert.Equal(5, list.GetNumUIItems());
|
||||
Assert.Same(
|
||||
list.Scroll,
|
||||
Assert.IsType<UiScrollbar>(
|
||||
layout.FindElement(SpellbookWindowController.ComponentScrollbarId)).Model);
|
||||
|
||||
UiTemplateListSlot scarabs = Assert.IsType<UiTemplateListSlot>(list.GetItem(0));
|
||||
UiText scarabTitle = Assert.IsType<UiText>(scarabs.Content.Root);
|
||||
Assert.Equal("SCARABS", Assert.Single(scarabTitle.LinesProvider()).Text);
|
||||
Assert.Equal(0x06001392u, scarabTitle.BackgroundSprite);
|
||||
|
||||
UiTemplateListSlot copper = Assert.IsType<UiTemplateListSlot>(list.GetItem(1));
|
||||
UiDatElement copperRoot = Assert.IsType<UiDatElement>(copper.Content.Root);
|
||||
Assert.Equal((0x06005E21u, 1), copperRoot.ActiveMedia());
|
||||
Assert.Equal("Copper Scarab", Assert.Single(
|
||||
Assert.IsType<UiText>(copper.Content.FindElement(
|
||||
ComponentBookTemplateFactory.NameId)).LinesProvider()).Text);
|
||||
Assert.Equal("2", Assert.Single(
|
||||
Assert.IsType<UiText>(copper.Content.FindElement(
|
||||
ComponentBookTemplateFactory.OwnedCountId)).LinesProvider()).Text);
|
||||
UiField desiredField = Assert.IsType<UiField>(copper.Content.FindElement(
|
||||
ComponentBookTemplateFactory.DesiredCountId));
|
||||
Assert.Equal((224f, 15f, 60f, 15f),
|
||||
(desiredField.Left, desiredField.Top, desiredField.Width, desiredField.Height));
|
||||
Assert.True(desiredField.RightAligned);
|
||||
Assert.Equal("7", desiredField.Text);
|
||||
UiElement iconHost = Assert.IsAssignableFrom<UiElement>(copper.Content.FindElement(
|
||||
ComponentBookTemplateFactory.IconId));
|
||||
Assert.Equal(
|
||||
0x06001100u,
|
||||
Assert.IsType<UiTextureElement>(Assert.Single(iconHost.Children)).Texture);
|
||||
|
||||
copper.OnEvent(new UiEvent(0u, copper, UiEventType.Click));
|
||||
Assert.Equal([10u], selected);
|
||||
Assert.True(copper.Selected);
|
||||
Assert.Equal(UiButtonStateMachine.Highlight, copperRoot.ActiveRetailStateId);
|
||||
Assert.Equal((0x06005E22u, 1), copperRoot.ActiveMedia());
|
||||
|
||||
desiredField.SetText("21");
|
||||
desiredField.OnSubmit!(desiredField.Text);
|
||||
Assert.Equal([(100u, 21u)], desired);
|
||||
desiredField.SetText("5001");
|
||||
desiredField.OnSubmit!(desiredField.Text);
|
||||
Assert.Equal("21", desiredField.Text);
|
||||
Assert.Equal([(100u, 21u)], desired);
|
||||
|
||||
UiTemplateListSlot herbs = Assert.IsType<UiTemplateListSlot>(list.GetItem(3));
|
||||
Assert.Equal("HERBS", Assert.Single(
|
||||
Assert.IsType<UiText>(herbs.Content.Root).LinesProvider()).Text);
|
||||
UiTemplateListSlot amaranth = Assert.IsType<UiTemplateListSlot>(list.GetItem(4));
|
||||
selection.Select(12u, SelectionChangeSource.Radar);
|
||||
Assert.True(amaranth.Selected);
|
||||
Assert.False(copper.Selected);
|
||||
selection.Select(0xDEADBEEFu, SelectionChangeSource.World);
|
||||
Assert.False(amaranth.Selected);
|
||||
}
|
||||
|
||||
private static SpellbookWindowController? Bind(
|
||||
ImportedLayout layout,
|
||||
Spellbook spellbook,
|
||||
|
|
@ -245,23 +370,35 @@ public sealed class SpellbookWindowControllerTests
|
|||
Action<uint>? sendFilter = null,
|
||||
Action<uint>? removeSpell = null,
|
||||
Action<string, Action<bool>>? showConfirmation = null,
|
||||
Action? close = null)
|
||||
Action? close = null,
|
||||
ClientObjectTable? objects = null,
|
||||
IReadOnlyDictionary<uint, SpellComponentDescriptor>? components = null,
|
||||
SelectionState? selection = null,
|
||||
Action<uint>? selectObject = null,
|
||||
Action<uint, uint>? setDesiredComponent = null,
|
||||
Func<uint, uint>? resolveComponentIcon = null)
|
||||
=> SpellbookWindowController.Bind(
|
||||
layout,
|
||||
spellbook,
|
||||
new ClientObjectTable(),
|
||||
objects ?? new ClientObjectTable(),
|
||||
() => 1u,
|
||||
new Dictionary<uint, SpellComponentDescriptor>(),
|
||||
components ?? new Dictionary<uint, SpellComponentDescriptor>(),
|
||||
selection ?? new SelectionState(),
|
||||
spellId => spellId + 1000u,
|
||||
iconId => iconId,
|
||||
resolveComponentIcon ?? (iconId => iconId),
|
||||
spellId => spellId == 103u ? 8 : 1,
|
||||
_ => { },
|
||||
selectObject ?? (_ => { }),
|
||||
addFavorite ?? (_ => { }),
|
||||
sendFilter ?? (_ => { }),
|
||||
removeSpell ?? (_ => { }),
|
||||
showConfirmation ?? ((_, _) => { }),
|
||||
(_, _) => { },
|
||||
setDesiredComponent ?? ((_, _) => { }),
|
||||
close ?? (() => { }),
|
||||
new ComponentBookTemplateFactory(
|
||||
FixtureLoader.LoadComponentCategoryTemplateInfos(),
|
||||
FixtureLoader.LoadComponentRowTemplateInfos(),
|
||||
_ => (0u, 0, 0),
|
||||
defaultFont: null),
|
||||
RowStyle,
|
||||
rowFont: null);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,358 @@
|
|||
{
|
||||
"Id": 268436582,
|
||||
"Type": 12,
|
||||
"X": 0,
|
||||
"Y": 0,
|
||||
"Width": 284,
|
||||
"Height": 32,
|
||||
"OriginalParentWidth": 0,
|
||||
"OriginalParentHeight": 0,
|
||||
"HasOriginalParentSize": false,
|
||||
"Left": 0,
|
||||
"Top": 0,
|
||||
"Right": 0,
|
||||
"Bottom": 0,
|
||||
"ReadOrder": 1,
|
||||
"ZLevel": 0,
|
||||
"States": {
|
||||
"4294967295": {
|
||||
"Id": 4294967295,
|
||||
"Name": "",
|
||||
"PassToChildren": false,
|
||||
"IncorporationFlags": 30,
|
||||
"Image": {
|
||||
"File": 100668306,
|
||||
"DrawMode": 1
|
||||
},
|
||||
"Cursor": null,
|
||||
"Properties": {
|
||||
"Values": {
|
||||
"35": {
|
||||
"Kind": 4,
|
||||
"MasterPropertyId": 35,
|
||||
"UnsignedValue": 0,
|
||||
"IntegerValue": 10,
|
||||
"FloatValue": 0,
|
||||
"BoolValue": false,
|
||||
"StringInfoValue": {
|
||||
"Token": 0,
|
||||
"StringId": 0,
|
||||
"TableId": 0,
|
||||
"Override": 0,
|
||||
"English": 0,
|
||||
"Comment": 0
|
||||
},
|
||||
"ColorValue": {
|
||||
"Blue": 0,
|
||||
"Green": 0,
|
||||
"Red": 0,
|
||||
"Alpha": 0
|
||||
},
|
||||
"VectorValue": {
|
||||
"X": 0,
|
||||
"Y": 0,
|
||||
"Z": 0
|
||||
},
|
||||
"ArrayValue": [],
|
||||
"StructValue": {}
|
||||
},
|
||||
"26": {
|
||||
"Kind": 7,
|
||||
"MasterPropertyId": 26,
|
||||
"UnsignedValue": 0,
|
||||
"IntegerValue": 0,
|
||||
"FloatValue": 0,
|
||||
"BoolValue": false,
|
||||
"StringInfoValue": {
|
||||
"Token": 0,
|
||||
"StringId": 0,
|
||||
"TableId": 0,
|
||||
"Override": 0,
|
||||
"English": 0,
|
||||
"Comment": 0
|
||||
},
|
||||
"ColorValue": {
|
||||
"Blue": 0,
|
||||
"Green": 0,
|
||||
"Red": 0,
|
||||
"Alpha": 0
|
||||
},
|
||||
"VectorValue": {
|
||||
"X": 0,
|
||||
"Y": 0,
|
||||
"Z": 0
|
||||
},
|
||||
"ArrayValue": [
|
||||
{
|
||||
"Kind": 2,
|
||||
"MasterPropertyId": 24,
|
||||
"UnsignedValue": 1073741828,
|
||||
"IntegerValue": 0,
|
||||
"FloatValue": 0,
|
||||
"BoolValue": false,
|
||||
"StringInfoValue": {
|
||||
"Token": 0,
|
||||
"StringId": 0,
|
||||
"TableId": 0,
|
||||
"Override": 0,
|
||||
"English": 0,
|
||||
"Comment": 0
|
||||
},
|
||||
"ColorValue": {
|
||||
"Blue": 0,
|
||||
"Green": 0,
|
||||
"Red": 0,
|
||||
"Alpha": 0
|
||||
},
|
||||
"VectorValue": {
|
||||
"X": 0,
|
||||
"Y": 0,
|
||||
"Z": 0
|
||||
},
|
||||
"ArrayValue": [],
|
||||
"StructValue": {}
|
||||
}
|
||||
],
|
||||
"StructValue": {}
|
||||
},
|
||||
"37": {
|
||||
"Kind": 4,
|
||||
"MasterPropertyId": 37,
|
||||
"UnsignedValue": 0,
|
||||
"IntegerValue": 0,
|
||||
"FloatValue": 0,
|
||||
"BoolValue": false,
|
||||
"StringInfoValue": {
|
||||
"Token": 0,
|
||||
"StringId": 0,
|
||||
"TableId": 0,
|
||||
"Override": 0,
|
||||
"English": 0,
|
||||
"Comment": 0
|
||||
},
|
||||
"ColorValue": {
|
||||
"Blue": 0,
|
||||
"Green": 0,
|
||||
"Red": 0,
|
||||
"Alpha": 0
|
||||
},
|
||||
"VectorValue": {
|
||||
"X": 0,
|
||||
"Y": 0,
|
||||
"Z": 0
|
||||
},
|
||||
"ArrayValue": [],
|
||||
"StructValue": {}
|
||||
},
|
||||
"27": {
|
||||
"Kind": 7,
|
||||
"MasterPropertyId": 27,
|
||||
"UnsignedValue": 0,
|
||||
"IntegerValue": 0,
|
||||
"FloatValue": 0,
|
||||
"BoolValue": false,
|
||||
"StringInfoValue": {
|
||||
"Token": 0,
|
||||
"StringId": 0,
|
||||
"TableId": 0,
|
||||
"Override": 0,
|
||||
"English": 0,
|
||||
"Comment": 0
|
||||
},
|
||||
"ColorValue": {
|
||||
"Blue": 0,
|
||||
"Green": 0,
|
||||
"Red": 0,
|
||||
"Alpha": 0
|
||||
},
|
||||
"VectorValue": {
|
||||
"X": 0,
|
||||
"Y": 0,
|
||||
"Z": 0
|
||||
},
|
||||
"ArrayValue": [
|
||||
{
|
||||
"Kind": 6,
|
||||
"MasterPropertyId": 25,
|
||||
"UnsignedValue": 0,
|
||||
"IntegerValue": 0,
|
||||
"FloatValue": 0,
|
||||
"BoolValue": false,
|
||||
"StringInfoValue": {
|
||||
"Token": 0,
|
||||
"StringId": 0,
|
||||
"TableId": 0,
|
||||
"Override": 0,
|
||||
"English": 0,
|
||||
"Comment": 0
|
||||
},
|
||||
"ColorValue": {
|
||||
"Blue": 255,
|
||||
"Green": 255,
|
||||
"Red": 255,
|
||||
"Alpha": 255
|
||||
},
|
||||
"VectorValue": {
|
||||
"X": 0,
|
||||
"Y": 0,
|
||||
"Z": 0
|
||||
},
|
||||
"ArrayValue": [],
|
||||
"StructValue": {}
|
||||
}
|
||||
],
|
||||
"StructValue": {}
|
||||
},
|
||||
"20": {
|
||||
"Kind": 0,
|
||||
"MasterPropertyId": 20,
|
||||
"UnsignedValue": 1,
|
||||
"IntegerValue": 0,
|
||||
"FloatValue": 0,
|
||||
"BoolValue": false,
|
||||
"StringInfoValue": {
|
||||
"Token": 0,
|
||||
"StringId": 0,
|
||||
"TableId": 0,
|
||||
"Override": 0,
|
||||
"English": 0,
|
||||
"Comment": 0
|
||||
},
|
||||
"ColorValue": {
|
||||
"Blue": 0,
|
||||
"Green": 0,
|
||||
"Red": 0,
|
||||
"Alpha": 0
|
||||
},
|
||||
"VectorValue": {
|
||||
"X": 0,
|
||||
"Y": 0,
|
||||
"Z": 0
|
||||
},
|
||||
"ArrayValue": [],
|
||||
"StructValue": {}
|
||||
},
|
||||
"21": {
|
||||
"Kind": 0,
|
||||
"MasterPropertyId": 21,
|
||||
"UnsignedValue": 1,
|
||||
"IntegerValue": 0,
|
||||
"FloatValue": 0,
|
||||
"BoolValue": false,
|
||||
"StringInfoValue": {
|
||||
"Token": 0,
|
||||
"StringId": 0,
|
||||
"TableId": 0,
|
||||
"Override": 0,
|
||||
"English": 0,
|
||||
"Comment": 0
|
||||
},
|
||||
"ColorValue": {
|
||||
"Blue": 0,
|
||||
"Green": 0,
|
||||
"Red": 0,
|
||||
"Alpha": 0
|
||||
},
|
||||
"VectorValue": {
|
||||
"X": 0,
|
||||
"Y": 0,
|
||||
"Z": 0
|
||||
},
|
||||
"ArrayValue": [],
|
||||
"StructValue": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"13": {
|
||||
"Id": 13,
|
||||
"Name": "Ghosted",
|
||||
"PassToChildren": false,
|
||||
"IncorporationFlags": 0,
|
||||
"Image": null,
|
||||
"Cursor": null,
|
||||
"Properties": {
|
||||
"Values": {
|
||||
"27": {
|
||||
"Kind": 7,
|
||||
"MasterPropertyId": 27,
|
||||
"UnsignedValue": 0,
|
||||
"IntegerValue": 0,
|
||||
"FloatValue": 0,
|
||||
"BoolValue": false,
|
||||
"StringInfoValue": {
|
||||
"Token": 0,
|
||||
"StringId": 0,
|
||||
"TableId": 0,
|
||||
"Override": 0,
|
||||
"English": 0,
|
||||
"Comment": 0
|
||||
},
|
||||
"ColorValue": {
|
||||
"Blue": 0,
|
||||
"Green": 0,
|
||||
"Red": 0,
|
||||
"Alpha": 0
|
||||
},
|
||||
"VectorValue": {
|
||||
"X": 0,
|
||||
"Y": 0,
|
||||
"Z": 0
|
||||
},
|
||||
"ArrayValue": [
|
||||
{
|
||||
"Kind": 6,
|
||||
"MasterPropertyId": 25,
|
||||
"UnsignedValue": 0,
|
||||
"IntegerValue": 0,
|
||||
"FloatValue": 0,
|
||||
"BoolValue": false,
|
||||
"StringInfoValue": {
|
||||
"Token": 0,
|
||||
"StringId": 0,
|
||||
"TableId": 0,
|
||||
"Override": 0,
|
||||
"English": 0,
|
||||
"Comment": 0
|
||||
},
|
||||
"ColorValue": {
|
||||
"Blue": 76,
|
||||
"Green": 76,
|
||||
"Red": 76,
|
||||
"Alpha": 255
|
||||
},
|
||||
"VectorValue": {
|
||||
"X": 0,
|
||||
"Y": 0,
|
||||
"Z": 0
|
||||
},
|
||||
"ArrayValue": [],
|
||||
"StructValue": {}
|
||||
}
|
||||
],
|
||||
"StructValue": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"DefaultStateId": 0,
|
||||
"FontDid": 1073741828,
|
||||
"HJustify": 1,
|
||||
"VJustify": 1,
|
||||
"FontColor": {
|
||||
"X": 1,
|
||||
"Y": 1,
|
||||
"Z": 1,
|
||||
"W": 1
|
||||
},
|
||||
"StateMedia": {
|
||||
"": {
|
||||
"Item1": 100668306,
|
||||
"Item2": 1
|
||||
}
|
||||
},
|
||||
"StateCursors": {},
|
||||
"DefaultStateName": "",
|
||||
"Children": []
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue