Skip to content

Commit 24caac2

Browse files
committed
feat: decouple timeline css colors fro vscode
- Introduced EditorColors interface to encapsulate resolved color values for PixiJS renderers. - Updated TimelineFlameChart to extract editor colors from CSS custom properties. - Modified TimelineView to define new CSS variables for editor theming. - Enhanced FlameChart, MeasureRangeRenderer, MinimapRenderer, and others to accept and utilize resolved color values. - Refactored rendering logic in SearchHighlightRenderer and SelectionHighlightRenderer to use provided highlight colors. - Updated tooltip styles to reference new CSS variables for consistent theming. - Removed direct CSS variable extraction in favor of pre-resolved color values for performance improvements.
1 parent bc1c647 commit 24caac2

19 files changed

Lines changed: 305 additions & 181 deletions

log-viewer/src/features/timeline/components/TimelineFlameChart.ts

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ import { customElement, property, query, state } from 'lit/decorators.js';
1414

1515
import type { ApexLog } from 'apex-log-parser';
1616
import { ApexLogTimeline } from '../optimised/ApexLogTimeline.js';
17-
18-
import type { TimelineOptions } from '../types/flamechart.types.js';
17+
import { parseColorToHex } from '../optimised/rendering/ColorUtils.js';
18+
import type { EditorColors, TimelineOptions } from '../types/flamechart.types.js';
1919
import { TimelineError } from '../types/flamechart.types.js';
2020

