From e6f7cd6eb68ee480f99a8fddd465e132a29e8cf2 Mon Sep 17 00:00:00 2001 From: Andrew McNutt Date: Thu, 15 Feb 2024 10:08:42 -0800 Subject: [PATCH] Affects (#17) * some refactoring to make pal manipulation easier * pretty much full affect story * messing around trying to make everything feel tighter * . * fix test --- LintLanguageDocs.md | 10 +- README.md | 5 +- src/components/Tooltip.svelte | 4 +- src/content-modules/MainColumn.svelte | 37 +---- src/controls/Config.svelte | 2 + src/controls/NewPal.svelte | 20 +-- src/controls/PalTypeConfig.svelte | 116 +++++++++++++ src/controls/SetColorSpace.svelte | 2 - src/controls/SuggestColorPal.svelte | 4 +- .../SuggestionModificationToSelection.svelte | 16 +- src/example/Examples.svelte | 21 +-- src/lib/ColorLint.test.ts | 64 +++---- src/lib/ColorLint.ts | 6 +- src/lib/CustomLint.ts | 18 +- src/lib/__snapshots__/ColorLint.test.ts.snap | 2 +- src/lib/api-calls.ts | 11 +- src/lib/lint-language/LintLanguage.test.ts | 2 + src/lib/linter-tools/lint-fixer.ts | 3 +- src/lib/linter-tools/lint-worker.worker.ts | 4 + src/lib/linter.ts | 45 +++-- src/lib/lints/affects.ts | 157 ++++++++++++++++++ src/lib/lints/color-blindness.ts | 10 +- src/lib/lints/diverging-order.ts | 39 ++++- src/lib/lints/even-distribution.ts | 4 +- src/lib/lints/fair.ts | 1 - src/lib/lints/sequential-order.ts | 2 +- src/lib/utils.ts | 71 +++++++- src/linting/Eval.svelte | 8 +- src/linting/EvalResponse.svelte | 56 +++++-- src/linting/LintDisplay.svelte | 25 +-- src/linting/NewLintSuggestion.svelte | 4 +- src/stores/color-store.ts | 31 ++-- src/stores/lint-store.ts | 4 +- src/types.ts | 27 +++ 34 files changed, 608 insertions(+), 223 deletions(-) create mode 100644 src/controls/PalTypeConfig.svelte create mode 100644 src/lib/lints/affects.ts diff --git a/LintLanguageDocs.md b/LintLanguageDocs.md index 4228034e..3bd0ecd4 100644 --- a/LintLanguageDocs.md +++ b/LintLanguageDocs.md @@ -20,11 +20,11 @@ Comparisons: {">": {left: Value, right: Value}} Math Operations: -\*: {left: Number | Variable, right: Number | Variable} -+: {left: Number | Variable, right: Number | Variable} -/: {left: Number | Variable, right: Number | Variable} --: {left: Number | Variable, right: Number | Variable} -absDiff: {left: Number | Variable, right: Number | Variable} +{"\*": {left: Number | Variable, right: Number | Variable}} +{"+": {left: Number | Variable, right: Number | Variable}} +{"/": {left: Number | Variable, right: Number | Variable}} +{"-": {left: Number | Variable, right: Number | Variable}} +{absDiff: {left: Number | Variable, right: Number | Variable}} Value Comparisons: {dist: {left: Color | Variable, right: Color | Variable}, space: COLOR_SPACE } diff --git a/README.md b/README.md index 3eebc453..68dbffdd 100644 --- a/README.md +++ b/README.md @@ -13,20 +13,21 @@ First time you start it up you should also run `yarn prep data` # User study burn down - [ ] Tour? -- [ ] roles, palette level semantics +- [ ] Color Roles - [ ] Design adjustments for smaller screens - [ ] Language Docs?? +- [x] palette level semantics - [x] Get most of the lints converted - [x] Make lints fast / non blocking as much as possible # Language todos -- [ ] Affect rules - [ ] Add more blame robustness, may pay to try to reason across all of the operator families (insight: keep a list of the blamable variables in the environment to support tracing) - [ ] per cols 4 all: color blindness metric should maybe be sensitive to task? - [ ] Sequential check fix is incorrect for things with equi-ligthness - [ ] Macros story: "not similar", "sequences", "where": { "!=": {"left": "index(a)", "right": "index(b)"} }, - [ ] More crashy type validation +- [x] Affect rules - [x] "No out of gamut" # General Todos diff --git a/src/components/Tooltip.svelte b/src/components/Tooltip.svelte index 5ca55dbb..2564c6b9 100644 --- a/src/components/Tooltip.svelte +++ b/src/components/Tooltip.svelte @@ -55,8 +55,8 @@ : "0"; $: leftString = boundingBox ? `${boundingBox.x}px` : "0"; $: { - if (boundingBox.y + 300 > window.screen.height) { - topString = `${window.screen.height - 300}px`; + if (boundingBox.y + 500 > window.screen.height) { + topString = `${window.screen.height - 500}px`; } } $: { diff --git a/src/content-modules/MainColumn.svelte b/src/content-modules/MainColumn.svelte index c18d9416..e73e2cf2 100644 --- a/src/content-modules/MainColumn.svelte +++ b/src/content-modules/MainColumn.svelte @@ -2,12 +2,14 @@ import colorStore from "../stores/color-store"; import focusStore from "../stores/focus-store"; import configStore from "../stores/config-store"; + import { buttonStyle } from "../lib/styles"; import AdjustOrder from "../controls/AdjustOrder.svelte"; import Background from "../components/Background.svelte"; import ColorScatterPlot from "../scatterplot/ColorScatterPlot.svelte"; import ExampleAlaCart from "../example/ExampleAlaCarte.svelte"; - import GetColorsFromString from "../controls/GetColorsFromString.svelte"; + import PalTypeConfig from "../controls/PalTypeConfig.svelte"; + import ModifySelection from "../controls/ModifySelection.svelte"; import Nav from "../components/Nav.svelte"; import PalPreview from "../components/PalPreview.svelte"; @@ -17,16 +19,6 @@ import ContentEditable from "../components/ContentEditable.svelte"; $: currentPal = $colorStore.palettes[$colorStore.currentPal]; - - const descriptions = { - sequential: - "Sequential palettes are used to represent a range of values. They are often used to represent quantitative data, such as temperature or elevation.", - diverging: - "Diverging palettes are used to represent a range of values around a central point. They are often used to represent quantitative data, such as temperature or elevation.", - categorical: - "Categorical palettes are used to represent a set of discrete values. They are often used to represent qualitative data, such as different types of land cover or different political parties.", - }; - $: palType = currentPal.type; $: selectedBlindType = $configStore.colorSim; @@ -100,28 +92,7 @@ }} /> {#if $configStore.mainColumnRoute === "palette-config"} - colorStore.setCurrentPalColors(colors)} - colorSpace={currentPal.colorSpace} - colors={currentPal.colors} - /> - -
- This is a - palette. {descriptions[palType]} -
+ {/if} {#if $configStore.mainColumnRoute === "example"} import { Color } from "../lib/Color"; - import colorStore, { newGenericPal } from "../stores/color-store"; + import colorStore from "../stores/color-store"; import type { StringPalette, Palette } from "../types"; import focusStore from "../stores/focus-store"; import { onMount } from "svelte"; import Tooltip from "../components/Tooltip.svelte"; import { VegaColors } from "../lib/charts"; import { buttonStyle, denseButtonStyle } from "../lib/styles"; - import { makePal, toHex } from "../lib/utils"; + import { + makePal, + toHex, + newGenericPal, + createPalFromHexes, + } from "../lib/utils"; import type { ExtendedPal } from "../lib/utils"; import SuggestColorPal from "./SuggestColorPal.svelte"; @@ -16,17 +21,6 @@ $: currentPal = $colorStore.palettes[$colorStore.currentPal]; $: colorSpace = currentPal.colorSpace; - function createPalFromHexes(colors: string[]): StringPalette { - return { - name: "new palette", - colors, - background: "#ffffff", - type: "categorical", - evalConfig: {}, - colorSpace: "lab", - }; - } - onMount(async () => { let newPals = [] as ExtendedPal[]; diff --git a/src/controls/PalTypeConfig.svelte b/src/controls/PalTypeConfig.svelte new file mode 100644 index 00000000..d4387aeb --- /dev/null +++ b/src/controls/PalTypeConfig.svelte @@ -0,0 +1,116 @@ + + +
+ colorStore.setCurrentPalColors(colors)} + colorSpace={currentPal.colorSpace} + colors={currentPal.colors} + /> + +
Config
+
+ This is a + palette. {descriptions[palType]} +
+
+ What types of affects do you intend to have on the palette? +
+
+ + {#each affects as affect} +
+ +
+ {/each} +
+ +
What types of contexts do you intend to use?
+
+ + {#each contexts as context} +
+ +
+ {/each} +
+
diff --git a/src/controls/SetColorSpace.svelte b/src/controls/SetColorSpace.svelte index 58a7ea44..70c00458 100644 --- a/src/controls/SetColorSpace.svelte +++ b/src/controls/SetColorSpace.svelte @@ -2,13 +2,11 @@ import { colorPickerConfig } from "../lib/Color"; import Tooltip from "../components/Tooltip.svelte"; import { buttonStyle } from "../lib/styles"; - // $: colorSpace = $colorStore.currentPal.colorSpace; export let colorSpace: string; export let onChange: (e: any) => void; // const notAllowed = new Set(["rgb", "hsv", "hsl", "srgb", "lch", "oklch"]); // const notAllowed = new Set(["rgb", "lch", "oklch", "srgb"]); const notAllowed = new Set(["rgb", "oklch", "srgb", "jzazbz", "oklab"]); - // const onChange = (e: any) => colorStore.setColorSpace(e); $: options = Object.keys(colorPickerConfig) .filter((x) => !notAllowed.has(x)) .sort(); diff --git a/src/controls/SuggestColorPal.svelte b/src/controls/SuggestColorPal.svelte index e5fa7a6b..7bfbcc4b 100644 --- a/src/controls/SuggestColorPal.svelte +++ b/src/controls/SuggestColorPal.svelte @@ -5,7 +5,7 @@ import { suggestPal } from "../lib/api-calls"; import type { Palette } from "../types"; import PalPreview from "../components/PalPreview.svelte"; - import { buttonStyle, AIButtonStyle } from "../lib/styles"; + import { buttonStyle } from "../lib/styles"; $: currentPal = $colorStore.palettes[$colorStore.currentPal]; $: colorSpace = currentPal.colorSpace; @@ -29,6 +29,8 @@ type: "categorical", evalConfig: {}, colorSpace: colorSpace as any, + intendedAffects: [], + intendedContexts: [], }; } diff --git a/src/controls/SuggestionModificationToSelection.svelte b/src/controls/SuggestionModificationToSelection.svelte index ca97c274..4bcd6fa0 100644 --- a/src/controls/SuggestionModificationToSelection.svelte +++ b/src/controls/SuggestionModificationToSelection.svelte @@ -6,6 +6,7 @@ import { suggestContextualAdjustments } from "../lib/api-calls"; import { buttonStyle } from "../lib/styles"; import PalDiff from "../components/PalDiff.svelte"; + import { toPal } from "../lib/utils"; let requestState: "idle" | "loading" | "loaded" | "failed" = "idle"; $: currentPal = $colorStore.palettes[$colorStore.currentPal]; @@ -17,17 +18,6 @@ let suggestedColorSets: string[][] = []; let palPrompt: string = ""; - function toPal(colors: string[]) { - return { - colors: colors.map((x) => Color.colorFromString(x, colorSpace)), - name: "mods", - background: currentPal.background, - type: currentPal.type, - evalConfig: {}, - colorSpace, - }; - } - function makeRequest() { requestState = "loading"; const pal = selectedColors.length @@ -100,9 +90,9 @@
diff --git a/src/example/Examples.svelte b/src/example/Examples.svelte index 46c5cd47..e131a269 100644 --- a/src/example/Examples.svelte +++ b/src/example/Examples.svelte @@ -1,6 +1,5 @@
{check.name}
- {#if check.passes} + {#if check.passes || ignored}
{check.description}
{:else} {/if}
Actions
+ {#if check.isCustom} + + {/if} {#if cbMatch} + {#if !ignored} + + {:else} + + {/if} {#if requestState === "loading"}
Loading...
@@ -110,9 +136,9 @@
Failed to generate suggestions
{:else if requestState === "loaded"} {#each suggestions as suggestion} -
+
-
+
- {#if check.isCustom} - - {/if}
{:else}
@@ -67,17 +57,6 @@
--> - {#if check.isCustom} - - {/if}
{#if !check.passes && !isCompact} diff --git a/src/linting/NewLintSuggestion.svelte b/src/linting/NewLintSuggestion.svelte index 3b082dae..527e8a73 100644 --- a/src/linting/NewLintSuggestion.svelte +++ b/src/linting/NewLintSuggestion.svelte @@ -45,8 +45,8 @@ } - -
+ +
What would you like your lint to be able to do?
[x.fill1, x.fill2, x.fill3]; -const outfits = fits.map((x) => outfitToPal(x)); -import type { Palette, StringPalette, PalType, ColorSpace } from "../types"; +import { deDup, newGenericPal } from "../lib/utils"; + +import type { + Palette, + StringPalette, + PalType, + ColorSpace, + Context, + Affect, +} from "../types"; interface StoreData { palettes: Palette[]; @@ -16,17 +21,6 @@ interface StorageData { currentPal: number; } -export function newGenericPal(name: string): StringPalette { - return { - name, - colors: pick(outfits), - background: "#ffffff", - type: "categorical", - evalConfig: {}, - colorSpace: "lab", - }; -} - function stringPalToColorPal(pal: StringPalette): Palette { return { ...pal, @@ -194,6 +188,11 @@ function createStore() { setSort: (sort: Color[]) => palUp((n) => ({ ...n, colors: deDup(sort) })), setCurrentPalName: (name: string) => palUp((n) => ({ ...n, name })), setCurrentPalType: (type: PalType) => palUp((n) => ({ ...n, type })), + setCurrentAffects: (intendedAffects: Affect[]) => + palUp((n) => ({ ...n, intendedAffects })), + setCurrentContexts: (intendedContexts: Context[]) => + palUp((n) => ({ ...n, intendedContexts })), + setCurrentPalEvalConfig: (evalConfig: Record) => palUp((n) => ({ ...n, evalConfig: hardCopy(evalConfig) })), addColorToCurrentPal: (color: Color) => diff --git a/src/stores/lint-store.ts b/src/stores/lint-store.ts index 7149783a..6d1caf09 100644 --- a/src/stores/lint-store.ts +++ b/src/stores/lint-store.ts @@ -1,7 +1,7 @@ import { writable } from "svelte/store"; import * as idb from "idb-keyval"; import type { CustomLint } from "../lib/CustomLint"; -import type { TaskType } from "../lib/ColorLint"; +import type { PalType } from "../types"; import { JSONStringify } from "../lib/utils"; import { BUILT_INS } from "../lib/linter"; import { loadLints } from "../lib/api-calls"; @@ -72,7 +72,7 @@ function createStore() { lintUpdate((old) => ({ ...old, program })), setCurrentLintName: (name: string) => lintUpdate((old) => ({ ...old, name })), - setCurrentLintTaskTypes: (taskTypes: TaskType[]) => + setCurrentLintTaskTypes: (taskTypes: PalType[]) => lintUpdate((old) => ({ ...old, taskTypes })), setCurrentLintLevel: (level: "error" | "warning") => lintUpdate((old) => ({ ...old, level })), diff --git a/src/types.ts b/src/types.ts index d3340499..4d77320b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,21 @@ import { Color } from "./lib/Color"; +export const affects = [ + "calm", + "exciting", + "positive", + "negative", + "serious", + "playful", + "trustworthy", + "disturbing", +]; +export type Affect = (typeof affects)[number]; +export const contexts = ["scatterplot", "linechart", "barchart", "dashboard"]; + +export type Context = (typeof contexts)[number]; + +// Todo make this connect witht the color system export type ColorSpace = | "lab" | "hsl" @@ -10,6 +26,14 @@ export type ColorSpace = | "oklch" | "rgb" | "srgb"; + +// pretty nervous about the role stuff bc it will mean a lot of index manipulation to keep things straight when things get reordered +type ColorSemantics = { + role?: "background" | "text" | "accent" | "de-emphasize" | "main"; + size?: "small" | "medium" | "large"; + // what does main mean? + use?: "main" | "accent" | "de-emphasize"; +}; type Pal = { background: A; colorSpace: ColorSpace; @@ -17,6 +41,9 @@ type Pal = { evalConfig: Record; name: string; type: PalType; + intendedAffects: Affect[]; + intendedContexts: Context[]; + // todo: colorRole: Role[] }; export type PalType = "sequential" | "diverging" | "categorical"; export type Palette = Pal;