diff --git a/apps/color-buddy/src/App.svelte b/apps/color-buddy/src/App.svelte index 775380e..26e4324 100644 --- a/apps/color-buddy/src/App.svelte +++ b/apps/color-buddy/src/App.svelte @@ -15,6 +15,50 @@ {}, $configStore.userName ); + + // check if there's a palette specified in the url + const url = new URL(window.location.href); + // const colors = url.searchParams.get("colors"); + // const background = url.searchParams.get("background") || "#ffffff"; + // const palName = url.searchParams.get("palName"); + // const space = url.searchParams.get("space"); + const pal = deserializePaletteForUrl(window.location.href); + if (pal) { + colorStore.createNewPal(pal); + url.searchParams.delete("colors"); + url.searchParams.delete("bg"); + url.searchParams.delete("name"); + url.searchParams.delete("space"); + window.history.replaceState({}, "", url.toString()); + } + // // } else + // if (colors) { + // console.log(colors, background, palName, space); + // let parsedColors = []; + // try { + // parsedColors = JSON.parse(colors || "[]"); + // } catch (e) { + // console.error(e); + // } + // const newPal = makePalFromString(parsedColors, background); + // if (palName) { + // newPal.name = palName; + // } + // if (space) { + // newPal.colorSpace = space as any; + // } + // if (parsedColors.length > 0) { + // console.log("here", newPal); + // colorStore.createNewPal(newPal); + // } + + // // remove the palette from the url + // url.searchParams.delete("colors"); + // url.searchParams.delete("background"); + // url.searchParams.delete("palName"); + // url.searchParams.delete("space"); + // window.history.replaceState({}, "", url.toString()); + // } }); // make sure no focused colors are out of bounds @@ -43,11 +87,13 @@ import TourProvider from "./content-modules/TourProvider.svelte"; import Config from "./controls/Config.svelte"; import Title from "./controls/Title.svelte"; + import SharePal from "./components/SharePal.svelte"; import { lint } from "./lib/api-calls"; import { debounce } from "vega"; import { buttonStyle } from "./lib/styles"; + import { deserializePaletteForUrl } from "./lib/utils"; $: route = $configStore.route; $: evalRoute = $configStore.evalDisplayMode; @@ -114,6 +160,7 @@ Redo + diff --git a/apps/color-buddy/src/components/SharePal.svelte b/apps/color-buddy/src/components/SharePal.svelte new file mode 100644 index 0000000..47ce966 --- /dev/null +++ b/apps/color-buddy/src/components/SharePal.svelte @@ -0,0 +1,44 @@ + + +
+ +
+
Share via url
+
+ {open && serializePaletteForUrl(palette)} +
+
+ + {#if copyState === "copied"} +
Copied!
+ {/if} +
+
+ + +
+
diff --git a/apps/color-buddy/src/components/Tooltip.svelte b/apps/color-buddy/src/components/Tooltip.svelte index 234102d..2c45718 100644 --- a/apps/color-buddy/src/components/Tooltip.svelte +++ b/apps/color-buddy/src/components/Tooltip.svelte @@ -129,7 +129,7 @@ }} > {/if} - + diff --git a/apps/color-buddy/src/lib/utils.ts b/apps/color-buddy/src/lib/utils.ts index 8723525..dd3e4ad 100644 --- a/apps/color-buddy/src/lib/utils.ts +++ b/apps/color-buddy/src/lib/utils.ts @@ -4,6 +4,7 @@ import { makePalFromString, } from "color-buddy-palette"; import type { Palette, StringPalette } from "color-buddy-palette"; +import queryString from "query-string"; import { Formatter, FracturedJsonOptions, EolStyle } from "fracturedjsonjs"; import fits from "../assets/outfits.json"; @@ -576,3 +577,36 @@ export function computeStroke( } return "none"; } + +export function serializePaletteForUrl(palette: Palette): string { + const colors = palette.colors.map((color) => color.toString()); + const bg = palette.background.toString(); + const name = palette.name; + const space = palette.colorSpace; + const params = { colors, bg, name, space }; + console.log(params); + const str = queryString.stringify(params); + const prefix = window.location.origin + window.location.pathname; + return `${prefix}?&${str}`; +} + +export function deserializePaletteForUrl(url: string): Palette | undefined { + const parsed = queryString.parse(url); + console.log(parsed); + const colorSpace = parsed.space as ColorSpace; + const colors = parsed.colors as string[]; + const bg = parsed.bg as string; + const name = parsed.name as string; + console.log(name, bg, colors, colorSpace); + if (!colorSpace || !colors || !bg || !name) { + return undefined; + } + try { + const newPal = makePalFromString(colors, bg); + newPal.name = name; + return convertPalToSpace(newPal, colorSpace); + } catch (e) { + console.log(e); + return undefined; + } +} diff --git a/packages/color-namer/main.ts b/packages/color-namer/main.ts index 0f5515c..9a907b8 100644 --- a/packages/color-namer/main.ts +++ b/packages/color-namer/main.ts @@ -33,7 +33,7 @@ export function nameColor( const numResults = props?.numResults ?? 1; const colors = props?.colors ?? heerStoneColors; const listName = props?.colorListName ?? "heerStone"; - const str = `${color.toString().toUpperCase()}-${numResults}-${listName}`; + const str = `${color.toHex().toUpperCase()}-${numResults}-${listName}`; if (nameCache.has(str)) { return nameCache.get(str)!; } diff --git a/packages/palette-lint/package.json b/packages/palette-lint/package.json index 2901136..a5c9daf 100644 --- a/packages/palette-lint/package.json +++ b/packages/palette-lint/package.json @@ -20,8 +20,8 @@ "rollup": "^4.18.0", "ts-json-schema-generator": "^1.5.0", "typescript": "^5.5.3", - "vite-plugin-dts": "^4.0.0-beta.1", "vite": "^5.2.0", + "vite-plugin-dts": "^4.0.0-beta.1", "vitest": "^1.6.0" }, "dependencies": { @@ -29,7 +29,8 @@ "color-buddy-color-namer": "*", "color-buddy-palette": "*", "fracturedjsonjs": "^4.0.1", - "ohm-js": "^17.1.0" + "ohm-js": "^17.1.0", + "query-string": "^9.1.1" }, "license": "BSD-3-Clause", "files": [ diff --git a/packages/palette/src/Color.ts b/packages/palette/src/Color.ts index 6045e8e..e9bc3df 100644 --- a/packages/palette/src/Color.ts +++ b/packages/palette/src/Color.ts @@ -11,6 +11,7 @@ type DistAlgorithm = "76" | "CMC" | "2000" | "ITP" | "Jz" | "OK"; const ColorIOCaches = new Map(); const InGamutCache = new Map(); const colorStateCache = new Map(); +const distCache = new Map(); /** * The base class for all color spaces @@ -178,16 +179,28 @@ export class Color { return val; } distance(color: Color, space: string): number { + const key = `distance-${this.toString()}-${color.toString()}-${space}`; + if (distCache.has(key)) { + return distCache.get(key)!; + } const colorSpace = space || this.spaceName; const targetSpace = space === "rgb" ? "srgb" : colorSpace; const left = this.toColorIO().to(targetSpace); const right = color.toColorIO().to(targetSpace); - return left.distance(right); + const val = left.distance(right); + distCache.set(key, val); + return val; } symmetricDeltaE(color: Color, algorithm: DistAlgorithm = "2000"): number { + const key = `${this.toString()}-${color.toString()}-${algorithm}`; + if (distCache.has(key)) { + return distCache.get(key)!; + } const left = this.deltaE(color, algorithm); const right = color.deltaE(this, algorithm); - return 0.5 * (left + right); + const val = 0.5 * (left + right); + distCache.set(key, val); + return val; } copy(): Color { return this.fromChannels(this.toChannels()); diff --git a/yarn.lock b/yarn.lock index b4d8eda..0b520f4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2314,11 +2314,6 @@ resolved "https://registry.npmjs.org/@types/color-namer/-/color-namer-1.3.3.tgz" integrity sha512-DJ0MHHqazwb94dUBRhahK49nXhRGk2DIRNrVNVR3U3uQjYWY4uNhEjHXO+TbFQGpZcwli1pCj6xm3S3KsB/+3A== -"@types/color-thief-node@^1.0.4": - version "1.0.4" - resolved "https://registry.yarnpkg.com/@types/color-thief-node/-/color-thief-node-1.0.4.tgz#33b250895de87ed7090dce44bee2b19a419acbb6" - integrity sha512-9mHoSyLAojvDQsZWxu3FHhiwUsQC7Lfuxrii6rnYc11uJ7GH2ODaM82gyDCXnR/5eXqlOymauUXU5eCEYAHS4A== - "@types/d3-array@*": version "3.2.1" resolved "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz" @@ -4653,6 +4648,11 @@ decimal.js@^10.4.3: resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz" integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== +decode-uri-component@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.4.1.tgz#2ac4859663c704be22bf7db760a1494a49ab2cc5" + integrity sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ== + decompress-response@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz" @@ -8754,6 +8754,15 @@ qs@^6.9.6: dependencies: side-channel "^1.0.6" +query-string@^9.1.1: + version "9.1.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-9.1.1.tgz#dbfebb4196aeb2919915f2b2b81b91b965cf03a0" + integrity sha512-MWkCOVIcJP9QSKU52Ngow6bsAWAPlPK2MludXvcrS2bGZSl+T1qX9MZvRIkqUIkGLJquMJHWfsT6eRqUpp4aWg== + dependencies: + decode-uri-component "^0.4.1" + filter-obj "^5.1.0" + split-on-first "^3.0.0" + querystringify@^2.1.1: version "2.2.0" resolved "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz" @@ -9536,6 +9545,11 @@ speakingurl@^14.0.1: resolved "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz" integrity sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ== +split-on-first@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-3.0.0.tgz#f04959c9ea8101b9b0bbf35a61b9ebea784a23e7" + integrity sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA== + split2@^1.0.0: version "1.1.1" resolved "https://registry.npmjs.org/split2/-/split2-1.1.1.tgz"