Skip to content

Commit

Permalink
Fix a bunch of UX bugs, interpolate bug
Browse files Browse the repository at this point in the history
  • Loading branch information
mcnuttandrew committed Feb 6, 2024
1 parent b03a47c commit f31829f
Show file tree
Hide file tree
Showing 9 changed files with 136 additions and 155 deletions.
126 changes: 12 additions & 114 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,139 +18,37 @@ Macros story

# Language todos

- [ ] Integration into main app
- [ ] Add background, roles, palette level semantics
- [ ] Categorical mutually distinct
- [x] Integration into main app
- [x] "Blame" for colors
- [ ] JSON Schema (for validation) (if parser is not used), get a sustainable way to use it
- [x] JSON Schema (for validation) (if parser is not used), get a sustainable way to use it
- [x] Basic Language
- [x] Multi indexing (eg all colors a b c)
- [na] Parser?
- [na] Swap left/right for [a, b] for density

# Todo bankruptcy

- [ ] off by one persistance error in undo/redo
- [ ] Gamut algorithm broken again
- [ ] Add HCT/CAM, add explainers to each of the color spaces
- [ ] Add HCT/CAM, add explainers to each of the color spaces, blocked by colorjs release
- [ ] Allow no palettes, allows renaming of non-current palettes
- [ ] MS didn't like the location of the new button
- [ ] Colors from String should save on enter
- [ ] Colors from String should save on enter?
- [ ] Changing spaces is pretty bad on lab <-> oklab, cf ("#35ffbf", "#87b995", "#e84f82")
- [ ] "new from blank" "new from small generic palette" buttons in the new menu.
- [ ] "Easy on ramp" progressive disclosure
- [ ] per cols 4 all: color blindness metric should maybe be sensitive to task?
- [ ] Minor: Make keyboard short cut (option+up/down) for the z-direction
- [ ] Insert color theory options, eg insert opposing, inserting analogous color, etc, mine from the adobe picker
- [ ] Labels, tooltips, etc
- [ ] Nice to have: Rest of basic geometry manipulations: flip (horizontal, vertical), scale, Distribute radially
- [ ] Bug: Color channel usage slightly cursed (doesn't update positions correctly)
- [ ] Add clone color (and similar buttons) to the tooltip, mostly for making interacting with the examples simpler
- [ ] Undo / redo stack size. Nice to have: summarize each action
- [ ] XYY is probaably now possible
- [ ] Bug: rotate in polar coordinates doesn't work right
- [ ] Ad hoc lints seem possible, do a spike
- [ ] Directional subtlies for aligns, they do not work in polar also
- [ ] Sequential check fix is incorrect for things with equi-ligthness
- [x] Bug: make a selection. interpolate it. undo the interpolation, crash. Proposed solution: if the number of points changes clear the selection?
- [x] Show simulated colors on examples, add a gray scale check
- [x] Bug: if comparing the same palette in two spaces, make a change and the space reverts
- [x] Compare: should show the values a pal preview
- [x] Deselect: In the row of colors, clicking on a selected color should deselect it
- [x] Tool tip not staying put
- [x] Chore: Extract some common types into a top level location (like types.ts type thing)
- [x] For the palettes, there is a copy and delete bug. Try this: Copy Example 2, then immediately click delete (menu is still up). Surprise!
- [x] Direct manipulation new color add "puttin" mode on the scatterplot
- [x] For the palettes, a couple of ideas. Right now, feels like selecting a palette in the list reorders all the palettes, which is disorienting. I realize what you're doing in removing the palette I clicked on, adding the one I was editing. I think don't do this. Keep a fixed order (which I could change if I wanted to), and simply mark the palette that's being editing, don't remove it from the list. Similarly, if I copy a palette, put it right after the palette I copied, not up at the top. I also think adding a compact view of the palettes will eventually be necessary, so now might be the time to do it. Even as a default, would make the circles a bit smaller and pack them tighter. Or maybe make them tall rectangles that would pack even tighter.
- [x] Bug report. If I am comparing a palette with itself and select a color in the main view, the color is not selected in the xy plot of the comparison panel. It is selected in the vertical only plot
- [x] Similarly, would like to work through the various options for the examples. Delete, Edit, Hide (not Mute) seem good. If you're going to Hide, then ideally you provide a selective unhide (dropdown of names is the easiest) as well as the unhide all. Your Solo means "hide everything else" which I'm not convinced is worth a button. However, a "full screen" or "fit width" "fit height" options that scales it up and reduces the rest to thumbnails might be cool. And I'm sure there are a few more we'll discover as we work with it.
- [x] Albers color theory games style examples

## Direct manipulation bugs/improvement

The basic UX for editing has the following components

## Two graphs, one 2D and one 1D.

The 2D graph displays hue/chroma graph and the 1D graph displays lightness. You can map any of a number of colorspaces onto this pair of graphs. (in the code, the lightness graph is "Z"). I propose the following changes

- [x] Reduce the visual impact of the axes and labels by making them transparent gray. Set the colors, made them adaptive, set the luminance flip to .3 (50% visually)
- [x] Flip the Y axis (zero at the bottom)
- [x] Make the labels integers for CIELAB
- [x] Make the axis scale sliders less visualy prominent.
- [x] Consider removing the axis sliders, replace them with zoom controls in the same panel as the background color selection. Changing these values is rare, they don't need to take up so much UX space.
- [x] The 2D graph should always be a centered hue/chroma graph. CIELAB and CIELCH would therefore use the same graph. Leave the LAB vs LCH distinction for slider based editing.
- [x] I'd consider creating an rectangle that enclosed both graphs and filling it with the background color. I might then also pull the axis labels outside bounding box for each graph.

## Single and multiple selection, drag and drop editing.

Click to select, drag or shift click to multi-select
# Nice to have

- [x] Show the selection bounding box only on multi-select. Make it thinner and a lighter
- [x] **Bug** a single click in the 1D graph selects and immediately deselects the color.
- [x] **Bug** shift click doesn't do multi-select for me.
- [x] I would add a deselect when you click in the white space, either a single click or the start of a new area select (this may also be a bug)
- [ ] There needs to be feedback when you drag out of gamut
- [x] Undo for dragging is too granular. Undo should undo the entire drag.

## Color editing with sliders.

To raise the sliders, you need to click on one of the examples that are displayed below the graphs. You can then manipulate 2 colorspaces simultaneously.

- [x] I'd move the examples into the example pane, use the space to permanently display the sliders, make them longer
- [x] Always display the hex values of the state of the sliders.
- [x] Once this is done, it make sense to show a tooltip that displays a pair of values as you hover over the graph. (maybe also the hex?)
- [x] (na) There should be an add color button that adds the current state of the sliders.
- [?] A single selection sets the sliders. Multi-select does not
- [x] Might then make sense to move the graph colorspace, range and background color controls into this area.
- [x] As a short term improvement, raise the sliders when you click on a color in the graphs.

## TODOS

- [x] New "blank" button.
- [x] Too many menu options, refactor so that they have a logical and usable hierarchy
- [x] Chore: Rearrange some of the colors in the color area eg make rg on xy and b on z etc, blocking polar stuff
- [x] Polar stuff
- [x] Make lints have configurable parameters
- [x] Examples: A (default) example showing annotated math stuff
- [x] Examples: SVG Upload
- [x] Examples: Vega/Vega-Lite Specification
- [x] Examples: Examples held as assets somewhere that are downloaded rather than components (for consistency)
- [x] Examples: higher card. vis examples
- [x] Black and white simulation
- [x] Chore: clean up the color store, many unnecessary methods.
- [x] Bug: changing the color frame fucks everything up???? (particularly for HSV/HSL)
- [x] Eval response options
- [x] "opposing color" to "Convert selection to opposing"
- [x] Meta: figure out all the other features in maureen's setup
- [x] order as diverging
- [x] Make it possible to ignore / dismiss lints
- [x] Touch events polish
- [x] drag box in z channel should work
- [x] Add color by name
- [x] Controls explainer
- [x] Color channel controller polish
- [x] Responsiveness
- [x] Step needs to be adapted to small spaces
- [x] Show select on eval
- [x] Minor: make axis numbers not too big
- [x] Copy/paste
- [x] Touch events, just pull the the touch event stuff from the double rangle slider, also refactor that out into something common
- [x] Plot operate over other color spaces
- [x] make old pals searchable
- [x] make charts be end user manipulable
- [x] kbd commands, up/down for moving selections, cmd+z for undo, etc
- [x] distribute horizontal / vertical
- [x] Plot zoom
- [x] Cursor selection range
- [x] color text input
- [x] make color sliders be shaded in the background
- [x] Add support for other color schemes (finish generalizing the chroma refactor)
- [x] Make colors actually work right (eg rip out chroma)
- [x] Minor: Make the aligns be a tool tip
- [x] Refactor file structure
- [x] Unbreak the color channel stuff, also the avging actions
- [x] add fancy cacheing to make the vega-charts not re-render constantly
- [x] add text commands
- [x] make text display deterministic
- [x] multi-select of colors?
- [x] undo / redo
- [ ] Insert color theory options, eg insert opposing, inserting analogous color, etc, mine from the adobe picker
- [ ] Add clone color (and similar buttons) to the tooltip, mostly for making interacting with the examples simpler
- [ ] XYY is probaably now possible
- [ ] Nice to have: Rest of basic geometry manipulations: flip (horizontal, vertical), scale, Distribute radially
- [ ] Undo / redo stack size. Nice to have: summarize each action
- [ ] Minor: Make keyboard short cut (option+up/down) for the z-direction
10 changes: 0 additions & 10 deletions src/App.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,6 @@
<div class="flex w-full grow overflow-auto">
<div class="flex flex-col">
<div class="w-full flex bg-stone-800 px-2 py-3 text-white">
<div class="flex">
<NewPal />
<button
id="save"
class={`${buttonStyle} `}
on:click={() => colorStore.duplicatePal($colorStore.currentPal)}
>
Save
</button>
</div>
<SetSimulation />
<Zoom />
</div>
Expand Down
32 changes: 26 additions & 6 deletions src/content-modules/LeftPanel.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
import colorStore from "../stores/color-store";
import configStore from "../stores/config-store";
import Nav from "../components/Nav.svelte";
import NewPal from "../controls/NewPal.svelte";
import Config from "../controls/Config.svelte";
import Controls from "./Controls.svelte";
import { buttonStyle } from "../lib/styles";
import { denseButtonStyle } from "../lib/styles";
import SavedPals from "./SavedPals.svelte";
</script>
Expand All @@ -16,20 +17,39 @@
Color Buddy 𑁍
</div>
<section class="flex flex-col flex-none">
<div class="flex justify-between z-50 p-2">
<div>
<button class={buttonStyle} on:click={() => colorStore.undo()}>
<div class="flex w-full justify-between items-start">
<div class="flex ml-1">
<NewPal />
/
<button
id="save"
class={`${denseButtonStyle} mt-0.5`}
on:click={() => colorStore.duplicatePal($colorStore.currentPal)}
>
Duplicate
</button>
</div>
<div class="flex">
<button
class={`${denseButtonStyle} p-0 mt-0.5`}
on:click={() => colorStore.undo()}
>
Undo
</button>
<button class={buttonStyle} on:click={() => colorStore.redo()}>
/
<button
class={`${denseButtonStyle} p-0 mt-0.5`}
on:click={() => colorStore.redo()}
>
Redo
</button>
</div>
<Config />
</div>
<div class="flex justify-center z-50"></div>
</section>

<section class="flex flex-col flex-1 overflow-auto p-2">
<section class="flex flex-col flex-1 overflow-auto p-1">
<div class="flex justify-center items-center">
<Nav
tabs={["palettes", "controls"]}
Expand Down
26 changes: 19 additions & 7 deletions src/controls/InterpolatePoints.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,20 @@
if (t === 0 || t === 1) continue;
const spaceOverWrite = { rgb: "srgb" } as any;
const adjustedSpace = spaceOverWrite[colorSpace] || colorSpace;
const space = spaceOverWrite[pointA.spaceName] || pointA.spaceName;
const [x1, x2, x3] = pointA.toColorIO().to(adjustedSpace).coords;
const [y1, y2, y3] = pointB.toColorIO().to(adjustedSpace).coords;
const lab = [
const space = spaceOverWrite[colorSpace] || colorSpace;
// const space = spaceOverWrite[pointA.spaceName] || pointA.spaceName;
const [x1, x2, x3] = pointA.toColorIO().to(space).coords;
const [y1, y2, y3] = pointB.toColorIO().to(space).coords;
const coords = [
x1 * (1 - t) + y1 * t,
x2 * (1 - t) + y2 * t,
x3 * (1 - t) + y3 * t,
] as [number, number, number];
points.push(Color.colorFromChannels(lab, "lab").toColorSpace(space));
const newColor = Color.colorFromChannels(coords, space);
points.push(newColor.toColorSpace(currentPal.colorSpace));
}
return points;
}
function createInterpolation(): Color[] {
Expand All @@ -62,6 +65,15 @@
);
return newColors;
}
const interpolationSpecs = [
"lab",
"lch",
"hsl",
"hsv",
// "oklab",
// "oklch",
"srgb",
] as const;
</script>

