Skip to content

Commit 851a1f1

Browse files
feat: Implement color code rendering for player names and chat messages
- Added PlayerNameTagHelper to render player names with color codes. - Introduced CodColorHelper for processing color codes in player names. - Updated various views to utilize the new player name rendering component. - Enhanced JavaScript files to support color code rendering in chat logs and player lists. - Created SCSS styles for color codes to ensure proper display.
1 parent cfa4412 commit 851a1f1

29 files changed

Lines changed: 249 additions & 51 deletions
Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,72 @@
1+
using System.Text;
2+
using System.Text.RegularExpressions;
3+
using System.Web;
14
using Microsoft.AspNetCore.Razor.TagHelpers;
25

36
namespace XtremeIdiots.Portal.Web.Helpers;
47

58
[HtmlTargetElement("player-name")]
6-
public class PlayerNameTagHelper : TagHelper
9+
public partial class PlayerNameTagHelper : TagHelper
710
{
811
[HtmlAttributeName("value")] public string? Value { get; set; }
912

1013
public override void Process(TagHelperContext context, TagHelperOutput output)
1114
{
1215
output.TagName = "span";
1316
output.TagMode = TagMode.StartTagAndEndTag;
17+
output.Attributes.SetAttribute("class", "cod-colored");
18+
1419
if (string.IsNullOrWhiteSpace(Value))
1520
{
1621
output.Content.SetContent(string.Empty);
1722
return;
1823
}
1924

20-
var cleaned = Value;
21-
string[] toRemove = ["^1", "^2", "^3", "^4", "^5", "^6", "^7", "^8", "^9"];
22-
foreach (var code in toRemove)
25+
output.Content.SetHtmlContent(CodColorHelper.RenderColorCodes(Value));
26+
}
27+
}
28+
29+
internal static partial class CodColorHelper
30+
{
31+
[GeneratedRegex(@"\^([0-9])")]
32+
private static partial Regex colorCodeRegex();
33+
34+
public static string RenderColorCodes(string input)
35+
{
36+
var matches = colorCodeRegex().Matches(input);
37+
if (matches.Count == 0)
38+
return HttpUtility.HtmlEncode(input);
39+
40+
var result = new StringBuilder();
41+
var lastIndex = 0;
42+
int? currentColor = null;
43+
44+
foreach (var match in matches.Cast<Match>())
45+
{
46+
var textBefore = input[lastIndex..match.Index];
47+
if (textBefore.Length > 0)
48+
{
49+
var encoded = HttpUtility.HtmlEncode(textBefore);
50+
if (currentColor.HasValue)
51+
result.Append($"<span class=\"cod-color-{currentColor.Value}\">{encoded}</span>");
52+
else
53+
result.Append(encoded);
54+
}
55+
56+
currentColor = int.Parse(match.Groups[1].Value);
57+
lastIndex = match.Index + match.Length;
58+
}
59+
60+
var remaining = input[lastIndex..];
61+
if (remaining.Length > 0)
2362
{
24-
cleaned = cleaned.Replace(code, string.Empty);
63+
var encoded = HttpUtility.HtmlEncode(remaining);
64+
if (currentColor.HasValue)
65+
result.Append($"<span class=\"cod-color-{currentColor.Value}\">{encoded}</span>");
66+
else
67+
result.Append(encoded);
2568
}
2669

27-
output.Content.SetContent(cleaned);
70+
return result.ToString();
2871
}
2972
}

src/XtremeIdiots.Portal.Web/Helpers/ServerTagHelpers.cs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,9 @@ public class ServerNameTagHelper : TagHelper
3535
public override void Process(TagHelperContext context, TagHelperOutput output)
3636
{
3737
output.TagName = "span";
38+
output.Attributes.SetAttribute("class", "cod-colored");
3839
var value = string.IsNullOrWhiteSpace(LiveTitle) ? Title ?? string.Empty : LiveTitle;
39-
string[] toRemove = ["^1", "^2", "^3", "^4", "^5", "^6", "^7", "^8", "^9"];
40-
foreach (var code in toRemove)
41-
{
42-
value = value?.Replace(code, string.Empty);
43-
}
44-
45-
output.Content.SetContent(value ?? string.Empty);
40+
output.Content.SetHtmlContent(CodColorHelper.RenderColorCodes(value ?? string.Empty));
4641
}
4742
}
4843

