1- using System . Linq ;
1+ using System . Linq ;
22using System ;
33using System . Text . RegularExpressions ;
44using 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