{#if focusedColors.length >= 2}
Expand All @@ -71,7 +83,7 @@
<div class="flex justify-between">
<label for="color-space-select">Color Space</label>
<select id="color-space-select" bind:value={colorSpace}>
{#each ["lab", "lch", "hsl", "hsv", "oklab", "oklch", "rgb"] as space}
{#each interpolationSpecs as space}
<option value={space}>{space}</option>
{/each}
</select>
Expand Down
30 changes: 27 additions & 3 deletions src/controls/NewPal.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import type { Palette } from "../stores/color-store";
import Tooltip from "../components/Tooltip.svelte";
import { VegaColors } from "../lib/charts";
import { buttonStyle } from "../lib/styles";
import { buttonStyle, denseButtonStyle } from "../lib/styles";
import { makePal, toHex } from "../lib/utils";
import type { ExtendedPal } from "../lib/utils";
import SuggestColorPal from "./SuggestColorPal.svelte";
Expand Down Expand Up @@ -47,6 +47,15 @@
);
function newPalFromBlank() {
const pal = newGenericPal("new palette") as any;
pal.colors = [];
pal.background = Color.colorFromString("#ffffff", colorSpace);
pal.colorSpace = colorSpace;
const newPal = pal as Palette;
colorStore.createNewPal(newPal);
}
function newWithGenericColors() {
const pal = newGenericPal("new palette") as any;
pal.colors = pal.colors.map((x: string) =>
Color.colorFromString(x, colorSpace)
Expand All @@ -62,9 +71,24 @@
<Tooltip>
<span slot="content" let:onClick class="max-w-lg">
<div>
<button class={buttonStyle} on:click={() => newPalFromBlank()}>
<button
class={buttonStyle}
on:click={() => {
newPalFromBlank();
onClick();
}}
>
New blank
</button>
<button
class={buttonStyle}
on:click={() => {
newWithGenericColors();
onClick();
}}
>
New with generic colors
</button>
</div>
<div class="mt-5 border-t-2 border-black"></div>
<div class="font-bold">Generate a new palette using AI</div>
Expand Down Expand Up @@ -92,6 +116,6 @@
</span>

<span slot="target" let:toggle>
<button class={buttonStyle} on:click={toggle}>New</button>
<button class={denseButtonStyle} on:click={toggle}>New</button>
</span>
</Tooltip>
27 changes: 25 additions & 2 deletions src/lib/Color.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,21 @@ class RGB extends Color {
}
}

class SRGB extends Color {
name = "sRGB";
channelNames = ["r", "g", "b"];
channels = { r: 0, g: 0, b: 0 };
spaceName = "srgb" as const;
domains = { r: [0, 1], g: [0, 1], b: [0, 1] } as Domain;
stepSize: Channels = [0.01, 0.01, 0.01];
dimensionToChannel = { x: "g", y: "b", z: "r" };
axisLabel = (num: number) => `${Math.round(num)}`;
toString(): string {
const [r, g, b] = this.stringChannels();
return `rgb(${Number(r) * 255} ${Number(g) * 255} ${Number(b) * 255})`;
}
}

class HSL extends Color {
name = "HSL";
channelNames = ["h", "s", "l"];
Expand Down Expand Up @@ -336,11 +351,18 @@ function colorFromHex(
return outColor;
}

const colorChannelsCache = new Map<string, Color>();
function colorFromChannels(
channels: Channels,
colorSpace: keyof typeof colorDirectory
): Color {
return new colorDirectory[colorSpace]().fromChannels(channels);
const cacheKey = `${colorSpace}(${channels.join(",")})`;
if (colorChannelsCache.has(cacheKey)) {
return colorChannelsCache.get(cacheKey)!.copy() as Color;
}
const color = new colorDirectory[colorSpace]().fromChannels(channels);
colorChannelsCache.set(cacheKey, color);
return color;
}

function toColorSpace(
Expand All @@ -365,7 +387,8 @@ const colorDirectory = {
oklab: OKLAB,
oklch: OKLCH,
rgb: RGB,
srgb: RGB,
// srgb: RGB,
srgb: SRGB,
};

type ColorSpace = keyof typeof colorDirectory | string;
Expand Down
5 changes: 5 additions & 0 deletions src/lib/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,8 @@ export const buttonStyleDisabled = `${baseButtonStyle} bg-stone-300 text-stone-5
// export const AIButtonStyle = `${baseButtonStyle} bg-amber-900`;

export const AIButtonStyle = `${baseButtonStyle} `;

export const denseButtonStyle = buttonStyle
.replace("p-2", "p-1")
.replace("px-2", "p-0")
.replace("mr-2", "mr-0");
Loading

0 comments on commit f31829f

Please sign in to comment.