mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-24 17:03:55 +10:00
feat(editor): add multicolor highlight with auto-contrast text (#3110)
* feat(editor): add multicolor highlight with auto-contrast text Enable the Tiptap Highlight extension in multicolor mode, replacing the single-color yellow toggle with a full color picker (16 presets + custom). When the chosen highlight color is perceptually dark, text inside the mark automatically renders white for readability. Changes span the full pipeline: - Editor: ColorPicker UI, extended renderHTML for contrast detection - PDF: normalizeMarkElements preserves data-color as inline style - DOCX: mergeStyle reads actual background-color from <mark> - Utils: new isDarkColor() luminance helper Backward-compatible: legacy <mark> without data-color still renders yellow. Resolves #3109 * fix: handle multicolor highlight edge cases --------- Co-authored-by: Amruth Pillai <im.amruth@gmail.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { parseColorString, rgbaStringToHex } from "./color";
|
||||
import { isDarkColor, parseColorString, rgbaStringToHex } from "./color";
|
||||
|
||||
describe("rgbaStringToHex", () => {
|
||||
it("converts opaque rgb to hex", () => {
|
||||
@@ -28,6 +28,16 @@ describe("rgbaStringToHex", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("isDarkColor", () => {
|
||||
it("classifies opaque dark colors as dark", () => {
|
||||
expect(isDarkColor("rgba(0, 0, 0, 1)")).toBe(true);
|
||||
});
|
||||
|
||||
it("composites transparent colors over white before checking darkness", () => {
|
||||
expect(isDarkColor("rgba(0, 0, 0, 0.1)")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("parseColorString", () => {
|
||||
describe("rgb format", () => {
|
||||
it("parses rgb without alpha as alpha=1", () => {
|
||||
|
||||
@@ -54,3 +54,16 @@ export function parseColorString(value: string): ColorResult["rgba"] | null {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Returns true if the given color string is perceptually dark (luminance < 128). */
|
||||
export function isDarkColor(colorString: string): boolean {
|
||||
const color = parseColorString(colorString);
|
||||
if (!color) return false;
|
||||
const alpha = Math.max(0, Math.min(1, color.a ?? 1));
|
||||
const r = color.r * alpha + 255 * (1 - alpha);
|
||||
const g = color.g * alpha + 255 * (1 - alpha);
|
||||
const b = color.b * alpha + 255 * (1 - alpha);
|
||||
// Relative luminance (ITU-R BT.601)
|
||||
const luminance = 0.299 * r + 0.587 * g + 0.114 * b;
|
||||
return luminance < 128;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user