🧹 Extract SVG sparkline rendering to separate module

Extracted `buildSparkline` from `src/ui/editor.ts` into a new file `src/ui/sparkline.ts`. This separates the data processing and generic editor logic from the visual presentation layer.

Co-authored-by: Horizontal42 <75383519+Horizontal42@users.noreply.github.com>
This commit is contained in:
google-labs-jules[bot] 2026-08-09 10:23:35 +00:00
parent c958f30662
commit 5c576aba49
2 changed files with 46 additions and 46 deletions

View file

@ -5,6 +5,7 @@ import { EditorState, StateEffect, StateField, RangeSetBuilder } from "@codemirr
import { defaultKeymap, history, historyKeymap } from "@codemirror/commands"; import { defaultKeymap, history, historyKeymap } from "@codemirror/commands";
import { autocompletion, CompletionContext, CompletionResult, acceptCompletion } from "@codemirror/autocomplete"; import { autocompletion, CompletionContext, CompletionResult, acceptCompletion } from "@codemirror/autocomplete";
import { SumEngine, LineResult } from "../engine"; import { SumEngine, LineResult } from "../engine";
import { buildSparkline } from "./sparkline";
export interface EditorCallbacks { export interface EditorCallbacks {
onChange(text: string): void; onChange(text: string): void;
@ -141,52 +142,6 @@ const varHighlightPlugin = ViewPlugin.fromClass(
} }
); );
function buildSparkline(pts: number[], top: number): SVGSVGElement {
const NS = "http://www.w3.org/2000/svg";
const W = 80, H = 20, pad = 2;
const svg = document.createElementNS(NS, "svg") as SVGSVGElement;
svg.setAttribute("width", String(W));
svg.setAttribute("height", String(H));
svg.setAttribute("viewBox", `0 0 ${W} ${H}`);
svg.classList.add("result-sparkline");
svg.style.top = `${top}px`;
const min = Math.min(...pts), max = Math.max(...pts);
if (min === max) {
const line = document.createElementNS(NS, "line");
line.setAttribute("x1", String(pad));
line.setAttribute("y1", String(H / 2));
line.setAttribute("x2", String(W - pad));
line.setAttribute("y2", String(H / 2));
line.setAttribute("stroke", "var(--result)");
line.setAttribute("stroke-width", "1.5");
svg.appendChild(line);
} else {
const coords = pts.map((v, i) => {
const x = pad + (i / (pts.length - 1)) * (W - 2 * pad);
const y = pad + (1 - (v - min) / (max - min)) * (H - 2 * pad);
return `${x.toFixed(1)},${y.toFixed(1)}`;
}).join(" ");
const poly = document.createElementNS(NS, "polyline");
poly.setAttribute("points", coords);
poly.setAttribute("fill", "none");
poly.setAttribute("stroke", "var(--result)");
poly.setAttribute("stroke-width", "1.5");
poly.setAttribute("stroke-linecap", "round");
poly.setAttribute("stroke-linejoin", "round");
svg.appendChild(poly);
const last = pts[pts.length - 1];
const cx = (W - pad).toFixed(1);
const cy = (pad + (1 - (last - min) / (max - min)) * (H - 2 * pad)).toFixed(1);
const dot = document.createElementNS(NS, "circle");
dot.setAttribute("cx", cx);
dot.setAttribute("cy", cy);
dot.setAttribute("r", "2");
dot.setAttribute("fill", "var(--result)");
svg.appendChild(dot);
}
return svg;
}
export class SumEditor { export class SumEditor {
view: EditorView; view: EditorView;
private resultsEl: HTMLElement; private resultsEl: HTMLElement;

45
src/ui/sparkline.ts Normal file
View file

@ -0,0 +1,45 @@
export function buildSparkline(pts: number[], top: number): SVGSVGElement {
const NS = "http://www.w3.org/2000/svg";
const W = 80, H = 20, pad = 2;
const svg = document.createElementNS(NS, "svg") as SVGSVGElement;
svg.setAttribute("width", String(W));
svg.setAttribute("height", String(H));
svg.setAttribute("viewBox", `0 0 ${W} ${H}`);
svg.classList.add("result-sparkline");
svg.style.top = `${top}px`;
const min = Math.min(...pts), max = Math.max(...pts);
if (min === max) {
const line = document.createElementNS(NS, "line");
line.setAttribute("x1", String(pad));
line.setAttribute("y1", String(H / 2));
line.setAttribute("x2", String(W - pad));
line.setAttribute("y2", String(H / 2));
line.setAttribute("stroke", "var(--result)");
line.setAttribute("stroke-width", "1.5");
svg.appendChild(line);
} else {
const coords = pts.map((v, i) => {
const x = pad + (i / (pts.length - 1)) * (W - 2 * pad);
const y = pad + (1 - (v - min) / (max - min)) * (H - 2 * pad);
return `${x.toFixed(1)},${y.toFixed(1)}`;
}).join(" ");
const poly = document.createElementNS(NS, "polyline");
poly.setAttribute("points", coords);
poly.setAttribute("fill", "none");
poly.setAttribute("stroke", "var(--result)");
poly.setAttribute("stroke-width", "1.5");
poly.setAttribute("stroke-linecap", "round");
poly.setAttribute("stroke-linejoin", "round");
svg.appendChild(poly);
const last = pts[pts.length - 1];
const cx = (W - pad).toFixed(1);
const cy = (pad + (1 - (last - min) / (max - min)) * (H - 2 * pad)).toFixed(1);
const dot = document.createElementNS(NS, "circle");
dot.setAttribute("cx", cx);
dot.setAttribute("cy", cy);
dot.setAttribute("r", "2");
dot.setAttribute("fill", "var(--result)");
svg.appendChild(dot);
}
return svg;
}