Skip to content

Commit 9fef5f2

Browse files
authored
fix: settings page styling (#438)
Add overrides for fallback CSS variables that were forgotten about. Handle CSS `var(--something)` and `var(--something, fallback)` by replacing them with the values defined in the `:root`. Remove the custom X button and re-enable the in-built window X button.
1 parent 9115e13 commit 9fef5f2

5 files changed

Lines changed: 418 additions & 44 deletions

File tree

Snyk.VisualStudio.Extension.2022/Properties/AssemblyInfo.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using Microsoft.VisualStudio.Shell;
1+
using Microsoft.VisualStudio.Shell;
22
using System.Reflection;
33
using System.Runtime.CompilerServices;
44
using System.Runtime.InteropServices;
@@ -32,3 +32,6 @@
3232
// [assembly: AssemblyVersion("1.0.*")]
3333
[assembly: AssemblyVersion("1.0.0.0")]
3434
[assembly: AssemblyFileVersion("1.0.0.0")]
35+
36+
// Allow unit tests to access internal methods
37+
[assembly: InternalsVisibleTo("Snyk.VisualStudio.Extension.Tests")]

Snyk.VisualStudio.Extension.2022/Settings/HtmlSettingsWindow.xaml

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
xmlns:imaging="clr-namespace:Microsoft.VisualStudio.Imaging;assembly=Microsoft.VisualStudio.Imaging"
1111
mc:Ignorable="d"
1212
WindowStartupLocation="CenterScreen"
13-
IsCloseButtonEnabled="False"
13+
IsCloseButtonEnabled="True"
1414
HasHelpButton="False"
1515
MinHeight="750" Height="850"
1616
MinWidth="600" Width="950"
@@ -26,30 +26,6 @@
2626
CornerRadius="0" />
2727
</WindowChrome.WindowChrome> -->
2828
<DockPanel Margin="10">
29-
<!-- Top: Draggable Bar with Close Button -->
30-
<Grid DockPanel.Dock="Top" Margin="0,0,0,10">
31-
<Grid.ColumnDefinitions>
32-
<ColumnDefinition Width="*" />
33-
<ColumnDefinition Width="Auto" />
34-
</Grid.ColumnDefinitions>
35-
36-
<!-- Draggable area (empty, just for dragging) -->
37-
<Border Grid.Column="0"
38-
Background="Transparent"
39-
MouseLeftButtonDown="TitleBar_MouseLeftButtonDown"
40-
Height="35" />
41-
42-
<!-- Close button -->
43-
<Button Grid.Column="1"
44-
Click="CloseButton_OnClick"
45-
MinWidth="35"
46-
Width="35"
47-
Height="35"
48-
Padding="0">
49-
<imaging:CrispImage Moniker="{x:Static catalog:KnownMonikers.Close}" />
50-
</Button>
51-
</Grid>
52-
5329
<!-- Bottom: OK and Cancel Buttons -->
5430
<StackPanel HorizontalAlignment="Right" DockPanel.Dock="Bottom" Orientation="Horizontal" Margin="0,10,0,0">
5531
<Button x:Name="OkButton" Margin="5, 5" Content="OK" Click="OkButton_OnClick"

Snyk.VisualStudio.Extension.2022/Settings/HtmlSettingsWindow.xaml.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -251,12 +251,6 @@ private void CancelButton_OnClick(object sender, RoutedEventArgs e)
251251
Close();
252252
}
253253

254-
private void CloseButton_OnClick(object sender, RoutedEventArgs e)
255-
{
256-
DialogResult = false;
257-
Close();
258-
}
259-
260254
private void TitleBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
261255
{
262256
if (e.ClickCount == 1)

Snyk.VisualStudio.Extension.2022/UI/Html/BaseHtmlProvider.cs

Lines changed: 90 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Linq;
1+
using System.Linq;
22
using System;
33
using System.Text.RegularExpressions;
44
using Microsoft.VisualStudio.PlatformUI;
@@ -92,8 +92,10 @@ public virtual string ReplaceCssVariables(string html)
9292
var scrollbarThumb = VSColorTheme.GetThemedColor(EnvironmentColors.ScrollBarThumbBackgroundColorKey).ToHex();
9393
var scrollbarThumbHover = VSColorTheme.GetThemedColor(EnvironmentColors.ScrollBarThumbMouseOverBackgroundColorKey).ToHex();
9494

95-
// Build variable map for regex-based replacement (like IntelliJ plugin)
96-
var varMap = new System.Collections.Generic.Dictionary<string, string>
95+
var varMap = ExtractRootCssVariables(html);
96+
97+
// IDE custom theme variables. These take priority over the extracted variables.
98+
var ideVarOverridesMap = new System.Collections.Generic.Dictionary<string, string>
9799
{
98100
// VS Code style variables (from LS HTML)
99101
{ "vscode-font-family", "'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" },
@@ -128,7 +130,7 @@ public virtual string ReplaceCssVariables(string html)
128130
{ "button-secondary-background", secondaryButtonBackground },
129131
{ "button-secondary-foreground", secondaryButtonForeground },
130132
{ "button-secondary-hover-background", secondaryButtonHoverBackground },
131-
// Legacy variables (for fallback HTML)
133+
// Legacy variables (for fallback HTML theme colors)
132134
{ "default-font", "'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" },
133135
{ "text-color", textColor },
134136
{ "background-color", backgroundColor },
@@ -137,20 +139,21 @@ public virtual string ReplaceCssVariables(string html)
137139
{ "horizontal-border-color", borderColor },
138140
{ "code-background-color", inputBackground },
139141
{ "input-border", inputBorder },
142+
{ "input-background-color", inputBackground },
143+
{ "section-background-color", inactiveSelectionBackground },
144+
{ "focus-color", linkColor },
140145
{ "main-font-size", "15px" },
141146
{ "ide-background-color", backgroundColor },
142147
{ "dimmed-text-color", disabledForeground }
143148
};
144149

145-
// Regex pattern to match var(--varName) or var(--varName, fallback)
146-
// Matches var() usage in CSS
147-
var cssVarPattern = new Regex(@"var\(--([a-zA-Z0-9_-]+)(?:,\s*[^)]+)?\)");
148-
149-
html = cssVarPattern.Replace(html, match =>
150+
// Merge IDE variables into varMap (IDE values override :root values)
151+
foreach (var ideVarOverride in ideVarOverridesMap)
150152
{
151-
var varName = match.Groups[1].Value;
152-
return varMap.ContainsKey(varName) ? varMap[varName] : match.Value;
153-
});
153+
varMap[ideVarOverride.Key] = ideVarOverride.Value;
154+
}
155+
156+
html = ReplaceCssVarUsages(html, varMap, maxIterations: 10);
154157

