From 335f13c22a89da64eca9c0b69f5e415456d0b05c Mon Sep 17 00:00:00 2001 From: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com> Date: Sat, 25 Jul 2026 18:35:19 +0200 Subject: [PATCH] Add ability to include "experimental" icon for labeled PRs (#39686) * Initial commit * Include correct license * Review changes * And some stuff I missed * Make entries have a separate control * Remove readonly --- Content.Client/Changelog/ChangelogEntry.xaml | 8 +++ .../Changelog/ChangelogEntry.xaml.cs | 66 ++++++++++++++++++ Content.Client/Changelog/ChangelogManager.cs | 12 +++- Content.Client/Changelog/ChangelogTab.xaml.cs | 46 ++++-------- .../Interface/Changelog/attributions.yml | 4 ++ .../Interface/Changelog/test-tube.svg | 18 +++++ .../Changelog/test-tube.svg.192dpi.png | Bin 0 -> 396 bytes .../Changelog/test-tube.svg.192dpi.png.yml | 2 + Tools/actions_changelog_rss.py | 5 ++ Tools/actions_changelogs_since_last_run.py | 6 ++ 10 files changed, 130 insertions(+), 37 deletions(-) create mode 100644 Content.Client/Changelog/ChangelogEntry.xaml create mode 100644 Content.Client/Changelog/ChangelogEntry.xaml.cs create mode 100644 Resources/Textures/Interface/Changelog/attributions.yml create mode 100644 Resources/Textures/Interface/Changelog/test-tube.svg create mode 100644 Resources/Textures/Interface/Changelog/test-tube.svg.192dpi.png create mode 100644 Resources/Textures/Interface/Changelog/test-tube.svg.192dpi.png.yml diff --git a/Content.Client/Changelog/ChangelogEntry.xaml b/Content.Client/Changelog/ChangelogEntry.xaml new file mode 100644 index 0000000000..3e1b2719d9 --- /dev/null +++ b/Content.Client/Changelog/ChangelogEntry.xaml @@ -0,0 +1,8 @@ + + + + + + diff --git a/Content.Client/Changelog/ChangelogEntry.xaml.cs b/Content.Client/Changelog/ChangelogEntry.xaml.cs new file mode 100644 index 0000000000..67b533a513 --- /dev/null +++ b/Content.Client/Changelog/ChangelogEntry.xaml.cs @@ -0,0 +1,66 @@ +using System.Numerics; +using Content.Client.Resources; +using Robust.Client.AutoGenerated; +using Robust.Client.Graphics; +using Robust.Client.ResourceManagement; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; +using Robust.Shared.Utility; +using static Content.Client.Changelog.ChangelogManager; + +namespace Content.Client.Changelog; + +[GenerateTypedNameReferences] +public sealed partial class ChangelogEntry : Control +{ + [Dependency] private IResourceCache _resourceCache = default!; + + public ChangelogEntry() + { + RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); + } + + public void SetText(FormattedMessage text) + { + Text.SetMessage(text); + } + + public void SetIcons(ChangelogLineType type, bool experimental) + { + var icon = GetIcon(type); + Icon.Texture = icon.Item1; + Icon.ModulateSelfOverride = icon.Item2; + + if (experimental) + { + var expIcon = new TextureRect + { + Texture = _resourceCache.GetTexture( + new ResPath("/Textures/Interface/Changelog/test-tube.svg.192dpi.png")), + TextureScale = new Vector2(0.5f, 0.5f), + VerticalAlignment = VAlignment.Top, + Margin = new Thickness(2, 4, 0, 2), + ModulateSelfOverride = Color.FromHex("#A0d16E"), + }; + + Icon.Parent!.AddChild(expIcon); + expIcon.SetPositionInParent(1); + } + } + + private (Texture, Color) GetIcon(ChangelogLineType type) + { + var (file, color) = type switch + { + ChangelogLineType.Add => ("plus.svg.192dpi.png", "#6ED18D"), + ChangelogLineType.Remove => ("minus.svg.192dpi.png", "#D16E6E"), + ChangelogLineType.Fix => ("bug.svg.192dpi.png", "#D1BA6E"), + ChangelogLineType.Tweak => ("wrench.svg.192dpi.png", "#6E96D1"), + _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) + }; + + return (_resourceCache.GetTexture(new ResPath($"/Textures/Interface/Changelog/{file}")), Color.FromHex(color)); + } +} diff --git a/Content.Client/Changelog/ChangelogManager.cs b/Content.Client/Changelog/ChangelogManager.cs index 1bf99af19d..c233873ee3 100644 --- a/Content.Client/Changelog/ChangelogManager.cs +++ b/Content.Client/Changelog/ChangelogManager.cs @@ -195,17 +195,23 @@ namespace Content.Client.Changelog [DataDefinition] public sealed partial class ChangelogEntry { - [DataField("id")] + [DataField] public int Id { get; private set; } - [DataField("author")] + [DataField] public string Author { get; private set; } = ""; [DataField] public DateTime Time { get; private set; } - [DataField("changes")] + [DataField] public List Changes { get; private set; } = default!; + + /// + /// Labels attached to the related PR, passed on via the SS14.Changelog parser. + /// + [DataField] + public List Labels { get; private set; } = []; } [DataDefinition] diff --git a/Content.Client/Changelog/ChangelogTab.xaml.cs b/Content.Client/Changelog/ChangelogTab.xaml.cs index 24d4f39036..3cbc2a4547 100644 --- a/Content.Client/Changelog/ChangelogTab.xaml.cs +++ b/Content.Client/Changelog/ChangelogTab.xaml.cs @@ -7,7 +7,6 @@ using Robust.Client.ResourceManagement; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; -using Robust.Shared.ContentPack; using Robust.Shared.Utility; using static Content.Client.Changelog.ChangelogManager; using static Robust.Client.UserInterface.Controls.BoxContainer; @@ -22,6 +21,11 @@ public sealed partial class ChangelogTab : Control public bool AdminOnly; + /// + /// Changelog is from a PR marked "Experimental" and should therefore have an additional icon. + /// + private static string _experimentalString = "Intent: Experimental"; + public ChangelogTab() { RobustXamlLoader.Load(this); @@ -134,43 +138,17 @@ public sealed partial class ChangelogTab : Control FormattedMessage.FromMarkupOrThrow(Loc.GetString("changelog-author-changed", ("author", FormattedMessage.EscapeText(author))))); ChangelogBody.AddChild(authorLabel); - foreach (var change in groupedEntry.SelectMany(c => c.Changes)) + foreach (var (labels, changes) in groupedEntry.Select(c => (c.Labels, c.Changes))) { - var text = new RichTextLabel(); - text.SetMessage(FormattedMessage.FromUnformatted(change.Message)); - ChangelogBody.AddChild(new BoxContainer + foreach (var change in changes) { - Orientation = LayoutOrientation.Horizontal, - Margin = new Thickness(14, 1, 10, 2), - Children = - { - GetIcon(change.Type), - text - } - }); + var entry = new ChangelogEntry(); + entry.SetText(FormattedMessage.FromUnformatted(change.Message)); + entry.SetIcons(change.Type, labels.Contains(_experimentalString)); + ChangelogBody.AddChild(entry); + } } } } } - - private TextureRect GetIcon(ChangelogLineType type) - { - var (file, color) = type switch - { - ChangelogLineType.Add => ("plus.svg.192dpi.png", "#6ED18D"), - ChangelogLineType.Remove => ("minus.svg.192dpi.png", "#D16E6E"), - ChangelogLineType.Fix => ("bug.svg.192dpi.png", "#D1BA6E"), - ChangelogLineType.Tweak => ("wrench.svg.192dpi.png", "#6E96D1"), - _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) - }; - - return new TextureRect - { - Texture = _resourceCache.GetTexture(new ResPath($"/Textures/Interface/Changelog/{file}")), - VerticalAlignment = VAlignment.Top, - TextureScale = new Vector2(0.5f, 0.5f), - Margin = new Thickness(2, 4, 6, 2), - ModulateSelfOverride = Color.FromHex(color) - }; - } } diff --git a/Resources/Textures/Interface/Changelog/attributions.yml b/Resources/Textures/Interface/Changelog/attributions.yml new file mode 100644 index 0000000000..da5a3dcee7 --- /dev/null +++ b/Resources/Textures/Interface/Changelog/attributions.yml @@ -0,0 +1,4 @@ +- files: ["test-tube.svg"] + license: "CC-BY-4.0" + copyright: "Solar Icons" + source: "https://www.svgrepo.com/svg/526351/test-tube-minimalistic" diff --git a/Resources/Textures/Interface/Changelog/test-tube.svg b/Resources/Textures/Interface/Changelog/test-tube.svg new file mode 100644 index 0000000000..54d968351a --- /dev/null +++ b/Resources/Textures/Interface/Changelog/test-tube.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + diff --git a/Resources/Textures/Interface/Changelog/test-tube.svg.192dpi.png b/Resources/Textures/Interface/Changelog/test-tube.svg.192dpi.png new file mode 100644 index 0000000000000000000000000000000000000000..dd38ab7facc6e2378c76339f08472628a9336a3c GIT binary patch literal 396 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10!Z^Su#1%;YCj|i2Z`VFD z0Z8$e1o;IsBpjH~u)yGhz=a1J3LFBWSl+z@N--vRySp%Su*!M>Ih+L^k;Oo31wokc zl-g%|px|sz7sn8b({m@@7CNNB<1#%&acR)M|M{%PR!*5 str: for entry in sorted(group, key=lambda x: x["time"]): for change in entry["changes"]: emoji = TYPES_TO_EMOJI.get(change["type"], "") + if EXPERIMENTAL_LABEL in entry["labels"] + emoji = f"{emoji}{EXPERIMENTAL_EMOJI}" msg = change["message"] desc.write(f"
  • {emoji} {html.escape(msg)}
  • ") diff --git a/Tools/actions_changelogs_since_last_run.py b/Tools/actions_changelogs_since_last_run.py index 876fb65aa5..b80463fd21 100755 --- a/Tools/actions_changelogs_since_last_run.py +++ b/Tools/actions_changelogs_since_last_run.py @@ -31,6 +31,9 @@ CHANGELOG_FILE = "Resources/Changelog/Changelog.yml" TYPES_TO_EMOJI = {"Fix": "๐Ÿ›", "Add": "๐Ÿ†•", "Remove": "โŒ", "Tweak": "โš’๏ธ"} +EXPERIMENTAL_LABEL = "Intent: Experimental" +EXPERIMENTAL_EMOJI = "๐Ÿงช" + ChangelogEntry = dict[str, Any] @@ -239,6 +242,9 @@ def changelog_entries_to_message_lines(entries: Iterable[ChangelogEntry]) -> lis emoji = TYPES_TO_EMOJI.get(change["type"], "โ“") message = change["message"] + if EXPERIMENTAL_LABEL in entry["labels"] + emoji = f"{emoji}{EXPERIMENTAL_EMOJI}" + message_lines.append(create_change_line(emoji, message, url)) return message_lines