| 92 | // ============================================================================ |
| 93 | |
| 94 | export class CanvasRenderer { |
| 95 | private canvas: HTMLCanvasElement; |
| 96 | private ctx: CanvasRenderingContext2D; |
| 97 | private fontSize: number; |
| 98 | private fontFamily: string; |
| 99 | private cursorStyle: 'block' | 'underline' | 'bar'; |
| 100 | private cursorBlink: boolean; |
| 101 | private theme: Required<ITheme>; |
| 102 | private devicePixelRatio: number; |
| 103 | private metrics: FontMetrics; |
| 104 | private palette: string[]; |
| 105 | |
| 106 | // Cursor blinking state |
| 107 | private cursorVisible: boolean = true; |
| 108 | private cursorBlinkInterval?: number; |
| 109 | private lastCursorPosition: { x: number; y: number } = { x: 0, y: 0 }; |
| 110 | |
| 111 | // Viewport tracking (for scrolling) |
| 112 | private lastViewportY: number = 0; |
| 113 | |
| 114 | // Current buffer being rendered (for grapheme lookups) |
| 115 | private currentBuffer: IRenderable | null = null; |
| 116 | |
| 117 | // Selection manager (for rendering selection) |
| 118 | private selectionManager?: SelectionManager; |
| 119 | // Cached selection coordinates for current render pass (viewport-relative) |
| 120 | private currentSelectionCoords: { |
| 121 | startCol: number; |
| 122 | startRow: number; |
| 123 | endCol: number; |
| 124 | endRow: number; |
| 125 | } | null = null; |
| 126 | |
| 127 | // Link rendering state |
| 128 | private hoveredHyperlinkId: number = 0; |
| 129 | private previousHoveredHyperlinkId: number = 0; |
| 130 | |
| 131 | // Regex link hover tracking (for links without hyperlink_id) |
| 132 | private hoveredLinkRange: { startX: number; startY: number; endX: number; endY: number } | null = |
| 133 | null; |
| 134 | private previousHoveredLinkRange: { |
| 135 | startX: number; |
| 136 | startY: number; |
| 137 | endX: number; |
| 138 | endY: number; |
| 139 | } | null = null; |
| 140 | |
| 141 | constructor(canvas: HTMLCanvasElement, options: RendererOptions = {}) { |
| 142 | this.canvas = canvas; |
| 143 | const ctx = canvas.getContext('2d', { alpha: true }); |
| 144 | if (!ctx) { |
| 145 | throw new Error('Failed to get 2D rendering context'); |
| 146 | } |
| 147 | this.ctx = ctx; |
| 148 | |
| 149 | // Apply options |
| 150 | this.fontSize = options.fontSize ?? 15; |
| 151 | this.fontFamily = options.fontFamily ?? 'monospace'; |
nothing calls this directly
no outgoing calls
no test coverage detected
searching dependent graphs…