fix(app): stabilize canvas diff rendering

Keep diff headers and painted content on the same scroll clock while preserving native scrolling and independent per-file horizontal offsets.

Complete review, selection, typography, tree, and palette parity without returning line rendering to React.
This commit is contained in:
Mohamed Boudra
2026-08-17 21:14:38 +02:00
parent 4d23905587
commit c7b103a2cd
50 changed files with 2180 additions and 580 deletions
+12 -1
View File
@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { desaturateHexColor, parseHexColor } from "./color";
import { desaturateHexColor, hexColorWithAlpha, parseHexColor } from "./color";
describe("parseHexColor", () => {
it("parses six-digit hex", () => {
@@ -17,6 +17,17 @@ describe("parseHexColor", () => {
});
});
describe("hexColorWithAlpha", () => {
it("creates a cross-platform rgba color from an owned theme color", () => {
expect(hexColorWithAlpha("#3e704a", 0.15)).toBe("rgba(62, 112, 74, 0.15)");
});
it("rejects colors and alpha values outside its contract", () => {
expect(() => hexColorWithAlpha("currentColor", 0.15)).toThrow();
expect(() => hexColorWithAlpha("#3e704a", 1.1)).toThrow();
});
});
describe("desaturateHexColor", () => {
it("leaves a colour alone at full chroma", () => {
expect(desaturateHexColor("#0288d1", 1)).toBe("#0288d1");
+11
View File
@@ -67,6 +67,17 @@ export function parseHexColor(hex: string): [number, number, number] | null {
];
}
/** Adds alpha to a theme-owned hex color in a format accepted by Canvas and Skia. */
export function hexColorWithAlpha(hex: string, alpha: number): string {
const rgb = parseHexColor(hex);
if (!rgb) throw new TypeError(`Expected a hex color, received ${hex}`);
if (!Number.isFinite(alpha) || alpha < 0 || alpha > 1) {
throw new RangeError(`Color alpha must be between 0 and 1, received ${alpha}`);
}
const [r, g, b] = rgb.map((channel) => Math.round(channel * 255));
return `rgba(${r}, ${g}, ${b}, ${alpha})`;
}
function toHexChannel(channel: number): string {
const clamped = Math.min(255, Math.max(0, Math.round(channel * 255)));
return clamped.toString(16).padStart(2, "0");