2121
import { tooltipStyles } from '../styles/timeline.css.js';
@@ -145,6 +145,7 @@ export class TimelineFlameChart extends LitElement {
145145
const optionsWithTheme = {
146146
...this.options,
147147
themeName: this.themeName,
148+
editorColors: this.extractEditorColors(),
148149
};
149150

150151
this.apexLogTimeline = new ApexLogTimeline();
@@ -166,6 +167,48 @@ export class TimelineFlameChart extends LitElement {
166167
this.apexLogTimeline?.setTimeDisplayMode(mode);
167168
}
168169

170+
// ============================================================================
171+
// COLOR EXTRACTION
172+
// ============================================================================
173+
174+
/**
175+
* Extract resolved editor colors from CSS custom properties (--tl-*).
176+
* These are passed to PixiJS renderers so they don't read CSS directly.
177+
*/
178+
private extractEditorColors(): EditorColors {
179+
const style = getComputedStyle(this);
180+
return {
181+
cursorForeground: parseColorToHex(
182+
style.getPropertyValue('--tl-cursor-foreground').trim() || '#fff',
183+
0xffffff,
184+
),
185+
focusBorder: parseColorToHex(
186+
style.getPropertyValue('--tl-focus-border').trim() || '#007fd4',
187+
0x007fd4,
188+
),
189+
findMatchBackground: parseColorToHex(
190+
style.getPropertyValue('--tl-find-match-background').trim() || '#ff9632',
191+
0xea5c00,
192+
),
193+
widgetBackground: parseColorToHex(
194+
style.getPropertyValue('--tl-widget-background').trim() || '#252526',
195+
0x252526,
196+
),
197+
lineNumberForeground: parseColorToHex(
198+
style.getPropertyValue('--tl-line-number-foreground').trim() || '#808080',
199+
0x808080,
200+
),
201+
selectionBackground: parseColorToHex(
202+
style.getPropertyValue('--tl-selection-background').trim() || 'rgba(38, 79, 120, 0.5)',
203+
0x264f78,
204+
),
205+
selectionHighlightBorder: parseColorToHex(
206+
style.getPropertyValue('--tl-selection-highlight-border').trim() || '#007fd4',
207+
0x007fd4,
208+
),
209+
};
210+
}
211+
169212
// ============================================================================
170213
// CLEANUP
171214
// ============================================================================

log-viewer/src/features/timeline/components/TimelineView.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,39 @@ export class TimelineView extends LitElement {
7272
unsafeCSS(codiconStyles),
7373
css`
7474
:host {
75-
--button-icon-hover-background: var(--vscode-toolbar-hoverBackground);
75+
/* Editor */
76+
--tl-editor-background: var(--vscode-editor-background);
77+
--tl-editor-foreground: var(--vscode-editor-foreground);
78+
--tl-cursor-foreground: var(--vscode-editorCursor-foreground, #fff);
79+
--tl-focus-border: var(--vscode-focusBorder, #007fd4);
80+
--tl-line-number-foreground: var(--vscode-editorLineNumber-foreground, #808080);
81+
82+
/* Find/selection */
83+
--tl-find-match-background: var(--vscode-editor-findMatchBackground, #ff9632);
84+
--tl-selection-background: var(--vscode-editor-selectionBackground, rgba(38, 79, 120, 0.5));
85+
--tl-selection-highlight-border: var(--vscode-editor-selectionHighlightBorder, transparent);
86+
87+
/* Widgets */
88+
--tl-widget-background: var(--vscode-editorWidget-background, #252526);
89+
--tl-widget-border: var(--vscode-editorWidget-border, #454545);
90+
--tl-widget-foreground: var(--vscode-editorWidget-foreground, #cccccc);
91+
92+
/* Text */
93+
--tl-description-foreground: var(--vscode-descriptionForeground, #999);
94+
--tl-font-family: var(--vscode-font-family, sans-serif);
95+
96+
/* Buttons */
97+
--tl-button-secondary-background: var(--vscode-button-secondaryBackground, #3a3d41);
98+
--tl-button-secondary-foreground: var(--vscode-button-secondaryForeground, #cccccc);
99+
--tl-button-secondary-hover-background: var(
100+
--vscode-button-secondaryHoverBackground,
101+
#45494e
102+
);
103+
104+
/* Toolbar */
105+
--tl-toolbar-hover-background: var(--vscode-toolbar-hoverBackground);
106+
107+
--button-icon-hover-background: var(--tl-toolbar-hover-background);
76108
77109
display: flex;
78110
flex-direction: column;

log-viewer/src/features/timeline/optimised/FlameChart.ts

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -295,10 +295,13 @@ export class FlameChart<E extends EventNode = EventNode> {
295295

296296
// Create axis renderer SECOND
297297
if (this.axisContainer && this.uiContainer) {
298+
const editorColors = this.options.editorColors;
298299
const axisConfig = {
299300
height: 30,
300-
lineColor: 0x808080,
301-
textColor: '#808080',
301+
lineColor: editorColors?.lineNumberForeground ?? 0x808080,
302+
textColor: editorColors
303+
? `#${editorColors.lineNumberForeground.toString(16).padStart(6, '0')}`
304+
: '#808080',
302305
fontSize: 11,
303306
minLabelSpacing: 120,
304307
};
@@ -370,7 +373,10 @@ export class FlameChart<E extends EventNode = EventNode> {
370373

371374
// Initialize cursor line renderer (for bidirectional cursor mirroring)
372375
if (this.uiContainer) {
373-
this.cursorLineRenderer = new CursorLineRenderer(this.uiContainer);
376+
this.cursorLineRenderer = new CursorLineRenderer(
377+
this.uiContainer,
378+
this.options.editorColors?.cursorForeground,
379+
);
374380
}
375381

376382
// Initialize minimap orchestrator
@@ -561,6 +567,7 @@ export class FlameChart<E extends EventNode = EventNode> {
561567
this.textLabelRenderer,
562568
this.viewport,
563569
this.mainTimelineYOffset,
570+
this.options.editorColors?.findMatchBackground,
564571
);
565572

566573
// Set stage container for clip-space rendering
@@ -1241,13 +1248,21 @@ export class FlameChart<E extends EventNode = EventNode> {
12411248
});
12421249

12431250
// Initialize the orchestrator
1251+
const minimapEditorColors = this.options.editorColors;
12441252
await this.minimapOrchestrator.init(
12451253
this.minimapDiv,
12461254
displayWidth,
12471255
containerHeight,
12481256
this.index,
12491257
this.rectangleManager,
12501258
this.viewport,
1259+
minimapEditorColors
1260+
? {
1261+
widgetBackground: minimapEditorColors.widgetBackground,
1262+
focusBorder: minimapEditorColors.focusBorder,
1263+
lineNumberForeground: minimapEditorColors.lineNumberForeground,
1264+
}
1265+
: undefined,
12511266
);
12521267

12531268
// Focus container on minimap mousedown for keyboard support
@@ -1428,6 +1443,7 @@ export class FlameChart<E extends EventNode = EventNode> {
14281443
this.index.totalDuration,
14291444
this.index.maxDepth,
14301445
this.mainTimelineYOffset,
1446+
this.options.editorColors?.findMatchBackground,
14311447
);
14321448
}
14331449