src/XtremeIdiots.Portal.Web/Styles/app.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
@use 'components/detail-fields';
3737
@use 'components/empty-state';
3838
@use 'components/page-header';
39+
@use 'components/cod-colors';
3940
@use 'components/sticky-footer';
4041

4142
// 6. Features - Page-specific styles
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// CoD / Quake Color Code Component
2+
// Renders ^0–^9 color codes as colored spans
3+
4+
@use '../tokens/colors' as *;
5+
6+
.cod-colored {
7+
// Container for colored text — ensures inline display and inherits font
8+
display: inline;
9+
font-family: inherit;
10+
}
11+
12+
// Individual color classes
13+
.cod-color-0 { color: var(--cod-color-0); }
14+
.cod-color-1 { color: var(--cod-color-1); }
15+
.cod-color-2 { color: var(--cod-color-2); }
16+
.cod-color-3 { color: var(--cod-color-3); }
17+
.cod-color-4 { color: var(--cod-color-4); }
18+
.cod-color-5 { color: var(--cod-color-5); }
19+
.cod-color-6 { color: var(--cod-color-6); }
20+
.cod-color-7 {
21+
color: var(--cod-color-7);
22+
text-shadow: 0 0 1px rgba(0, 0, 0, 0.4);
23+
}
24+
.cod-color-8 { color: var(--cod-color-8); }
25+
.cod-color-9 { color: var(--cod-color-9); }
26+
27+
// Ensure colored spans within anchors keep their colors, override link default
28+
a .cod-colored span[class^="cod-color-"],
29+
a .cod-colored span[class*=" cod-color-"],
30+
a span[class^="cod-color-"],
31+
a span[class*=" cod-color-"] {
32+
color: unset;
33+
}
34+
35+
// Links wrapping colored content should not impose their own color
36+
a:has(> .cod-colored),
37+
a:has(> span[class^="cod-color-"]) {
38+
color: unset;
39+
text-decoration: none;
40+
41+
&:hover {
42+
text-decoration: underline;
43+
}
44+
}

src/XtremeIdiots.Portal.Web/Styles/tokens/_colors.scss

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,18 @@ $info: $info-color !default;
4242
$warning: $warning-color !default;
4343
$danger: $danger-color !default;
4444

45+
// Call of Duty / Quake color codes (^0 through ^9)
46+
$cod-color-0: #333333 !default; // Black (dark grey for visibility)
47+
$cod-color-1: #FF0000 !default; // Red
48+
$cod-color-2: #00FF00 !default; // Green
49+
$cod-color-3: #FFFF00 !default; // Yellow
50+
$cod-color-4: #0000FF !default; // Dark Blue
51+
$cod-color-5: #00FFFF !default; // Light Blue
52+
$cod-color-6: #FF00FF !default; // Purple
53+
$cod-color-7: #FFFFFF !default; // White
54+
$cod-color-8: #FF8C00 !default; // Orange (multicolor default)
55+
$cod-color-9: #808080 !default; // Grey
56+
4557
// CSS Custom Properties (for runtime theming)
4658
:root {
4759
--xi-primary: #{$xi-primary};
@@ -51,4 +63,16 @@ $danger: $danger-color !default;
5163
--xi-accent: #{$xi-accent};
5264
--xi-text: #{$xi-text};
5365
--xi-link: #{$xi-link};
66+
67+
// CoD color code tokens
68+
--cod-color-0: #{$cod-color-0};
69+
--cod-color-1: #{$cod-color-1};
70+
--cod-color-2: #{$cod-color-2};
71+
--cod-color-3: #{$cod-color-3};
72+
--cod-color-4: #{$cod-color-4};
73+
--cod-color-5: #{$cod-color-5};
74+
--cod-color-6: #{$cod-color-6};
75+
--cod-color-7: #{$cod-color-7};
76+
--cod-color-8: #{$cod-color-8};
77+
--cod-color-9: #{$cod-color-9};
5478
}

