diff --git a/.changeset/fresh-ligers-matter.md b/.changeset/fresh-ligers-matter.md new file mode 100644 index 00000000..4362aca7 --- /dev/null +++ b/.changeset/fresh-ligers-matter.md @@ -0,0 +1,5 @@ +--- +"@tokens-studio/graph-engine": minor +--- + +Created the Match Alpha node which finds the alpha value that can be used to composite two colors to match a reference third color. diff --git a/packages/graph-engine/src/nodes/color/index.ts b/packages/graph-engine/src/nodes/color/index.ts index fabdca3c..10b97df5 100644 --- a/packages/graph-engine/src/nodes/color/index.ts +++ b/packages/graph-engine/src/nodes/color/index.ts @@ -10,6 +10,7 @@ import deltaE from './deltaE.js'; import distance from './distance.js'; import flattenAlpha from './flattenAlpha.js'; import lighten from './lighten.js'; +import matchAlpha from './matchAlpha.js'; import mix from './mix.js'; import name from './name.js'; import poline from './poline.js'; @@ -28,6 +29,7 @@ export const nodes = [ distance, deltaE, flattenAlpha, + matchAlpha, name, poline, scale, diff --git a/packages/graph-engine/src/nodes/color/lib/utils.ts b/packages/graph-engine/src/nodes/color/lib/utils.ts index 8d33fa6c..be35546e 100644 --- a/packages/graph-engine/src/nodes/color/lib/utils.ts +++ b/packages/graph-engine/src/nodes/color/lib/utils.ts @@ -31,6 +31,11 @@ export const White = { channels: [1, 1, 1] } as ColorType; +export const Gray = { + space: 'srgb', + channels: [0.5, 0.5, 0.5] +} as ColorType; + export const Red = { space: 'srgb', channels: [1, 0, 0] diff --git a/packages/graph-engine/src/nodes/color/matchAlpha.ts b/packages/graph-engine/src/nodes/color/matchAlpha.ts new file mode 100644 index 00000000..c5f6c1a9 --- /dev/null +++ b/packages/graph-engine/src/nodes/color/matchAlpha.ts @@ -0,0 +1,176 @@ +import { Black, Gray, White, toColor, toColorObject } from './lib/utils.js'; +import { + BooleanSchema, + ColorSchema, + NumberSchema +} from '../../schemas/index.js'; +import { Color as ColorType } from '../../types.js'; +import { INodeDefinition, ToInput, ToOutput } from '../../index.js'; +import { Node } from '../../programmatic/node.js'; +import { setToPrecision } from '@/utils/precision.js'; +import Color from 'colorjs.io'; + +export default class NodeDefinition extends Node { + static title = 'Match Alpha'; + static type = 'studio.tokens.color.matchAlpha'; + static description = + 'Finds the alpha value that, when used to blend the foreground and background colors, will result in the reference color.'; + + declare inputs: ToInput<{ + foreground: ColorType; + background: ColorType; + reference: ColorType; + threshold: number; + precision: number; + }>; + declare outputs: ToOutput<{ + inRange: boolean; + color: ColorType; + alpha: number; + }>; + + constructor(props: INodeDefinition) { + super(props); + + this.addInput('foreground', { + type: { + ...ColorSchema, + default: Black + } + }); + this.addInput('background', { + type: { + ...ColorSchema, + default: White + } + }); + this.addInput('reference', { + type: { + ...ColorSchema, + default: Gray + } + }); + this.addInput('threshold', { + type: { + ...NumberSchema, + default: 0.01 + } + }); + this.addInput('precision', { + type: { + ...NumberSchema, + default: 0.01 + } + }); + + this.addOutput('inRange', { + type: BooleanSchema + }); + this.addOutput('color', { + type: ColorSchema + }); + this.addOutput('alpha', { + type: NumberSchema + }); + } + + execute(): void | Promise { + const { foreground, background, reference, threshold, precision } = + this.getAllInputs(); + + const bg = toColor(background); + const fg = toColor(foreground); + const ref = toColor(reference); + + let alpha = Number.NaN; + let inRange = false; + + // the matching is done per channel + + // if the background and reference are "the same" (within precision), return zero + if ( + Math.abs(bg.r - ref.r) < precision && + Math.abs(bg.g - ref.g) < precision && + Math.abs(bg.b - ref.b) < precision + ) { + alpha = 0; + inRange = true; + } else { + // find the matching alpha for each channel + const ar = validateAlpha((ref.r - bg.r) / (fg.r - bg.r)); + const ag = validateAlpha((ref.g - bg.g) / (fg.g - bg.g)); + const ab = validateAlpha((ref.b - bg.b) / (fg.b - bg.b)); + + // return the average of the alphas for all matched channels + if (!isNaN(ar) && !isNaN(ag) && !isNaN(ab)) { + if ( + Math.abs(ar - ar) < precision && + Math.abs(ag - ag) < precision && + Math.abs(ab - ab) < precision + ) { + alpha = (ar + ag + ab) / 3; + inRange = true; + } + } else if (!isNaN(ar) && !isNaN(ag)) { + if (Math.abs(ar - ag) < precision) { + alpha = (ar + ag) / 2; + inRange = true; + } + } else if (!isNaN(ag) && !isNaN(ab)) { + if (Math.abs(ag - ab) < precision) { + alpha = (ag + ab) / 2; + inRange = true; + } + } else if (!isNaN(ar) && !isNaN(ab)) { + if (Math.abs(ar - ab) < precision) { + alpha = (ar + ab) / 2; + inRange = true; + } + } else { + alpha = ar || ag || ab; + inRange = !isNaN(alpha); + } + } + + // round the result to match the precision + alpha = setToPrecision( + alpha, + Math.max(0, -Math.floor(Math.log10(precision))) + ); + + // calculate the composite color, and if it's too far from the reference, return NaN + // deltaE() returns values typically ranging from 0 to 100, so I divide it by 100 + // to compare normalized colors + const comp = blendColors(fg, bg, alpha); + + if (comp.deltaE2000(ref) / 100 > threshold) { + alpha = Number.NaN; + inRange = false; + } + + // if the resulting alpha is valid, assign it to the foreground color, + // which becomes the output color + if (inRange) fg.alpha = alpha; + + this.outputs.inRange.set(inRange); + this.outputs.color.set(toColorObject(fg)); + this.outputs.alpha.set(alpha); + } +} + +function validateAlpha(alpha: number) { + // set valid but out-of-range alphas to NaN + return !isNaN(alpha) && alpha >= 0 && alpha <= 1 ? alpha : Number.NaN; +} + +function blendChannels(fg: number, bg: number, alpha: number) { + return fg * alpha + bg * (1 - alpha); +} + +function blendColors(fg: Color, bg: Color, alpha: number) { + return new Color('srgb', [ + blendChannels(fg.r, bg.r, alpha), + blendChannels(fg.g, bg.g, alpha), + blendChannels(fg.b, bg.b, alpha) + ]); +} diff --git a/packages/graph-engine/tests/suites/nodes/color/matchAlpha.test.ts b/packages/graph-engine/tests/suites/nodes/color/matchAlpha.test.ts new file mode 100644 index 00000000..600d3cb7 --- /dev/null +++ b/packages/graph-engine/tests/suites/nodes/color/matchAlpha.test.ts @@ -0,0 +1,103 @@ +import { Graph } from '../../../../src/graph/graph.js'; +import { describe, expect, test } from 'vitest'; +import { getAllOutputs } from '../../../../src/utils/node.js'; +import Color from 'colorjs.io'; +import Node from '../../../../src/nodes/color/matchAlpha.js'; + +type number3 = [number, number, number]; + +describe('color/matchAlpha', () => { + test('all colors are valid and a result that makes sense can be found', async () => { + const graph = new Graph(); + const node = new Node({ graph }); + + const fg: number3 = [0.96, 0, 0]; + const bg: number3 = [0, 0, 0]; + const ref: number3 = [0.48, 0, 0]; + + node.inputs.foreground.setValue({ space: 'srgb', channels: fg }); + node.inputs.background.setValue({ space: 'srgb', channels: bg }); + node.inputs.reference.setValue({ space: 'srgb', channels: ref }); + + await node.run(); + + const output = getAllOutputs(node); + + const expAlpha = 0.5; + const expColor = { space: 'srgb', channels: fg, alpha: expAlpha }; + + expect(output.inRange).to.equal(true); + expect(output.color as Color).to.deep.equal(expColor); + expect(output.alpha).to.equal(expAlpha); + }); + + test('the hues of fg and bg are too different and no result makes sense', async () => { + const graph = new Graph(); + const node = new Node({ graph }); + + const fg: number3 = [0.96, 0, 0]; + const bg: number3 = [0, 0.2, 0]; + const ref: number3 = [0.48, 0, 0]; + + node.inputs.foreground.setValue({ space: 'srgb', channels: fg }); + node.inputs.background.setValue({ space: 'srgb', channels: bg }); + node.inputs.reference.setValue({ space: 'srgb', channels: ref }); + + await node.run(); + + const output = getAllOutputs(node); + + const expColor = { space: 'srgb', channels: fg, alpha: 1 }; + + expect(output.inRange).to.equal(false); + expect(output.color as Color).to.deep.equal(expColor); + expect(Number.isNaN(output.alpha)).toEqual(true); + }); + + test('bg and ref are the same (within threshold)', async () => { + const graph = new Graph(); + const node = new Node({ graph }); + + const fg: number3 = [0.96, 0.96, 0]; + const bg: number3 = [0.33, 0.33, 0]; + const ref: number3 = [0.325, 0.335, 0]; + + node.inputs.foreground.setValue({ space: 'srgb', channels: fg }); + node.inputs.background.setValue({ space: 'srgb', channels: bg }); + node.inputs.reference.setValue({ space: 'srgb', channels: ref }); + + await node.run(); + + const output = getAllOutputs(node); + + const expAlpha = 0; + const expColor = { space: 'srgb', channels: fg, alpha: expAlpha }; + + expect(output.inRange).to.equal(true); + expect(output.color as Color).to.deep.equal(expColor); + expect(output.alpha).to.equal(expAlpha); + }); + + test('ref is further from bg than fg, so the result is outside the valid alpha range (0-1)', async () => { + const graph = new Graph(); + const node = new Node({ graph }); + + const fg: number3 = [0, 0.5, 0.5]; + const bg: number3 = [0, 0, 0]; + const ref: number3 = [0, 1, 1]; + + node.inputs.foreground.setValue({ space: 'srgb', channels: fg }); + node.inputs.background.setValue({ space: 'srgb', channels: bg }); + node.inputs.reference.setValue({ space: 'srgb', channels: ref }); + + await node.run(); + + const output = getAllOutputs(node); + + const expColor = { space: 'srgb', channels: fg, alpha: 1 }; + + expect(output.inRange).to.equal(false); + expect(output.color as Color).to.deep.equal(expColor); + expect(Number.isNaN(output.alpha)).toEqual(true); + }); +}); diff --git a/packages/graph-engine/vitest.config.ts.timestamp-1732108255012-05584d17095ba.mjs b/packages/graph-engine/vitest.config.ts.timestamp-1732108255012-05584d17095ba.mjs new file mode 100644 index 00000000..0d463859 --- /dev/null +++ b/packages/graph-engine/vitest.config.ts.timestamp-1732108255012-05584d17095ba.mjs @@ -0,0 +1,13 @@ +// vitest.config.ts +import { defineConfig } from "file:///C:/Users/Alex%20(Hyma)/Documents/GitHub/graph-engine/packages/graph-engine/node_modules/vite/dist/node/index.js"; +import tsconfigPaths from "file:///C:/Users/Alex%20(Hyma)/Documents/GitHub/graph-engine/node_modules/vite-tsconfig-paths/dist/index.mjs"; +var vitest_config_default = defineConfig({ + test: { + // ... Specify options here. + }, + plugins: [tsconfigPaths()] +}); +export { + vitest_config_default as default +}; +//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZXN0LmNvbmZpZy50cyJdLAogICJzb3VyY2VzQ29udGVudCI6IFsiY29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2Rpcm5hbWUgPSBcIkM6XFxcXFVzZXJzXFxcXEFsZXggKEh5bWEpXFxcXERvY3VtZW50c1xcXFxHaXRIdWJcXFxcZ3JhcGgtZW5naW5lXFxcXHBhY2thZ2VzXFxcXGdyYXBoLWVuZ2luZVwiO2NvbnN0IF9fdml0ZV9pbmplY3RlZF9vcmlnaW5hbF9maWxlbmFtZSA9IFwiQzpcXFxcVXNlcnNcXFxcQWxleCAoSHltYSlcXFxcRG9jdW1lbnRzXFxcXEdpdEh1YlxcXFxncmFwaC1lbmdpbmVcXFxccGFja2FnZXNcXFxcZ3JhcGgtZW5naW5lXFxcXHZpdGVzdC5jb25maWcudHNcIjtjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfaW1wb3J0X21ldGFfdXJsID0gXCJmaWxlOi8vL0M6L1VzZXJzL0FsZXglMjAoSHltYSkvRG9jdW1lbnRzL0dpdEh1Yi9ncmFwaC1lbmdpbmUvcGFja2FnZXMvZ3JhcGgtZW5naW5lL3ZpdGVzdC5jb25maWcudHNcIjsvLy8gPHJlZmVyZW5jZSB0eXBlcz1cInZpdGVzdFwiIC8+XG5pbXBvcnQgeyBkZWZpbmVDb25maWcgfSBmcm9tICd2aXRlJztcbmltcG9ydCB0c2NvbmZpZ1BhdGhzIGZyb20gJ3ZpdGUtdHNjb25maWctcGF0aHMnO1xuXG5leHBvcnQgZGVmYXVsdCBkZWZpbmVDb25maWcoe1xuXHR0ZXN0OiB7XG5cdFx0Ly8gLi4uIFNwZWNpZnkgb3B0aW9ucyBoZXJlLlxuXHR9LFxuXHRwbHVnaW5zOiBbdHNjb25maWdQYXRocygpXVxufSk7XG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQ0EsU0FBUyxvQkFBb0I7QUFDN0IsT0FBTyxtQkFBbUI7QUFFMUIsSUFBTyx3QkFBUSxhQUFhO0FBQUEsRUFDM0IsTUFBTTtBQUFBO0FBQUEsRUFFTjtBQUFBLEVBQ0EsU0FBUyxDQUFDLGNBQWMsQ0FBQztBQUMxQixDQUFDOyIsCiAgIm5hbWVzIjogW10KfQo= diff --git a/yarn.lock b/yarn.lock index d4fdef31..bd3a6a89 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4901,25 +4901,11 @@ "@react-hook/passive-layout-effect" "^1.2.0" intersection-observer "^0.10.0" -"@react-hook/latest@^1.0.2": - version "1.0.3" - resolved "https://registry.yarnpkg.com/@react-hook/latest/-/latest-1.0.3.tgz#c2d1d0b0af8b69ec6e2b3a2412ba0768ac82db80" - integrity sha512-dy6duzl+JnAZcDbNTfmaP3xHiKtbXYOaz3G51MGVljh548Y8MWzTr+PHLOfvpypEVW9zwvl+VyKjbWKEVbV1Rg== - "@react-hook/passive-layout-effect@^1.2.0": version "1.2.1" resolved "https://registry.yarnpkg.com/@react-hook/passive-layout-effect/-/passive-layout-effect-1.2.1.tgz#c06dac2d011f36d61259aa1c6df4f0d5e28bc55e" integrity sha512-IwEphTD75liO8g+6taS+4oqz+nnroocNfWVHWz7j+N+ZO2vYrc6PV1q7GQhuahL0IOR7JccFTsFKQ/mb6iZWAg== -"@react-hook/resize-observer@^1.2.6": - version "1.2.6" - resolved "https://registry.yarnpkg.com/@react-hook/resize-observer/-/resize-observer-1.2.6.tgz#9a8cf4c5abb09becd60d1d65f6bf10eec211e291" - integrity sha512-DlBXtLSW0DqYYTW3Ft1/GQFZlTdKY5VAFIC4+km6IK5NiPPDFchGbEJm1j6pSgMqPRHbUQgHJX7RaR76ic1LWA== - dependencies: - "@juggle/resize-observer" "^3.3.1" - "@react-hook/latest" "^1.0.2" - "@react-hook/passive-layout-effect" "^1.2.0" - "@reactflow/background@11.3.14": version "11.3.14" resolved "https://registry.yarnpkg.com/@reactflow/background/-/background-11.3.14.tgz#778ca30174f3de77fc321459ab3789e66e71a699" @@ -6482,14 +6468,6 @@ "@swc/counter" "^0.1.3" tslib "^2.4.0" -"@swc/helpers@^0.4.14": - version "0.4.36" - resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.36.tgz#fcfff76ed52c214f357e8e9d3f37b568908072d9" - integrity sha512-5lxnyLEYFskErRPenYItLRSge5DjrJngYKdVjRSrWfza9G6KkgHEXi0vUZiyUeMU5JfXH1YnvXZzSp8ul88o2Q== - dependencies: - legacy-swc-helpers "npm:@swc/helpers@=0.4.14" - tslib "^2.4.0" - "@swc/types@^0.1.9": version "0.1.9" resolved "https://registry.yarnpkg.com/@swc/types/-/types-0.1.9.tgz#e67cdcc2e4dd74a3cef4474b465eb398e7ae83e2" @@ -7677,7 +7655,7 @@ resolved "https://registry.yarnpkg.com/@use-gesture/core/-/core-10.3.1.tgz#976c9421e905f0079d49822cfd5c2e56b808fc56" integrity sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw== -"@use-gesture/react@^10.2.23": +"@use-gesture/react@^10.2.27": version "10.3.1" resolved "https://registry.yarnpkg.com/@use-gesture/react/-/react-10.3.1.tgz#17a743a894d9bd9a0d1980c618f37f0164469867" integrity sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g== @@ -8886,13 +8864,6 @@ better-path-resolve@1.0.0: dependencies: is-windows "^1.0.0" -better-react-mathjax@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/better-react-mathjax/-/better-react-mathjax-2.0.3.tgz#202dc6fe5c7263278f2491516f43f70ba188122f" - integrity sha512-wfifT8GFOKb1TWm2+E50I6DJpLZ5kLbch283Lu043EJtwSv0XvZDjr4YfR4d2MjAhqP6SH4VjjrKgbX8R00oCQ== - dependencies: - mathjax-full "^3.2.2" - big-integer@^1.6.44: version "1.6.52" resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.52.tgz#60a887f3047614a8e1bffe5d7173490a97dc8c85" @@ -9926,11 +9897,6 @@ commander@7, commander@^7.2.0: resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== -commander@9.2.0: - version "9.2.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-9.2.0.tgz#6e21014b2ed90d8b7c9647230d8b7a94a4a419a9" - integrity sha512-e2i4wANQiSXgnrBlIatyHtP1odfUp0BbV5Y5nEGbxtIrStkEOAAzCUirvLBNXHLr7kwLvJl6V+4V3XV9x7Wd9w== - commander@^10.0.0: version "10.0.1" resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" @@ -12277,11 +12243,6 @@ eslint@^8.57.0: strip-ansi "^6.0.1" text-table "^0.2.0" -esm@^3.2.25: - version "3.2.25" - resolved "https://registry.yarnpkg.com/esm/-/esm-3.2.25.tgz#342c18c29d56157688ba5ce31f8431fbb795cc10" - integrity sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== - esniff@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/esniff/-/esniff-2.0.1.tgz#a4d4b43a5c71c7ec51c51098c1d8a29081f9b308" @@ -16281,7 +16242,14 @@ just-extend@^4.0.2: resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.2.1.tgz#ef5e589afb61e5d66b24eca749409a8939a8c744" integrity sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg== -katex@^0.16.4, katex@^0.16.9: +katex@^0.16: + version "0.16.11" + resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.11.tgz#4bc84d5584f996abece5f01c6ad11304276a33f5" + integrity sha512-RQrI8rlHY92OLf3rho/Ts8i/XvjgguEjOkO1BEXcU3N8BqPpSzBNwV/G0Ukr+P/l3ivvJUE/Fa/CwbS6HesGNQ== + dependencies: + commander "^8.3.0" + +katex@^0.16.9: version "0.16.10" resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.10.tgz#6f81b71ac37ff4ec7556861160f53bc5f058b185" integrity sha512-ZiqaC04tp2O5utMsl2TEZTXxa6WSC4yo0fv5ML++D3QZv/vx2Mct0mTlRx3O+uUkjfuAgOkzsCmq5MiUEsDDdA== @@ -16377,13 +16345,6 @@ lazy-universal-dotenv@^4.0.0: dotenv "^16.0.0" dotenv-expand "^10.0.0" -"legacy-swc-helpers@npm:@swc/helpers@=0.4.14": - version "0.4.14" - resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.14.tgz#1352ac6d95e3617ccb7c1498ff019654f1e12a74" - integrity sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw== - dependencies: - tslib "^2.4.0" - leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" @@ -16784,18 +16745,16 @@ lz-string@^1.4.4, lz-string@^1.5.0: resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== -mafs@^0.17.1: - version "0.17.1" - resolved "https://registry.yarnpkg.com/mafs/-/mafs-0.17.1.tgz#86341daba19b38c306a5d60306bf9005fe93a78c" - integrity sha512-pMUeBUHLwjp4N/4oFkcPrlhP6uncIylZAaUa4OgEql8R2u5WdJ76/MishoPahakSCFYoyKVBJYI7i5c2/Y7nMw== +mafs@^0.20.0: + version "0.20.1" + resolved "https://registry.yarnpkg.com/mafs/-/mafs-0.20.1.tgz#d447f6ab05df03c901982c18dd9ef3f1663cfced" + integrity sha512-EOgHHsVxWq7oGmdUJhZJ+Fr3b37xP1i3EBWuQahy/3ZOiOo2xvxLd6KChF1Lf00b6xRvOFyixuHpWexBE8uffA== dependencies: - "@react-hook/resize-observer" "^1.2.6" - "@swc/helpers" "^0.4.14" - "@use-gesture/react" "^10.2.23" + "@use-gesture/react" "^10.2.27" computer-modern "^0.1.2" - katex "^0.16.4" + katex "^0.16" tiny-invariant "^1.3.1" - use-resize-observer "^9.0.0" + use-resize-observer "^9" magic-string@^0.27.0: version "0.27.0" @@ -16909,16 +16868,6 @@ marked@^4.0.15, marked@^4.3.0: resolved "https://registry.yarnpkg.com/marked/-/marked-4.3.0.tgz#796362821b019f734054582038b116481b456cf3" integrity sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A== -mathjax-full@^3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/mathjax-full/-/mathjax-full-3.2.2.tgz#43f02e55219db393030985d2b6537ceae82f1fa7" - integrity sha512-+LfG9Fik+OuI8SLwsiR02IVdjcnRCy5MufYLi0C3TdMT56L/pjB0alMVGgoWJF8pN9Rc7FESycZB9BMNWIid5w== - dependencies: - esm "^3.2.25" - mhchemparser "^4.1.0" - mj-context-menu "^0.6.1" - speech-rule-engine "^4.0.6" - mdast-util-directive@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/mdast-util-directive/-/mdast-util-directive-3.0.0.tgz#3fb1764e705bbdf0afb0d3f889e4404c3e82561f" @@ -17355,11 +17304,6 @@ methods@~1.1.2: resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== -mhchemparser@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/mhchemparser/-/mhchemparser-4.2.1.tgz#d73982e66bc06170a85b1985600ee9dabe157cb0" - integrity sha512-kYmyrCirqJf3zZ9t/0wGgRZ4/ZJw//VwaRVGA75C4nhE60vtnIzhl9J9ndkX/h6hxSN7pjg/cE0VxbnNM+bnDQ== - micromark-core-commonmark@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-1.1.0.tgz#1386628df59946b2d39fb2edfd10f3e8e0a75bb8" @@ -18201,11 +18145,6 @@ mixpanel-browser@2.52.0: dependencies: rrweb "2.0.0-alpha.13" -mj-context-menu@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/mj-context-menu/-/mj-context-menu-0.6.1.tgz#a043c5282bf7e1cf3821de07b13525ca6f85aa69" - integrity sha512-7NO5s6n10TIV96d4g2uDpG7ZDpIhMh0QNfGdJw/W47JswFcosz457wqz/b5sAKvl12sxINGFCn80NZHKwxQEXA== - mkdirp-classic@^0.5.2, mkdirp-classic@^0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" @@ -20666,7 +20605,7 @@ prepend-http@^1.0.1: resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" integrity sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg== -"prettier-fallback@npm:prettier@^3", prettier@^3.1.1, prettier@^3.3.2: +"prettier-fallback@npm:prettier@^3": version "3.3.2" resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.2.tgz#03ff86dc7c835f2d2559ee76876a3914cec4a90a" integrity sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA== @@ -20688,6 +20627,11 @@ prettier@^2.7.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== +prettier@^3.1.1, prettier@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.3.2.tgz#03ff86dc7c835f2d2559ee76876a3914cec4a90a" + integrity sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA== + pretty-bytes@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-4.0.2.tgz#b2bf82e7350d65c6c33aa95aaa5a4f6327f61cd9" @@ -23399,15 +23343,6 @@ spdy@^4.0.2: select-hose "^2.0.0" spdy-transport "^3.0.0" -speech-rule-engine@^4.0.6: - version "4.0.7" - resolved "https://registry.yarnpkg.com/speech-rule-engine/-/speech-rule-engine-4.0.7.tgz#b655dacbad3dae04acc0f7665e26ef258397dd09" - integrity sha512-sJrL3/wHzNwJRLBdf6CjJWIlxC04iYKkyXvYSVsWVOiC2DSkHmxsqOhEeMsBA9XK+CHuNcsdkbFDnoUfAsmp9g== - dependencies: - commander "9.2.0" - wicked-good-xpath "1.3.0" - xmldom-sre "0.1.31" - split-string@^3.0.1, split-string@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" @@ -23633,7 +23568,7 @@ string-to-arraybuffer@^1.0.0: atob-lite "^2.0.0" is-base64 "^0.1.0" -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -23651,6 +23586,15 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^2.0.0, string-width@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" @@ -23764,7 +23708,7 @@ stringify-object@^3.3.0: is-obj "^1.0.1" is-regexp "^1.0.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -23785,6 +23729,13 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -25185,7 +25136,7 @@ use-callback-ref@^1.3.0: dependencies: tslib "^2.0.0" -use-resize-observer@^9.0.0: +use-resize-observer@^9: version "9.1.0" resolved "https://registry.yarnpkg.com/use-resize-observer/-/use-resize-observer-9.1.0.tgz#14735235cf3268569c1ea468f8a90c5789fc5c6c" integrity sha512-R25VqO9Wb3asSD4eqtcxk8sJalvIOYBqS8MNZlpDSQ4l4xMQxC/J7Id9HoTqPq8FwULIn0PVW+OAqF2dyYbjow== @@ -25858,11 +25809,6 @@ why-is-node-running@^2.2.2: siginfo "^2.0.0" stackback "0.0.2" -wicked-good-xpath@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/wicked-good-xpath/-/wicked-good-xpath-1.3.0.tgz#81b0e95e8650e49c94b22298fff8686b5553cf6c" - integrity sha512-Gd9+TUn5nXdwj/hFsPVx5cuHHiF5Bwuc30jZ4+ronF1qHK5O7HD0sgmXWSEgwKquT3ClLoKPVbO6qGwVwLzvAw== - wide-align@^1.1.0: version "1.1.5" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" @@ -25906,7 +25852,7 @@ workerpool@^6.5.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -25932,6 +25878,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.0.1, wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" @@ -26023,11 +25978,6 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== -xmldom-sre@0.1.31: - version "0.1.31" - resolved "https://registry.yarnpkg.com/xmldom-sre/-/xmldom-sre-0.1.31.tgz#10860d5bab2c603144597d04bf2c4980e98067f4" - integrity sha512-f9s+fUkX04BxQf+7mMWAp5zk61pciie+fFLC9hX9UVvCeJQfNHRHXpeo5MPcR0EUf57PYLdt+ZO4f3Ipk2oZUw== - xtend@^4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"