MCPcopy
hub / github.com/codeaashu/claude-code / ansiToSvg

Function ansiToSvg

src/utils/ansiToSvg.ts:207–272  ·  view source on GitHub ↗
(
  ansiText: string,
  options: AnsiToSvgOptions = {},
)

Source from the content-addressed store, hash-verified

205 * handles character spacing natively (no manual charWidth calculation)
206 */
207export function ansiToSvg(
208 ansiText: string,
209 options: AnsiToSvgOptions = {},
210): string {
211 const {
212 fontFamily = 'Menlo, Monaco, monospace',
213 fontSize = 14,
214 lineHeight = 22,
215 paddingX = 24,
216 paddingY = 24,
217 backgroundColor = `rgb(${DEFAULT_BG.r}, ${DEFAULT_BG.g}, ${DEFAULT_BG.b})`,
218 borderRadius = 8,
219 } = options
220
221 const lines = parseAnsi(ansiText)
222
223 // Trim trailing empty lines
224 while (
225 lines.length > 0 &&
226 lines[lines.length - 1]!.every(span => span.text.trim() === '')
227 ) {
228 lines.pop()
229 }
230
231 // Estimate width based on max line length (for SVG dimensions only)
232 // For monospace fonts, character width is roughly 0.6 * fontSize
233 const charWidthEstimate = fontSize * 0.6
234 const maxLineLength = Math.max(
235 ...lines.map(spans => spans.reduce((acc, s) => acc + s.text.length, 0)),
236 )
237 const width = Math.ceil(maxLineLength * charWidthEstimate + paddingX * 2)
238 const height = lines.length * lineHeight + paddingY * 2
239
240 // Build SVG - use tspan elements so renderer handles character positioning
241 let svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">\n`
242 svg += ` <rect width="100%" height="100%" fill="${backgroundColor}" rx="${borderRadius}" ry="${borderRadius}"/>\n`
243 svg += ` <style>\n`
244 svg += ` text { font-family: ${fontFamily}; font-size: ${fontSize}px; white-space: pre; }\n`
245 svg += ` .b { font-weight: bold; }\n`
246 svg += ` </style>\n`
247
248 for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
249 const spans = lines[lineIndex]!
250 const y =
251 paddingY + (lineIndex + 1) * lineHeight - (lineHeight - fontSize) / 2
252
253 // Build a single <text> element with <tspan> children for each colored segment
254 // xml:space="preserve" prevents SVG from collapsing whitespace
255 svg += ` <text x="${paddingX}" y="${y}" xml:space="preserve">`
256
257 for (const span of spans) {
258 if (!span.text) continue
259
260 const colorStr = `rgb(${span.color.r}, ${span.color.g}, ${span.color.b})`
261 const boldClass = span.bold ? ' class="b"' : ''
262
263 svg += `<tspan fill="${colorStr}"${boldClass}>${escapeXml(span.text)}</tspan>`
264 }

Callers

nothing calls this directly

Calls 4

escapeXmlFunction · 0.85
popMethod · 0.80
maxMethod · 0.80
parseAnsiFunction · 0.70

Tested by

no test coverage detected