* Render a single capture frame to the canvas
( ctx: CanvasRenderingContext2D, capture: Capture, canvasWidth: number, canvasHeight: number, renderCtx: RenderContext )
| 89 | * Render a single capture frame to the canvas |
| 90 | */ |
| 91 | function renderFrame( |
| 92 | ctx: CanvasRenderingContext2D, |
| 93 | capture: Capture, |
| 94 | canvasWidth: number, |
| 95 | canvasHeight: number, |
| 96 | renderCtx: RenderContext |
| 97 | ): void { |
| 98 | const { fontSize, lineHeight, bgColor, fgColor, labelColor, showLabel } = renderCtx |
| 99 | |
| 100 | // Clear and fill background |
| 101 | ctx.fillStyle = bgColor |
| 102 | ctx.fillRect(0, 0, canvasWidth, canvasHeight) |
| 103 | |
| 104 | // Set up text rendering |
| 105 | ctx.font = `${fontSize}px monospace` |
| 106 | ctx.textBaseline = 'top' |
| 107 | ctx.fillStyle = fgColor |
| 108 | |
| 109 | // Calculate content area |
| 110 | const padding = 10 |
| 111 | const labelHeight = showLabel ? 30 : 0 |
| 112 | const contentStartY = padding + labelHeight |
| 113 | |
| 114 | // Render label if enabled |
| 115 | if (showLabel) { |
| 116 | const label = capture.frontMatter.label || `Capture ${capture.frontMatter.sequence}` |
| 117 | const time = formatTimestamp(capture.frontMatter.timestamp) |
| 118 | |
| 119 | ctx.fillStyle = labelColor |
| 120 | ctx.font = `bold ${fontSize - 2}px sans-serif` |
| 121 | ctx.fillText(`[${capture.frontMatter.sequence}] ${label}`, padding, padding) |
| 122 | |
| 123 | // Time on the right |
| 124 | const timeText = time |
| 125 | const timeWidth = ctx.measureText(timeText).width |
| 126 | ctx.fillText(timeText, canvasWidth - padding - timeWidth, padding) |
| 127 | |
| 128 | // Draw separator line |
| 129 | ctx.strokeStyle = labelColor |
| 130 | ctx.lineWidth = 1 |
| 131 | ctx.beginPath() |
| 132 | ctx.moveTo(padding, padding + fontSize + 5) |
| 133 | ctx.lineTo(canvasWidth - padding, padding + fontSize + 5) |
| 134 | ctx.stroke() |
| 135 | } |
| 136 | |
| 137 | // Render terminal content |
| 138 | ctx.font = `${fontSize}px monospace` |
| 139 | ctx.fillStyle = fgColor |
| 140 | |
| 141 | const lines = capture.content.split('\n') |
| 142 | const maxLines = Math.floor((canvasHeight - contentStartY - padding) / lineHeight) |
| 143 | |
| 144 | for (let i = 0; i < Math.min(lines.length, maxLines); i++) { |
| 145 | const line = lines[i] |
| 146 | // Strip ANSI codes for now (basic support) |
| 147 | const cleanLine = stripAnsiCodes(line) |
| 148 | ctx.fillText(cleanLine, padding, contentStartY + i * lineHeight) |
no test coverage detected