src/XtremeIdiots.Portal.Web/Views/AdminActions/Claim.cshtml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
@Html.DisplayNameFor(model => model.Player.Username)
4545
</dt>
4646
<dd class="col-sm-10">
47-
@Html.DisplayFor(model => model.Player.Username)
47+
<player-name value="@Model.Player?.Username"></player-name>
4848
</dd>
4949
<dt class="col-sm-2">
5050
@Html.DisplayNameFor(model => model.Text)

src/XtremeIdiots.Portal.Web/Views/AdminActions/Delete.cshtml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
</div>
3232
<div class="detail-field col-sm-6">
3333
<dt class="detail-label">@Html.DisplayNameFor(model => model.Player.Username)</dt>
34-
<dd class="detail-value">@Html.DisplayFor(model => model.Player.Username)</dd>
34+
<dd class="detail-value"><player-name value="@Model.Player?.Username"></player-name></dd>
3535
</div>
3636
<div class="detail-field col-sm-12">
3737
<dt class="detail-label">@Html.DisplayNameFor(model => model.Text)</dt>

src/XtremeIdiots.Portal.Web/Views/AdminActions/Lift.cshtml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
@Html.DisplayNameFor(model => model.Player.Username)
4646
</dt>
4747
<dd class="col-sm-10">
48-
@Html.DisplayFor(model => model.Player.Username)
48+
<player-name value="@Model.Player?.Username"></player-name>
4949
</dd>
5050
<dt class="col-sm-2">
5151
@Html.DisplayNameFor(model => model.Text)

src/XtremeIdiots.Portal.Web/Views/AdminActions/_MyAdminActionDetailsPanel.cshtml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
<div class="flex-grow-1 min-w-0">
3636
<h5 class="mb-1 text-truncate">@adminAction.Type on @if (player is not null){
3737
<a asp-controller="Players"
38-
asp-action="Details" asp-route-id="@adminAction.PlayerId">@player.Username</a>
38+
asp-action="Details" asp-route-id="@adminAction.PlayerId"><player-name value="@player.Username"></player-name></a>
3939
} else
4040
{
4141

@@ -64,7 +64,7 @@
6464
<div class="col-sm-6 col-md-5">
6565
<div class="border rounded p-2 h-100">
6666
<div class="fw-semibold mb-1 small text-uppercase text-muted">Player</div>
67-
<div class="mb-1"><strong>@player?.Username</strong></div>
67+
<div class="mb-1"><strong><player-name value="@player?.Username"></player-name></strong></div>
6868
<div class="small text-muted">Guid: @player?.Guid</div>
6969
@* Country information not available on PlayerDto (CountryCode removed) *@
7070
</div>

src/XtremeIdiots.Portal.Web/Views/External/LatestAdminActions.cshtml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
@Html.Raw(text)
4141

4242
<a target="_blank"
43-
href="@((Configuration["XtremeIdiots:PortalBaseUrl"] ?? "https://portal.xtremeidiots.com").TrimEnd('/'))/Players/Details/@item.PlayerId">@item.Player?.Username</a>
43+
href="@((Configuration["XtremeIdiots:PortalBaseUrl"] ?? "https://portal.xtremeidiots.com").TrimEnd('/'))/Players/Details/@item.PlayerId"><player-name value="@item.Player?.Username"></player-name></a>
4444
</p>
4545
</div>
4646

0 commit comments

Comments
 (0)