diff --git a/src/content-modules/Eval.svelte b/src/content-modules/Eval.svelte index 8d73b48f..5d9b1c7a 100644 --- a/src/content-modules/Eval.svelte +++ b/src/content-modules/Eval.svelte @@ -24,9 +24,11 @@ $: colorNames = colorNameSimple(colors); $: palType = $colorStore.currentPal.type; $: evalConfig = $colorStore.currentPal.evalConfig; - $: checks = runLintChecks($colorStore.currentPal).filter((x) => - x.taskTypes.includes(palType) - ); + $: checks = []; + $: runLintChecks($colorStore.currentPal).then((result) => { + console.log(result); + checks = result.filter((x) => x.taskTypes.includes(palType)); + }); $: colorsToIssues = colors.map((x) => { const hex = `${x.toHex()}`; diff --git a/src/lib/api-calls.ts b/src/lib/api-calls.ts index aa0b7fe0..4ec3fec8 100644 --- a/src/lib/api-calls.ts +++ b/src/lib/api-calls.ts @@ -8,11 +8,31 @@ import ViteWorker from "./heavy-computation.worker?worker"; const tsWorker = new ViteWorker(); // send and receive messages from the worker -tsWorker.postMessage({ type: "test", content: "hellow" }); +const message = { type: "test", content: "hellow" }; +const randID = () => Math.random().toString(36).substring(7); +function workerDispatch() { + const waitingCallbacks: { [key: string]: (msg: string) => void } = {}; + tsWorker.addEventListener("message", (msg: MessageEvent) => { + const { id, content } = msg.data; + if (waitingCallbacks[id]) { + waitingCallbacks[id](content); + delete waitingCallbacks[id]; + } + }); -tsWorker.addEventListener("message", (msg: MessageEvent) => { - console.log(msg.data); // how now, brown cow? -}); + return async function caller(msg: typeof message) { + const id = randID(); + tsWorker.postMessage({ ...msg, id }); + return new Promise((resolve) => { + waitingCallbacks[id] = resolve; + }); + }; +} +const dispatch = workerDispatch(); + +export function colorName(c: Color) { + return dispatch({ type: "color-name", content: c.toHex() }); +} type Engine = "openai" | "google"; type SimplePal = { background: string; colors: string[] }; diff --git a/src/lib/color-stats.ts b/src/lib/color-stats.ts index 9f4d0e68..1ca7086e 100644 --- a/src/lib/color-stats.ts +++ b/src/lib/color-stats.ts @@ -65,295 +65,3 @@ function deltaE94(c1: ChromaColor, c2: ChromaColor) { } /////////// JEFF'S CODE /////////////////////// - -// // const jsonData = require("../assets/c3-data.json"); -// import jsonData from "../assets/c3-data.json"; - -// function c3_api() { -// const C = c3.color.length; -// const W = c3.terms.length; -// const T = c3.T; -// const A = c3.A; -// const ccount = c3.color.count; -// const tcount = c3.terms.count; - -// c3.count = (c: number, w: number) => T[c * W + w] || 0; -// c3.terms.prob = (w: number, c: number) => (T[c * W + w] || 0) / tcount[w]; - -// c3.terms.entropy = function (w: number) { -// let H = 0; -// let p; -// for (let c = 0; c < C; ++c) { -// p = (T[c * W + w] || 0) / tcount[w]; -// if (p > 0) H += (p * Math.log(p)) / Math.LN2; -// } -// return H; -// }; - -// c3.terms.perplexity = (w: any) => Math.pow(2, -c3.terms.entropy(w)); - -// c3.terms.cosine = function (a: number, b: number) { -// let sa = 0; -// let sb = 0; -// let sc = 0; -// let ta: number; -// let tb: number; -// for (let c = 0; c < C; ++c) { -// ta = T[c * W + a] || 0; -// tb = T[c * W + b] || 0; -// sa += ta * ta; -// sb += tb * tb; -// sc += ta * tb; -// } -// return sc / Math.sqrt(sa * sb); -// }; - -// c3.color.cosine = function (a: number, b: number) { -// let sa = 0; -// let sb = 0; -// let sc = 0; -// let ta: number; -// let tb: number; -// for (let w = 0; w < W; ++w) { -// ta = T[a * W + w] || 0; -// tb = T[b * W + w] || 0; -// sa += ta * ta; -// sb += tb * tb; -// sc += ta * tb; -// } -// return sc / Math.sqrt(sa * sb); -// }; - -// c3.color.prob = (c: number, w: number) => (T[c * W + w] || 0) / ccount[c]; - -// c3.color.entropy = function (c: number) { -// let H = 0; -// let p: number; -// for (let w = 0; w < W; ++w) { -// p = (T[c * W + w] || 0) / ccount[c]; -// if (p > 0) H += (p * Math.log(p)) / Math.LN2; -// } -// return H; -// }; - -// c3.terms.hellinger = function (a: number, b: number) { -// let bc = 0; -// let pa: number; -// let pb: number; -// let z = Math.sqrt(tcount[a] * tcount[b]); -// for (let c = 0; c < C; ++c) { -// pa = T[c * W + a] || 0; -// pb = T[c * W + b] || 0; -// bc += Math.sqrt(pa * pb); -// } -// return Math.sqrt(1 - bc / z); -// }; - -// c3.color.perplexity = (c: any) => Math.pow(2, -c3.color.entropy(c)); - -// c3.color.hellinger = function (a: number, b: number) { -// let bc = 0; -// let pa: number; -// let pb: number; -// let z = Math.sqrt(ccount[a] * ccount[b]); -// for (let w = 0; w < W; ++w) { -// pa = T[a * W + w] || 0; -// pb = T[b * W + w] || 0; -// bc += Math.sqrt(pa * pb); -// } -// return Math.sqrt(1 - bc / z); -// }; - -// c3.terms.relatedTerms = function (w: number, limit: number | undefined) { -// const c = c3.terms.center[w]; -// const list = []; -// for (let i = 0; i < W; ++i) { -// if (i != w) list.push({ index: i, score: A[i * W + w] }); -// } -// list.sort(function (a, b) { -// let ca: { de00: (arg0: any) => number }; -// let cb: { de00: (arg0: any) => number }; - -// let cmp = b.score - a.score; -// if (Math.abs(cmp) < 0.00005) { -// // break near ties by distance between centers -// ca = c3.terms.center[a.index]; -// cb = c3.terms.center[b.index]; -// cmp = ca.de00(c) - cb.de00(c); -// } -// return cmp; -// }); -// list.unshift({ index: w, score: A[w * W + w] }); -// return limit ? list.slice(0, limit) : list; -// }; - -// c3.terms.relatedColors = function (w: number, limit: number | undefined) { -// const list = []; -// for (let c = 0; c < C; ++c) { -// const s = (T[c * W + w] || 0) / ccount[c]; -// if (s > 0) list.push({ index: c, score: s }); -// } -// list.sort(function (a, b) { -// return b.score - a.score; -// }); -// return limit ? list.slice(0, limit) : list; -// }; - -// c3.color.relatedTerms = function ( -// c: number, -// limit: number | undefined, -// minCount: number -// ) { -// const cc = c * W; -// let list = []; -// let sum = 0; -// let s: any; -// let cnt = c3.terms.count; -// for (let w = 0; w < W; ++w) { -// if ((s = T[cc + w]) !== undefined) { -// list.push({ index: w, score: s }); -// sum += s; -// } -// } -// if (minCount) { -// list = list.filter(function (d) { -// return cnt[d.index] > minCount; -// }); -// } -// list.sort(function (a, b) { -// return b.score - a.score; -// }); -// list.forEach(function (d) { -// d.score /= sum; -// }); -// return limit ? list.slice(0, limit) : list; -// }; - -// // compute representative colors -// c3.terms.center = Array.from({ length: W }, (_, i) => i).map(function (w) { -// const list = c3.terms -// .relatedColors(w, 5) -// .map(function (d: { index: string | number }) { -// return c3.color[d.index]; -// }); -// let L = 0; -// let a = 0; -// let b = 0; -// let N = list.length; -// list.forEach(function (c: { L: any; a: any; b: any }) { -// L += c.L; -// a += c.a; -// b += c.b; -// }); -// return chroma.lab(Math.round(L / N), Math.round(a / N), Math.round(b / N)); -// // return d3.lab(Math.round(L / N), Math.round(a / N), Math.round(b / N)); -// }); -// } - -// function c3_init(json: { -// color: string | any[]; -// terms: any; -// T: string | any[]; -// A: any; -// }) { -// let i: number; -// let C: number; -// let W: number; -// let T: any[]; -// let A: any; -// let ccount: any[]; -// let tcount: any[]; - -// // parse colors -// c3.color = []; -// for (i = 0; i < json.color.length; i += 3) { -// c3.color[i / 3] = chroma.lab( -// json.color[i], -// json.color[i + 1], -// json.color[i + 2] -// ); -// } -// C = c3.color.length; - -// // parse terms -// c3.terms = json.terms; -// W = c3.terms.length; - -// // parse count table -// c3.T = T = []; -// for (let i = 0; i < json.T.length; i += 2) { -// T[json.T[i]] = json.T[i + 1]; -// } - -// // construct counts -// c3.color.count = ccount = []; -// for (i = 0; i < C; ++i) ccount[i] = 0; -// c3.terms.count = tcount = []; -// for (i = 0; i < W; ++i) tcount[i] = 0; -// T.forEach((_x, idx) => { -// const c = Math.floor(idx / W); -// const w = Math.floor(idx % W); -// const v = T[idx] || 0; -// ccount[c] += v; -// tcount[w] += v; -// }); - -// // parse word association matrix -// c3.A = A = json.A; - -// const labToString = (L: number, a: number, b: number) => -// [5 * Math.round(L / 5), 5 * Math.round(a / 5), 5 * Math.round(b / 5)].join( -// "," -// ); -// var map: Record = {}; -// for (var c = 0; c < c3.color.length; ++c) { -// const [L, a, b] = chroma(c3.color[c]).lab(); -// map[labToString(L, a, b)] = c; -// } - -// function index(c: string) { -// const [L, a, b] = chroma(c).lab(); -// const s = labToString(L, a, b); -// return map[s]; -// } - -// c3.colorIdentity = (colorString: string) => { -// // NOTE: entropy min/max currently hard-wired to XKCD results -// const minE = -4.5; -// const maxE = 0; -// var c = index(colorString); -// var h = (c3.color.entropy(c) - minE) / (maxE - minE); -// var t = c3.color -// .relatedTerms(c, 1) -// .map((x: any) => ({ score: x.score, word: c3.terms[x.index] })); -// var [L, a, b] = chroma(colorString).lab(); -// const z = ~~L + ", " + ~~a + ", " + ~~b; - -// return { x: colorString, c: c, h: h, terms: t, z: z }; -// }; -// } - -// export function colorNameDiscrimCheck( -// colorNames: { word: string }[] -// ): false | string { -// const colorNameCounts = colorNames.reduce((acc, colorName) => { -// if (!acc[colorName.word]) { -// acc[colorName.word] = 0; -// } -// acc[colorName.word] += 1; -// return acc; -// }, {} as Record); - -// const remaining = Object.entries(colorNameCounts as Record) -// .filter((x) => x[1] > 1) -// .map((x) => x[0]); -// if (remaining.length === 0) { -// return false; -// } -// const counts = remaining.map((x) => `${x} (${colorNameCounts[x]})`); -// return `Color Name discriminability check failed. The following color names are repeated: ${counts}`; -// } - -// export const c3: any = {}; -// c3_init(jsonData); -// c3_api(); diff --git a/src/lib/heavy-computation.worker.ts b/src/lib/heavy-computation.worker.ts index 21b61d8d..f37350a8 100644 --- a/src/lib/heavy-computation.worker.ts +++ b/src/lib/heavy-computation.worker.ts @@ -1,17 +1,298 @@ +import chroma from "chroma-js"; type Command = { type: "load-data"; content: "" }; +const get = async (url: string) => { + return await fetch(url).then((x) => x.json()); +}; + async function dispatch(cmd: Command) { switch (cmd.type) { - case "load-data": - await loadData(); + case "color-name": + const xx = c3.colorIdentity(cmd.content)?.terms[0]?.word || "black"; + console.log(xx); + return xx; + // await loadData(); break; } } -self.onmessage = (event: MessageEvent) => { +self.onmessage = async (event: MessageEvent) => { let response = ""; - console.log("here", event.data); - self.postMessage(event.data); + // console.log("here", event.data); + const result = await dispatch(event.data); + // console.log(JSON.stringify(result)); + self.postMessage({ id: event.data.id, content: result }); }; export {}; // this is to make typescript happy + +const c3 = {} as any; +// const jsonData = require("../assets/c3-data.json"); +import jsonData from "../assets/c3-data.json"; + +function c3_api() { + const C = c3.color.length; + const W = c3.terms.length; + const T = c3.T; + const A = c3.A; + const ccount = c3.color.count; + const tcount = c3.terms.count; + + c3.count = (c: number, w: number) => T[c * W + w] || 0; + c3.terms.prob = (w: number, c: number) => (T[c * W + w] || 0) / tcount[w]; + + c3.terms.entropy = function (w: number) { + let H = 0; + let p; + for (let c = 0; c < C; ++c) { + p = (T[c * W + w] || 0) / tcount[w]; + if (p > 0) H += (p * Math.log(p)) / Math.LN2; + } + return H; + }; + + c3.terms.perplexity = (w: any) => Math.pow(2, -c3.terms.entropy(w)); + + c3.terms.cosine = function (a: number, b: number) { + let sa = 0; + let sb = 0; + let sc = 0; + let ta: number; + let tb: number; + for (let c = 0; c < C; ++c) { + ta = T[c * W + a] || 0; + tb = T[c * W + b] || 0; + sa += ta * ta; + sb += tb * tb; + sc += ta * tb; + } + return sc / Math.sqrt(sa * sb); + }; + + c3.color.cosine = function (a: number, b: number) { + let sa = 0; + let sb = 0; + let sc = 0; + let ta: number; + let tb: number; + for (let w = 0; w < W; ++w) { + ta = T[a * W + w] || 0; + tb = T[b * W + w] || 0; + sa += ta * ta; + sb += tb * tb; + sc += ta * tb; + } + return sc / Math.sqrt(sa * sb); + }; + + c3.color.prob = (c: number, w: number) => (T[c * W + w] || 0) / ccount[c]; + + c3.color.entropy = function (c: number) { + let H = 0; + let p: number; + for (let w = 0; w < W; ++w) { + p = (T[c * W + w] || 0) / ccount[c]; + if (p > 0) H += (p * Math.log(p)) / Math.LN2; + } + return H; + }; + + c3.terms.hellinger = function (a: number, b: number) { + let bc = 0; + let pa: number; + let pb: number; + let z = Math.sqrt(tcount[a] * tcount[b]); + for (let c = 0; c < C; ++c) { + pa = T[c * W + a] || 0; + pb = T[c * W + b] || 0; + bc += Math.sqrt(pa * pb); + } + return Math.sqrt(1 - bc / z); + }; + + c3.color.perplexity = (c: any) => Math.pow(2, -c3.color.entropy(c)); + + c3.color.hellinger = function (a: number, b: number) { + let bc = 0; + let pa: number; + let pb: number; + let z = Math.sqrt(ccount[a] * ccount[b]); + for (let w = 0; w < W; ++w) { + pa = T[a * W + w] || 0; + pb = T[b * W + w] || 0; + bc += Math.sqrt(pa * pb); + } + return Math.sqrt(1 - bc / z); + }; + + c3.terms.relatedTerms = function (w: number, limit: number | undefined) { + const c = c3.terms.center[w]; + const list = []; + for (let i = 0; i < W; ++i) { + if (i != w) list.push({ index: i, score: A[i * W + w] }); + } + list.sort(function (a, b) { + let ca: { de00: (arg0: any) => number }; + let cb: { de00: (arg0: any) => number }; + + let cmp = b.score - a.score; + if (Math.abs(cmp) < 0.00005) { + // break near ties by distance between centers + ca = c3.terms.center[a.index]; + cb = c3.terms.center[b.index]; + cmp = ca.de00(c) - cb.de00(c); + } + return cmp; + }); + list.unshift({ index: w, score: A[w * W + w] }); + return limit ? list.slice(0, limit) : list; + }; + + c3.terms.relatedColors = function (w: number, limit: number | undefined) { + const list = []; + for (let c = 0; c < C; ++c) { + const s = (T[c * W + w] || 0) / ccount[c]; + if (s > 0) list.push({ index: c, score: s }); + } + list.sort(function (a, b) { + return b.score - a.score; + }); + return limit ? list.slice(0, limit) : list; + }; + + c3.color.relatedTerms = function ( + c: number, + limit: number | undefined, + minCount: number + ) { + const cc = c * W; + let list = []; + let sum = 0; + let s: any; + let cnt = c3.terms.count; + for (let w = 0; w < W; ++w) { + if ((s = T[cc + w]) !== undefined) { + list.push({ index: w, score: s }); + sum += s; + } + } + if (minCount) { + list = list.filter(function (d) { + return cnt[d.index] > minCount; + }); + } + list.sort(function (a, b) { + return b.score - a.score; + }); + list.forEach(function (d) { + d.score /= sum; + }); + return limit ? list.slice(0, limit) : list; + }; + + // compute representative colors + c3.terms.center = Array.from({ length: W }, (_, i) => i).map(function (w) { + const list = c3.terms + .relatedColors(w, 5) + .map(function (d: { index: string | number }) { + return c3.color[d.index]; + }); + let L = 0; + let a = 0; + let b = 0; + let N = list.length; + list.forEach(function (c: { L: any; a: any; b: any }) { + L += c.L; + a += c.a; + b += c.b; + }); + return chroma.lab(Math.round(L / N), Math.round(a / N), Math.round(b / N)); + // return d3.lab(Math.round(L / N), Math.round(a / N), Math.round(b / N)); + }); +} + +function c3_init(json: { + color: string | any[]; + terms: any; + T: string | any[]; + A: any; +}) { + let i: number; + let C: number; + let W: number; + let T: any[]; + let A: any; + let ccount: any[]; + let tcount: any[]; + + // parse colors + c3.color = []; + for (i = 0; i < json.color.length; i += 3) { + c3.color[i / 3] = chroma.lab( + json.color[i], + json.color[i + 1], + json.color[i + 2] + ); + } + C = c3.color.length; + + // parse terms + c3.terms = json.terms; + W = c3.terms.length; + + // parse count table + c3.T = T = []; + for (let i = 0; i < json.T.length; i += 2) { + T[json.T[i]] = json.T[i + 1]; + } + + // construct counts + c3.color.count = ccount = []; + for (i = 0; i < C; ++i) ccount[i] = 0; + c3.terms.count = tcount = []; + for (i = 0; i < W; ++i) tcount[i] = 0; + T.forEach((_x, idx) => { + const c = Math.floor(idx / W); + const w = Math.floor(idx % W); + const v = T[idx] || 0; + ccount[c] += v; + tcount[w] += v; + }); + + // parse word association matrix + c3.A = A = json.A; + + const labToString = (L: number, a: number, b: number) => + [5 * Math.round(L / 5), 5 * Math.round(a / 5), 5 * Math.round(b / 5)].join( + "," + ); + var map: Record = {}; + for (var c = 0; c < c3.color.length; ++c) { + const [L, a, b] = chroma(c3.color[c]).lab(); + map[labToString(L, a, b)] = c; + } + + function index(c: string) { + const [L, a, b] = chroma(c).lab(); + const s = labToString(L, a, b); + return map[s]; + } + + c3.colorIdentity = (colorString: string) => { + // NOTE: entropy min/max currently hard-wired to XKCD results + const minE = -4.5; + const maxE = 0; + var c = index(colorString); + var h = (c3.color.entropy(c) - minE) / (maxE - minE); + var t = c3.color + .relatedTerms(c, 1) + .map((x: any) => ({ score: x.score, word: c3.terms[x.index] })); + var [L, a, b] = chroma(colorString).lab(); + const z = ~~L + ", " + ~~a + ", " + ~~b; + + return { x: colorString, c: c, h: h, terms: t, z: z }; + }; +} + +c3_init(jsonData); +c3_api(); diff --git a/src/lib/linter.ts b/src/lib/linter.ts index 5a949b39..514be20c 100644 --- a/src/lib/linter.ts +++ b/src/lib/linter.ts @@ -13,18 +13,21 @@ import AvoidExtremes from "./lints/avoid-extremes"; import DivergingOrder from "./lints/diverging-order"; import BackgroundContrast from "./lints/contrast"; -export function runLintChecks(palette: Palette): ColorLint[] { - return [ - new NameDiscrim(palette), - new MaxColors(palette), - ...Discrims.map((x) => new x(palette)), - ...Blinds.map((x) => new x(palette)), - new ColorSimilarity(palette), - new BackgroundDifferentiability(palette), - new UglyColors(palette), - new SequentialOrder(palette), - new AvoidExtremes(palette), - new DivergingOrder(palette), - new BackgroundContrast(palette), - ].map((x) => x.run()); +export async function runLintChecks(palette: Palette) { + const lints = Promise.all( + [ + new NameDiscrim(palette), + new MaxColors(palette), + ...Discrims.map((x) => new x(palette)), + ...Blinds.map((x) => new x(palette)), + new ColorSimilarity(palette), + new BackgroundDifferentiability(palette), + new UglyColors(palette), + new SequentialOrder(palette), + new AvoidExtremes(palette), + new DivergingOrder(palette), + new BackgroundContrast(palette), + ].map(async (x) => await x.run()) + ); + return await lints; } diff --git a/src/lib/lints/ColorLint.ts b/src/lib/lints/ColorLint.ts index 5c4c91a9..cde591bb 100644 --- a/src/lib/lints/ColorLint.ts +++ b/src/lib/lints/ColorLint.ts @@ -53,21 +53,21 @@ export class ColorLint { return copy; } - run() { + async run() { const { evalConfig } = this.palette; this.config = { ...evalConfig[this.name], val: evalConfig[this.name]?.val || this.defaultParam, }; - const { passCheck, data } = this._runCheck(); + const { passCheck, data } = await this._runCheck(); this.passes = passCheck; this.checkData = data as CheckData; this.message = this.buildMessage(); return this; } - _runCheck(): { passCheck: boolean; data: CheckData } { + async _runCheck(): Promise<{ passCheck: boolean; data: CheckData }> { return { passCheck: true, data: {} as CheckData }; } // Fail Message diff --git a/src/lib/lints/avoid-extremes.ts b/src/lib/lints/avoid-extremes.ts index deb40556..93413dd4 100644 --- a/src/lib/lints/avoid-extremes.ts +++ b/src/lib/lints/avoid-extremes.ts @@ -15,7 +15,7 @@ export default class ExtremeColors extends ColorLint { taskTypes = ["sequential", "diverging", "categorical"] as TaskType[]; level: "error" | "warning" = "warning"; - _runCheck() { + async _runCheck() { const { colors } = this.palette; const extremes = findExtremeColors(colors); const passCheck = extremes.length === 0; diff --git a/src/lib/lints/background-differentiability.ts b/src/lib/lints/background-differentiability.ts index 49817307..229f54fe 100644 --- a/src/lib/lints/background-differentiability.ts +++ b/src/lib/lints/background-differentiability.ts @@ -10,7 +10,7 @@ export default class BackgroundDifferentiability extends ColorLint< > { name = "All colors differentiable from background"; taskTypes = ["sequential", "diverging", "categorical"] as TaskType[]; - _runCheck() { + async _runCheck() { const { colors, background } = this.palette; const colorsCloseToBackground = colors.reduce((acc, x, idx) => { const pass = x.symmetricDeltaE(background) < 15; diff --git a/src/lib/lints/blind-check.ts b/src/lib/lints/blind-check.ts index fa7fca89..709d3624 100644 --- a/src/lib/lints/blind-check.ts +++ b/src/lib/lints/blind-check.ts @@ -74,7 +74,7 @@ const checks = blindnessTypes.map((key) => { return class ColorBlindCheck extends ColorLint<[number, number][], false> { name = `Colorblind Friendly for ${key}`; taskTypes = ["sequential", "diverging", "categorical"] as TaskType[]; - _runCheck() { + async _runCheck() { const colors = this.palette.colors; const { pass, notOKColorIndexes } = checkType(colors, key); return { passCheck: pass, data: notOKColorIndexes }; diff --git a/src/lib/lints/color-similarity.ts b/src/lib/lints/color-similarity.ts index e6f99c2c..7bcd7017 100644 --- a/src/lib/lints/color-similarity.ts +++ b/src/lib/lints/color-similarity.ts @@ -13,7 +13,7 @@ export default class ColorSimilarity extends ColorLint { max: 100, step: 1, }; - _runCheck() { + async _runCheck() { const { colors } = this.palette; const data = computeStats( colors.map((x) => x.toChroma()), diff --git a/src/lib/lints/contrast.ts b/src/lib/lints/contrast.ts index ca3308fb..114e2e9a 100644 --- a/src/lib/lints/contrast.ts +++ b/src/lib/lints/contrast.ts @@ -21,7 +21,7 @@ export default class BackgroundContrast extends ColorLint { }; level: "error" | "warning" = "error"; - _runCheck() { + async _runCheck() { const { background } = this.palette; const bg = background.toColorIO(); const failingColors = this.palette.colors.filter((x) => { diff --git a/src/lib/lints/diverging-order.ts b/src/lib/lints/diverging-order.ts index 7dddc9d7..55a5ec95 100644 --- a/src/lib/lints/diverging-order.ts +++ b/src/lib/lints/diverging-order.ts @@ -21,7 +21,7 @@ export default class SequentialOrder extends ColorLint { name = "Diverging Palettes should have a middle color that is the lightest or darkest color"; taskTypes = ["diverging"] as TaskType[]; - _runCheck() { + async _runCheck() { const { colors } = this.palette; if (colors.length <= 2) { return { passCheck: true, data: false }; diff --git a/src/lib/lints/max-colors.ts b/src/lib/lints/max-colors.ts index af80dadd..c3a4bda1 100644 --- a/src/lib/lints/max-colors.ts +++ b/src/lib/lints/max-colors.ts @@ -14,7 +14,7 @@ export default class MaxColors extends ColorLint { }; level: "error" | "warning" = "warning"; - _runCheck() { + async _runCheck() { const { colors } = this.palette; const numColors = colors.length; return { diff --git a/src/lib/lints/name-discrim.ts b/src/lib/lints/name-discrim.ts index 698cf6b8..25183994 100644 --- a/src/lib/lints/name-discrim.ts +++ b/src/lib/lints/name-discrim.ts @@ -2,6 +2,7 @@ import namer from "color-namer"; import { ColorLint } from "./ColorLint"; import type { TaskType } from "./ColorLint"; import { Color, colorFromHex } from "../Color"; +import { colorName } from "../api-calls"; function findSmallest(arr: A[], accessor: (x: A) => number): A { let smallest = arr[0]; @@ -21,11 +22,14 @@ function titleCase(str: string) { // Simpler version of the color name stuff export function colorNameSimple(colors: Color[]) { - return colors.map((x) => ({ word: getName(x), hex: x.toHex() })); + // return colors.map((x) => ({ word: getName(x), hex: x.toHex() })); + return Promise.all(colors.map((x) => colorName(x))).then((names) => + names.map((x, idx) => ({ word: x, hex: colors[idx].toHex() })) + ); } -function simpleDiscrim(colors: Color[]) { - const names = colorNameSimple(colors); +async function simpleDiscrim(colors: Color[]) { + const names = await colorNameSimple(colors); const counts = names.reduce((acc, x) => { if (!acc[x.word]) { acc[x.word] = []; @@ -98,9 +102,9 @@ export default class ColorNameDiscriminability extends ColorLint< > { name = "Color Name Discriminability"; taskTypes = ["sequential", "diverging", "categorical"] as TaskType[]; - _runCheck() { + async _runCheck() { const { colors } = this.palette; - const passCheck = simpleDiscrim(colors); + const passCheck = await simpleDiscrim(colors); return { passCheck: !passCheck, data: passCheck || "" }; } buildMessage(): string { diff --git a/src/lib/lints/sequential-order.ts b/src/lib/lints/sequential-order.ts index 2e7ba3eb..35624182 100644 --- a/src/lib/lints/sequential-order.ts +++ b/src/lib/lints/sequential-order.ts @@ -7,7 +7,7 @@ const getLightness = (color: Color) => color.toColorIO().to("lab").coords[0]; export default class SequentialOrder extends ColorLint { name = "Sequential Palettes should be ordered by lightness"; taskTypes = ["sequential"] as TaskType[]; - _runCheck() { + async _runCheck() { const { colors } = this.palette; if (colors.length < 2) { return { passCheck: true, data: false }; diff --git a/src/lib/lints/size-discrim.ts b/src/lib/lints/size-discrim.ts index 20248e62..8a494716 100644 --- a/src/lib/lints/size-discrim.ts +++ b/src/lib/lints/size-discrim.ts @@ -89,7 +89,7 @@ const Discrims = ["thin", "medium", "wide"].map((key) => { > { name = `${key} Discriminability`; taskTypes = ["sequential", "diverging", "categorical"] as TaskType[]; - _runCheck() { + async _runCheck() { const jnds = checkJNDs(this.palette.colors); const passCheck = jnds.filter((x) => x[0] === key).length === 0; return { passCheck, data: jnds }; diff --git a/src/lib/lints/ugly-colors.ts b/src/lib/lints/ugly-colors.ts index c8e0286e..1f8d3390 100644 --- a/src/lib/lints/ugly-colors.ts +++ b/src/lib/lints/ugly-colors.ts @@ -21,7 +21,7 @@ export default class UglyColors extends ColorLint { name = "Palette does not have ugly colors"; taskTypes = ["sequential", "diverging", "categorical"] as TaskType[]; level: "error" | "warning" = "warning"; - _runCheck() { + async _runCheck() { const { colors } = this.palette; const data = checkIfAColorIsCloseToAnUglyColor(colors); const passCheck = data.length === 0;