@@ -1461,12 +1477,20 @@ export class FlameChart<E extends EventNode = EventNode> {
14611477
});
14621478

14631479
// Initialize the orchestrator
1480+
const editorColors = this.options.editorColors;
14641481
this.measurementOrchestrator.init(
14651482
this.worldContainer,
14661483
this.container,
14671484
this.viewport,
14681485
this.index.totalDuration,
14691486
this.index.maxDepth,
1487+
editorColors
1488+
? {
1489+
selectionBackground: editorColors.selectionBackground,
1490+
selectionHighlightBorder: editorColors.selectionHighlightBorder,
1491+
focusBorder: editorColors.focusBorder,
1492+
}
1493+
: undefined,
14701494
);
14711495
}
14721496

log-viewer/src/features/timeline/optimised/measurement/MeasureRangeRenderer.ts

Lines changed: 32 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import * as PIXI from 'pixi.js';
2020
import { formatDuration, formatTimeRange } from '../../../../core/utility/Util.js';
2121
import type { ViewportState } from '../../types/flamechart.types.js';
22-
import { parseColorToHex } from '../rendering/ColorUtils.js';
2322
import { calculateLabelPosition, createTimelineLabel } from '../rendering/LabelPositioning.js';
2423
import type { MeasurementSnapshot } from './MeasurementState.js';
2524