155158
// Template placeholder replacements (for embedded/fallback HTML)
156159
html = html.Replace("{{TEXT_COLOR}}", textColor);
@@ -175,6 +178,81 @@ public virtual string ReplaceCssVariables(string html)
175178
return html;
176179
}
177180

181+
/// <summary>
182+
/// Extracts CSS variable definitions from :root blocks in HTML.
183+
/// This is needed because IE11 doesn't support CSS custom properties natively.
184+
/// </summary>
185+
internal System.Collections.Generic.Dictionary<string, string> ExtractRootCssVariables(string html)
186+
{
187+
var variables = new System.Collections.Generic.Dictionary<string, string>();
188+
189+
// Match :root { ... } blocks (handles multi-line)
190+
var rootPattern = new Regex(@"^\s*:root\s*\{(?<content>[^}]+)\}\s*$", RegexOptions.Multiline);
191+
// Strip block comments /* ... */ (Singleline makes . match newlines)
192+
var blockCommentPattern = new Regex(@"/\*.*?\*/", RegexOptions.Singleline);
193+
// Match variable definitions: --name: value;
194+
var varDefPattern = new Regex(@"^\s*--(?<name>[a-zA-Z0-9_-]+)\s*:\s*(?<value>[^;]+);", RegexOptions.Multiline);
195+
196+
foreach (Match rootMatch in rootPattern.Matches(html))
197+
{
198+
var rootBlock = rootMatch.Groups["content"].Value;
199+
// Remove block comments before extracting variables
200+
rootBlock = blockCommentPattern.Replace(rootBlock, "");
201+
202+
foreach (Match varMatch in varDefPattern.Matches(rootBlock))
203+
{
204+
var name = varMatch.Groups["name"].Value;
205+
var value = varMatch.Groups["value"].Value.Trim();
206+
// Last definition wins (CSS cascade behavior)
207+
variables[name] = value;
208+
}
209+
}
210+
211+
return variables;
212+
}
213+
214+
/// <summary>
215+
/// Replaces var(--name) and var(--name, fallback) usages with actual values.
216+
/// Loops until content stabilizes or max iterations reached (handles nested variables).
217+
/// </summary>
218+
internal string ReplaceCssVarUsages(string html, System.Collections.Generic.Dictionary<string, string> varMap, int maxIterations)
219+
{
220+
var cssVarPattern = new Regex(@"var\(--(?<name>[a-zA-Z0-9_-]+)(?:,\s*(?<fallback>[^)]+))?\)");
221+
222+
for (int i = 0; i < maxIterations; i++)
223+
{
224+
var previousHtml = html;
225+
226+
html = cssVarPattern.Replace(html, match =>
227+
{
228+
var varName = match.Groups["name"].Value;
229+
var fallbackGroup = match.Groups["fallback"];
230+
231+
// Priority: varMap value > CSS fallback > leave unchanged
232+
if (varMap.ContainsKey(varName))
233+
{
234+
return varMap[varName];
235+
}
236+
else if (fallbackGroup.Success)
237+
{
238+
return fallbackGroup.Value.Trim();
239+
}
240+
else
241+
{
242+
return match.Value;
243+
}
244+
});
245+
246+
// Stop if content hasn't changed (no more replacements possible)
247+
if (html == previousHtml)
248+
{
249+
break;
250+
}
251+
}
252+
253+
return html;
254+
}
255+
178256
private string ColorToRgba(string hexColor, double alpha)
179257
{
180258
hexColor = hexColor.TrimStart('#');

0 commit comments

Comments
 (0)