@@ -70,8 +69,18 @@ export class MeasureRangeRenderer {
7069
* @param pixiContainer - PixiJS container for graphics (worldContainer)
7170
* @param htmlContainer - HTML container for label positioning
7271
* @param onZoomClick - Optional callback when zoom icon is clicked
72+
* @param resolvedColors - Optional pre-resolved colors (selectionBackground, selectionHighlightBorder, focusBorder)
7373
*/
74-
constructor(pixiContainer: PIXI.Container, htmlContainer: HTMLElement, onZoomClick?: () => void) {
74+
constructor(
75+
pixiContainer: PIXI.Container,
76+
htmlContainer: HTMLElement,
77+
onZoomClick?: () => void,
78+
resolvedColors?: {
79+
selectionBackground: number;
80+
selectionHighlightBorder: number;
81+
focusBorder: number;
82+
},
83+
) {
7584
this.onZoomClick = onZoomClick;
7685

7786
// Create graphics for overlay - render above frames but below tooltips
@@ -83,8 +92,15 @@ export class MeasureRangeRenderer {
8392
this.labelElement = this.createLabelElement();
8493
htmlContainer.appendChild(this.labelElement);
8594

86-
// Extract colors from CSS variables
87-
this.colors = this.extractColors();
95+
// Use provided colors or defaults
96+
if (resolvedColors) {
97+
this.colors = {
98+
fillColor: resolvedColors.selectionBackground,
99+
borderColor: resolvedColors.selectionHighlightBorder || resolvedColors.focusBorder,
100+
};
101+
} else {
102+
this.colors = { fillColor: 0x264f78, borderColor: 0x264f78 };
103+
}
88104
}
89105

90106
/**
@@ -141,8 +157,8 @@ export class MeasureRangeRenderer {
141157
padding: 0;
142158
border: none;
143159
border-radius: 4px;
144-
background: var(--vscode-button-secondaryBackground, #3a3d41);
145-
color: var(--vscode-button-secondaryForeground, #cccccc);
160+
background: var(--tl-button-secondary-background, #3a3d41);
161+
color: var(--tl-button-secondary-foreground, #cccccc);
146162
cursor: pointer;
147163
pointer-events: auto;
148164
transition: background 0.1s;
@@ -163,40 +179,26 @@ export class MeasureRangeRenderer {
163179
});
164180

165181
button.addEventListener('mouseenter', () => {
166-
button.style.background = 'var(--vscode-button-secondaryHoverBackground, #45494e)';
182+
button.style.background = 'var(--tl-button-secondary-hover-background, #45494e)';
167183
});
168184

169185
button.addEventListener('mouseleave', () => {
170-
button.style.background = 'var(--vscode-button-secondaryBackground, #3a3d41)';
186+
button.style.background = 'var(--tl-button-secondary-background, #3a3d41)';
171187
});
172188

173189
return button;
174190
}
175191

176192
/**
177-
* Extract measurement colors from CSS variables.
178-
* Uses VS Code selection colors for theme compatibility.
193+
* Update measurement colors (e.g., after theme change).
194+
*
195+
* @param selectionBackground - Fill color (0xRRGGBB)
196+
* @param borderColor - Border color (0xRRGGBB)
179197
*/
180-
private extractColors(): MeasurementColors {
181-
const computedStyle = getComputedStyle(document.documentElement);
182-
183-
// Selection background for fill
184-
const fillStr =
185-
computedStyle.getPropertyValue('--vscode-editor-selectionBackground').trim() ||
186-
'rgba(38, 79, 120, 0.5)';
187-
188-
// Selection highlight border or fallback to a visible color
189-
const borderStr =
190-
computedStyle.getPropertyValue('--vscode-editor-selectionHighlightBorder').trim() ||
191-
computedStyle.getPropertyValue('--vscode-focusBorder').trim() ||
192-
'#007fd4';
193-
194-
// Default blue (0x264f78) for measurement overlay
195-
const defaultBlue = 0x264f78;
196-
197-
return {
198-
fillColor: parseColorToHex(fillStr, defaultBlue),
199-
borderColor: parseColorToHex(borderStr, defaultBlue),
198+
public setColors(selectionBackground: number, borderColor: number): void {
199+
this.colors = {
200+
fillColor: selectionBackground,
201+
borderColor,
200202
};
201203
}
202204

@@ -298,13 +300,6 @@ export class MeasureRangeRenderer {
298300
this.labelElement.style.display = 'none';
299301
}
300302

301-
/**
302-
* Refresh colors from CSS variables (e.g., after theme change).
303-
*/
304-
public refreshColors(): void {
305-
this.colors = this.extractColors();
306-
}
307-
308303
/**
309304
* Destroy renderer and cleanup resources.
310305
*/

log-viewer/src/features/timeline/optimised/minimap/MinimapAxisRenderer.ts

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import { BitmapText, Container, Graphics } from 'pixi.js';
2525

2626
import { formatDuration, TEXT_LABEL_CONSTANTS } from '../../types/flamechart.types.js';
27-
import { parseColorToHex } from '../rendering/ColorUtils.js';
2827
import type { MinimapViewport } from './MinimapViewport.js';
2928

3029
/**
@@ -93,7 +92,7 @@ export class MinimapAxisRenderer {
9392
// Create container for labels
9493
this.labelsContainer = new Container();
9594

96-
// Initialize stroke options (updated in refreshColors)
95+
// Initialize stroke options (updated in setColors)
9796
this.strokeOptions = { color: this.config.tickColor, width: 1 };
9897
}
9998

@@ -196,25 +195,18 @@ export class MinimapAxisRenderer {
196195
}
197196

198197
/**
199-
* Refresh colors from CSS variables (e.g., after VS Code theme change).
200-
* Updates tick and label colors.
198+
* Update tick and label colors (e.g., after theme change).
199+
*
200+
* @param color - Resolved color for ticks and labels (0xRRGGBB)
201201
*/
202-
public refreshColors(): void {
203-
// Re-extract colors from CSS variables
204-
const computedStyle = getComputedStyle(document.documentElement);
205-
206-
// Update tick color
207-
const tickColorStr =
208-
computedStyle.getPropertyValue('--vscode-editorLineNumber-foreground').trim() || '#808080';
209-
this.config.tickColor = parseColorToHex(tickColorStr, 0x808080);
210-
this.strokeOptions.color = this.config.tickColor;
211-
212-
// Update label tint color
213-
this.config.labelTint = parseColorToHex(tickColorStr, 0x808080);
202+
public setColors(color: number): void {
203+
this.config.tickColor = color;
204+
this.strokeOptions.color = color;
205+
this.config.labelTint = color;
214206

215207
// Update existing labels with new tint (BitmapText uses tint for color)
216208
for (const label of this.labelPool) {
217-
label.tint = this.config.labelTint;
209+
label.tint = color;
218210
}
219211
}
220212

0 commit comments

Comments
 (0)