@ben-million/tweaker 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +614 -0
- package/dist/index.js.map +1 -0
- package/package.json +50 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as react_jsx_runtime0 from "react/jsx-runtime";
|
|
2
|
+
|
|
3
|
+
//#region src/types.d.ts
|
|
4
|
+
interface GrayScale {
|
|
5
|
+
label: string;
|
|
6
|
+
shades: Record<string, string>;
|
|
7
|
+
}
|
|
8
|
+
interface TweakerProps {
|
|
9
|
+
scales?: Record<string, GrayScale>;
|
|
10
|
+
activeScale?: string;
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
//#region src/tweaker.d.ts
|
|
14
|
+
declare const Tweaker: ({
|
|
15
|
+
scales,
|
|
16
|
+
activeScale
|
|
17
|
+
}: TweakerProps) => react_jsx_runtime0.JSX.Element;
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/gray-scales.d.ts
|
|
20
|
+
declare const GRAY_SCALES: Record<string, GrayScale>;
|
|
21
|
+
//#endregion
|
|
22
|
+
export { GRAY_SCALES, type GrayScale, Tweaker, type TweakerProps };
|
|
23
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/tweaker.tsx","../src/gray-scales.ts"],"mappings":";;;UAEiB,SAAA;EACf,KAAA;EACA,MAAA,EAAQ,MAAA;AAAA;AAAA,UAgBO,YAAA;EACf,MAAA,GAAS,MAAA,SAAe,SAAA;EACxB,WAAA;AAAA;;;cCZW,OAAA;EAAW,MAAA;EAAA;AAAA,GAAmD,YAAA,KAAY,kBAAA,CAAA,GAAA,CAAA,OAAA;;;cCR1E,WAAA,EAAa,MAAA,SAAe,SAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,614 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { AnimatePresence, motion, useMotionValue, useTransform } from "motion/react";
|
|
3
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
|
|
5
|
+
//#region src/gray-scales.ts
|
|
6
|
+
const GRAY_SCALES = {
|
|
7
|
+
neutral: {
|
|
8
|
+
label: "Neutral",
|
|
9
|
+
shades: {
|
|
10
|
+
"50": "oklch(0.985 0 0)",
|
|
11
|
+
"100": "oklch(0.97 0 0)",
|
|
12
|
+
"200": "oklch(0.922 0 0)",
|
|
13
|
+
"300": "oklch(0.87 0 0)",
|
|
14
|
+
"400": "oklch(0.708 0 0)",
|
|
15
|
+
"500": "oklch(0.556 0 0)",
|
|
16
|
+
"600": "oklch(0.439 0 0)",
|
|
17
|
+
"700": "oklch(0.371 0 0)",
|
|
18
|
+
"800": "oklch(0.269 0 0)",
|
|
19
|
+
"900": "oklch(0.205 0 0)",
|
|
20
|
+
"950": "oklch(0.145 0 0)"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
slate: {
|
|
24
|
+
label: "Slate",
|
|
25
|
+
shades: {
|
|
26
|
+
"50": "oklch(0.984 0.003 247.858)",
|
|
27
|
+
"100": "oklch(0.968 0.007 247.896)",
|
|
28
|
+
"200": "oklch(0.929 0.013 255.508)",
|
|
29
|
+
"300": "oklch(0.869 0.022 252.894)",
|
|
30
|
+
"400": "oklch(0.704 0.04 256.788)",
|
|
31
|
+
"500": "oklch(0.554 0.046 257.417)",
|
|
32
|
+
"600": "oklch(0.446 0.043 257.281)",
|
|
33
|
+
"700": "oklch(0.372 0.044 257.287)",
|
|
34
|
+
"800": "oklch(0.279 0.041 260.031)",
|
|
35
|
+
"900": "oklch(0.208 0.042 265.755)",
|
|
36
|
+
"950": "oklch(0.129 0.042 264.695)"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
gray: {
|
|
40
|
+
label: "Gray",
|
|
41
|
+
shades: {
|
|
42
|
+
"50": "oklch(0.985 0.002 247.839)",
|
|
43
|
+
"100": "oklch(0.967 0.003 264.542)",
|
|
44
|
+
"200": "oklch(0.928 0.006 264.531)",
|
|
45
|
+
"300": "oklch(0.872 0.01 258.338)",
|
|
46
|
+
"400": "oklch(0.707 0.022 261.325)",
|
|
47
|
+
"500": "oklch(0.551 0.027 264.364)",
|
|
48
|
+
"600": "oklch(0.446 0.03 256.802)",
|
|
49
|
+
"700": "oklch(0.373 0.034 259.733)",
|
|
50
|
+
"800": "oklch(0.278 0.033 256.848)",
|
|
51
|
+
"900": "oklch(0.21 0.034 264.665)",
|
|
52
|
+
"950": "oklch(0.13 0.028 261.692)"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
zinc: {
|
|
56
|
+
label: "Zinc",
|
|
57
|
+
shades: {
|
|
58
|
+
"50": "oklch(0.985 0 0)",
|
|
59
|
+
"100": "oklch(0.967 0.001 286.375)",
|
|
60
|
+
"200": "oklch(0.92 0.004 286.32)",
|
|
61
|
+
"300": "oklch(0.871 0.006 286.286)",
|
|
62
|
+
"400": "oklch(0.705 0.015 286.067)",
|
|
63
|
+
"500": "oklch(0.552 0.016 285.938)",
|
|
64
|
+
"600": "oklch(0.442 0.017 285.786)",
|
|
65
|
+
"700": "oklch(0.37 0.013 285.805)",
|
|
66
|
+
"800": "oklch(0.274 0.006 286.033)",
|
|
67
|
+
"900": "oklch(0.21 0.006 285.885)",
|
|
68
|
+
"950": "oklch(0.141 0.005 285.823)"
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
stone: {
|
|
72
|
+
label: "Stone",
|
|
73
|
+
shades: {
|
|
74
|
+
"50": "oklch(0.985 0.001 106.423)",
|
|
75
|
+
"100": "oklch(0.97 0.001 106.424)",
|
|
76
|
+
"200": "oklch(0.923 0.003 48.717)",
|
|
77
|
+
"300": "oklch(0.869 0.005 56.366)",
|
|
78
|
+
"400": "oklch(0.709 0.01 56.259)",
|
|
79
|
+
"500": "oklch(0.553 0.013 58.071)",
|
|
80
|
+
"600": "oklch(0.444 0.011 73.639)",
|
|
81
|
+
"700": "oklch(0.374 0.01 67.558)",
|
|
82
|
+
"800": "oklch(0.268 0.007 34.298)",
|
|
83
|
+
"900": "oklch(0.216 0.006 56.043)",
|
|
84
|
+
"950": "oklch(0.147 0.004 49.25)"
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
mauve: {
|
|
88
|
+
label: "Mauve",
|
|
89
|
+
shades: {
|
|
90
|
+
"50": "oklch(0.985 0.003 310)",
|
|
91
|
+
"100": "oklch(0.968 0.006 310)",
|
|
92
|
+
"200": "oklch(0.925 0.011 310)",
|
|
93
|
+
"300": "oklch(0.87 0.017 310)",
|
|
94
|
+
"400": "oklch(0.708 0.03 310)",
|
|
95
|
+
"500": "oklch(0.556 0.03 310)",
|
|
96
|
+
"600": "oklch(0.44 0.028 310)",
|
|
97
|
+
"700": "oklch(0.372 0.025 310)",
|
|
98
|
+
"800": "oklch(0.27 0.02 310)",
|
|
99
|
+
"900": "oklch(0.208 0.018 310)",
|
|
100
|
+
"950": "oklch(0.145 0.014 310)"
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
olive: {
|
|
104
|
+
label: "Olive",
|
|
105
|
+
shades: {
|
|
106
|
+
"50": "oklch(0.985 0.003 130)",
|
|
107
|
+
"100": "oklch(0.968 0.006 130)",
|
|
108
|
+
"200": "oklch(0.925 0.011 130)",
|
|
109
|
+
"300": "oklch(0.87 0.017 130)",
|
|
110
|
+
"400": "oklch(0.708 0.028 130)",
|
|
111
|
+
"500": "oklch(0.556 0.028 130)",
|
|
112
|
+
"600": "oklch(0.44 0.024 130)",
|
|
113
|
+
"700": "oklch(0.372 0.02 130)",
|
|
114
|
+
"800": "oklch(0.27 0.016 130)",
|
|
115
|
+
"900": "oklch(0.208 0.014 130)",
|
|
116
|
+
"950": "oklch(0.145 0.01 130)"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
//#endregion
|
|
122
|
+
//#region src/constants.ts
|
|
123
|
+
const SLIDER_MAX = 10;
|
|
124
|
+
const BAR_WIDTH_PX = 3;
|
|
125
|
+
const TEXT_PREVIEW_MAX_LENGTH = 30;
|
|
126
|
+
const TYPING_RESET_DELAY_MS = 1500;
|
|
127
|
+
const SHADE_KEYS = [
|
|
128
|
+
"50",
|
|
129
|
+
"100",
|
|
130
|
+
"200",
|
|
131
|
+
"300",
|
|
132
|
+
"400",
|
|
133
|
+
"500",
|
|
134
|
+
"600",
|
|
135
|
+
"700",
|
|
136
|
+
"800",
|
|
137
|
+
"900",
|
|
138
|
+
"950"
|
|
139
|
+
];
|
|
140
|
+
|
|
141
|
+
//#endregion
|
|
142
|
+
//#region src/utils/color.ts
|
|
143
|
+
const parseOklch = (oklchStr) => {
|
|
144
|
+
const match = oklchStr.match(/oklch\(([\d.]+)\s+([\d.]+)\s+([\d.]+)\)/);
|
|
145
|
+
if (!match) return [
|
|
146
|
+
0,
|
|
147
|
+
0,
|
|
148
|
+
0
|
|
149
|
+
];
|
|
150
|
+
return [
|
|
151
|
+
Number(match[1]),
|
|
152
|
+
Number(match[2]),
|
|
153
|
+
Number(match[3])
|
|
154
|
+
];
|
|
155
|
+
};
|
|
156
|
+
const lerpOklch = (colorA, colorB, interpolation) => [
|
|
157
|
+
colorA[0] + (colorB[0] - colorA[0]) * interpolation,
|
|
158
|
+
colorA[1] + (colorB[1] - colorA[1]) * interpolation,
|
|
159
|
+
colorA[2] + (colorB[2] - colorA[2]) * interpolation
|
|
160
|
+
];
|
|
161
|
+
const formatOklch = (oklch) => `oklch(${oklch[0].toFixed(3)} ${oklch[1].toFixed(3)} ${oklch[2].toFixed(1)})`;
|
|
162
|
+
const oklchToCssString = (oklch) => `oklch(${oklch[0]} ${oklch[1]} ${oklch[2]})`;
|
|
163
|
+
const getColorAtPosition = (scales, scaleKey, position) => {
|
|
164
|
+
const scale = scales[scaleKey];
|
|
165
|
+
if (!scale) return [
|
|
166
|
+
.5,
|
|
167
|
+
0,
|
|
168
|
+
0
|
|
169
|
+
];
|
|
170
|
+
const segment = (SLIDER_MAX - position) / SLIDER_MAX * (SHADE_KEYS.length - 1);
|
|
171
|
+
const index = Math.min(Math.floor(segment), SHADE_KEYS.length - 2);
|
|
172
|
+
const interpolation = segment - index;
|
|
173
|
+
return lerpOklch(parseOklch(scale.shades[SHADE_KEYS[index]]), parseOklch(scale.shades[SHADE_KEYS[index + 1]]), interpolation);
|
|
174
|
+
};
|
|
175
|
+
const getClosestShadeLabel = (position) => {
|
|
176
|
+
const segment = (SLIDER_MAX - position) / SLIDER_MAX * (SHADE_KEYS.length - 1);
|
|
177
|
+
const index = Math.round(segment);
|
|
178
|
+
return SHADE_KEYS[Math.min(index, SHADE_KEYS.length - 1)];
|
|
179
|
+
};
|
|
180
|
+
const parseRgb = (color) => {
|
|
181
|
+
const match = color.match(/rgba?\(\s*([\d.]+),\s*([\d.]+),\s*([\d.]+)(?:,\s*([\d.]+))?\s*\)/);
|
|
182
|
+
if (!match) return [
|
|
183
|
+
0,
|
|
184
|
+
0,
|
|
185
|
+
0,
|
|
186
|
+
0
|
|
187
|
+
];
|
|
188
|
+
return [
|
|
189
|
+
Number(match[1]),
|
|
190
|
+
Number(match[2]),
|
|
191
|
+
Number(match[3]),
|
|
192
|
+
match[4] !== void 0 ? Number(match[4]) : 1
|
|
193
|
+
];
|
|
194
|
+
};
|
|
195
|
+
const rgbToOklch = (red, green, blue) => {
|
|
196
|
+
const linearize = (channel) => {
|
|
197
|
+
const normalized = channel / 255;
|
|
198
|
+
return normalized <= .04045 ? normalized / 12.92 : Math.pow((normalized + .055) / 1.055, 2.4);
|
|
199
|
+
};
|
|
200
|
+
const linearRed = linearize(red);
|
|
201
|
+
const linearGreen = linearize(green);
|
|
202
|
+
const linearBlue = linearize(blue);
|
|
203
|
+
const lmsL = Math.cbrt(.4122214708 * linearRed + .5363325363 * linearGreen + .0514459929 * linearBlue);
|
|
204
|
+
const lmsM = Math.cbrt(.2119034982 * linearRed + .6806995451 * linearGreen + .1073969566 * linearBlue);
|
|
205
|
+
const lmsS = Math.cbrt(.0883024619 * linearRed + .2817188376 * linearGreen + .6299787005 * linearBlue);
|
|
206
|
+
const lightness = .2104542553 * lmsL + .793617785 * lmsM - .0040720468 * lmsS;
|
|
207
|
+
const labA = 1.9779984951 * lmsL - 2.428592205 * lmsM + .4505937099 * lmsS;
|
|
208
|
+
const labB = .0259040371 * lmsL + .7827717662 * lmsM - .808675766 * lmsS;
|
|
209
|
+
const chroma = Math.sqrt(labA * labA + labB * labB);
|
|
210
|
+
const hue = Math.atan2(labB, labA) * (180 / Math.PI);
|
|
211
|
+
return [
|
|
212
|
+
lightness,
|
|
213
|
+
chroma,
|
|
214
|
+
hue < 0 ? hue + 360 : hue
|
|
215
|
+
];
|
|
216
|
+
};
|
|
217
|
+
const findClosestPosition = (scales, scaleKey, targetOklch) => {
|
|
218
|
+
let bestPosition = 0;
|
|
219
|
+
let bestDistance = Infinity;
|
|
220
|
+
for (let position = 0; position <= SLIDER_MAX; position++) {
|
|
221
|
+
const color = getColorAtPosition(scales, scaleKey, position);
|
|
222
|
+
const distance = (color[0] - targetOklch[0]) ** 2 + (color[1] - targetOklch[1]) ** 2 + ((color[2] - targetOklch[2]) / 360) ** 2;
|
|
223
|
+
if (distance < bestDistance) {
|
|
224
|
+
bestDistance = distance;
|
|
225
|
+
bestPosition = position;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return bestPosition;
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
//#endregion
|
|
232
|
+
//#region src/utils/dom.ts
|
|
233
|
+
const getSelector = (element) => {
|
|
234
|
+
const tag = element.tagName.toLowerCase();
|
|
235
|
+
const classes = Array.from(element.classList).filter((className) => !className.startsWith("__")).slice(0, 2).join(".");
|
|
236
|
+
return classes ? `${tag}.${classes}` : tag;
|
|
237
|
+
};
|
|
238
|
+
const getTextPreview = (element) => {
|
|
239
|
+
const text = element.textContent?.trim() || "";
|
|
240
|
+
return text.length > TEXT_PREVIEW_MAX_LENGTH ? `${text.slice(0, TEXT_PREVIEW_MAX_LENGTH)}…` : text;
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
//#endregion
|
|
244
|
+
//#region src/utils/modification.ts
|
|
245
|
+
const applyModification = (modification, scales, scaleKey) => {
|
|
246
|
+
const colorValue = oklchToCssString(getColorAtPosition(scales, scaleKey, modification.position));
|
|
247
|
+
if (modification.property === "bg") modification.element.style.backgroundColor = colorValue;
|
|
248
|
+
else if (modification.property === "text") modification.element.style.color = colorValue;
|
|
249
|
+
else modification.element.style.borderColor = colorValue;
|
|
250
|
+
};
|
|
251
|
+
const restoreModification = (modification) => {
|
|
252
|
+
modification.element.style.backgroundColor = modification.originalInlineBg;
|
|
253
|
+
modification.element.style.color = modification.originalInlineColor;
|
|
254
|
+
modification.element.style.borderColor = modification.originalInlineBorderColor;
|
|
255
|
+
};
|
|
256
|
+
const roundToStep = (value) => parseFloat((Math.round(value * 10) / 10).toFixed(1));
|
|
257
|
+
|
|
258
|
+
//#endregion
|
|
259
|
+
//#region src/utils/prompt.ts
|
|
260
|
+
const generatePrompt = (modifications, scales, scaleKey) => {
|
|
261
|
+
if (modifications.length === 0) return "";
|
|
262
|
+
const scaleName = scales[scaleKey]?.label || scaleKey;
|
|
263
|
+
const lines = [];
|
|
264
|
+
modifications.forEach((modification) => {
|
|
265
|
+
const shade = getClosestShadeLabel(modification.position);
|
|
266
|
+
const oklch = getColorAtPosition(scales, scaleKey, modification.position);
|
|
267
|
+
const nameParts = [modification.selector];
|
|
268
|
+
if (modification.componentName) nameParts.unshift(`<${modification.componentName}>`);
|
|
269
|
+
if (modification.textPreview) nameParts.push(`("${modification.textPreview}")`);
|
|
270
|
+
const description = nameParts.join(" ");
|
|
271
|
+
const property = modification.property === "bg" ? "background color" : modification.property === "text" ? "text color" : "border color";
|
|
272
|
+
lines.push(`- ${property} of ${description} → ${scaleName} ${shade} (${formatOklch(oklch)})`);
|
|
273
|
+
if (modification.sourceFile) lines.push(` Source: ${modification.sourceFile}`);
|
|
274
|
+
});
|
|
275
|
+
return [
|
|
276
|
+
"Change the following colors using the design system's gray scale:",
|
|
277
|
+
"",
|
|
278
|
+
...lines
|
|
279
|
+
].join("\n");
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
//#endregion
|
|
283
|
+
//#region src/tweaker.tsx
|
|
284
|
+
const Tweaker = ({ scales = GRAY_SCALES, activeScale = "neutral" }) => {
|
|
285
|
+
const [picking, setPicking] = useState(false);
|
|
286
|
+
const [modifications, setModifications] = useState([]);
|
|
287
|
+
const [activeIndex, setActiveIndex] = useState(-1);
|
|
288
|
+
const [inputValue, setInputValue] = useState("");
|
|
289
|
+
const typingBuffer = useRef("");
|
|
290
|
+
const typingTimeout = useRef(void 0);
|
|
291
|
+
const fillPercent = useMotionValue(0);
|
|
292
|
+
const fillHeight = useTransform(fillPercent, (percent) => `${percent}%`);
|
|
293
|
+
const activeMod = activeIndex >= 0 ? modifications[activeIndex] : null;
|
|
294
|
+
const hasModifications = modifications.length > 0;
|
|
295
|
+
const activeIndexRef = useRef(activeIndex);
|
|
296
|
+
const activeScaleRef = useRef(activeScale);
|
|
297
|
+
const modificationsRef = useRef(modifications);
|
|
298
|
+
const scalesRef = useRef(scales);
|
|
299
|
+
activeIndexRef.current = activeIndex;
|
|
300
|
+
activeScaleRef.current = activeScale;
|
|
301
|
+
modificationsRef.current = modifications;
|
|
302
|
+
scalesRef.current = scales;
|
|
303
|
+
useEffect(() => {
|
|
304
|
+
if (activeMod) fillPercent.jump(activeMod.position / SLIDER_MAX * 100);
|
|
305
|
+
}, [activeMod?.position, fillPercent]);
|
|
306
|
+
const updateActivePosition = useCallback((newPosition) => {
|
|
307
|
+
if (activeIndex < 0) return;
|
|
308
|
+
setModifications((previous) => {
|
|
309
|
+
const updated = [...previous];
|
|
310
|
+
updated[activeIndex] = {
|
|
311
|
+
...updated[activeIndex],
|
|
312
|
+
position: newPosition
|
|
313
|
+
};
|
|
314
|
+
applyModification(updated[activeIndex], scales, activeScale);
|
|
315
|
+
return updated;
|
|
316
|
+
});
|
|
317
|
+
}, [
|
|
318
|
+
activeIndex,
|
|
319
|
+
activeScale,
|
|
320
|
+
scales
|
|
321
|
+
]);
|
|
322
|
+
useEffect(() => {
|
|
323
|
+
if (!hasModifications || picking) return;
|
|
324
|
+
const updateFromY = (clientY) => {
|
|
325
|
+
const percent = Math.max(0, Math.min(1, 1 - clientY / window.innerHeight));
|
|
326
|
+
const value = roundToStep(percent * SLIDER_MAX);
|
|
327
|
+
const index = activeIndexRef.current;
|
|
328
|
+
if (index < 0) return;
|
|
329
|
+
fillPercent.jump(percent * 100);
|
|
330
|
+
setModifications((previous) => {
|
|
331
|
+
const updated = [...previous];
|
|
332
|
+
updated[index] = {
|
|
333
|
+
...updated[index],
|
|
334
|
+
position: value
|
|
335
|
+
};
|
|
336
|
+
applyModification(updated[index], scalesRef.current, activeScaleRef.current);
|
|
337
|
+
return updated;
|
|
338
|
+
});
|
|
339
|
+
setInputValue(String(value));
|
|
340
|
+
};
|
|
341
|
+
const handlePointerMove = (event) => {
|
|
342
|
+
updateFromY(event.clientY);
|
|
343
|
+
};
|
|
344
|
+
document.addEventListener("pointermove", handlePointerMove, true);
|
|
345
|
+
return () => {
|
|
346
|
+
document.removeEventListener("pointermove", handlePointerMove, true);
|
|
347
|
+
};
|
|
348
|
+
}, [
|
|
349
|
+
hasModifications,
|
|
350
|
+
picking,
|
|
351
|
+
fillPercent
|
|
352
|
+
]);
|
|
353
|
+
useEffect(() => {
|
|
354
|
+
if (!hasModifications) return;
|
|
355
|
+
const handleKeyDown = (event) => {
|
|
356
|
+
if (event.key === "Escape") {
|
|
357
|
+
event.preventDefault();
|
|
358
|
+
const prompt = generatePrompt(modificationsRef.current, scalesRef.current, activeScaleRef.current);
|
|
359
|
+
navigator.clipboard.writeText(prompt);
|
|
360
|
+
modificationsRef.current.forEach(restoreModification);
|
|
361
|
+
setModifications([]);
|
|
362
|
+
setActiveIndex(-1);
|
|
363
|
+
setInputValue("");
|
|
364
|
+
}
|
|
365
|
+
if (event.key === " ") {
|
|
366
|
+
event.preventDefault();
|
|
367
|
+
const prompt = generatePrompt(modificationsRef.current, scalesRef.current, activeScaleRef.current);
|
|
368
|
+
navigator.clipboard.writeText(prompt);
|
|
369
|
+
setPicking(true);
|
|
370
|
+
}
|
|
371
|
+
};
|
|
372
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
373
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
374
|
+
}, [hasModifications]);
|
|
375
|
+
useEffect(() => {
|
|
376
|
+
if (!activeMod || picking) return;
|
|
377
|
+
const handleKeyDown = (event) => {
|
|
378
|
+
const target = event.target;
|
|
379
|
+
if (target.tagName === "INPUT" || target.tagName === "TEXTAREA") return;
|
|
380
|
+
if (event.key === "Escape") return;
|
|
381
|
+
if (event.key >= "0" && event.key <= "9" || event.key === ".") {
|
|
382
|
+
event.preventDefault();
|
|
383
|
+
const next = typingBuffer.current + event.key;
|
|
384
|
+
if (event.key === "." && typingBuffer.current.includes(".")) return;
|
|
385
|
+
if (typingBuffer.current.includes(".") && event.key !== "." && typingBuffer.current.split(".")[1]?.length >= 1) return;
|
|
386
|
+
const parsed = parseFloat(next);
|
|
387
|
+
if (!isNaN(parsed) && parsed > SLIDER_MAX) typingBuffer.current = event.key === "." ? "." : event.key;
|
|
388
|
+
else typingBuffer.current = next;
|
|
389
|
+
const value = parseFloat(typingBuffer.current);
|
|
390
|
+
if (!isNaN(value)) {
|
|
391
|
+
updateActivePosition(Math.min(SLIDER_MAX, value));
|
|
392
|
+
setInputValue(typingBuffer.current);
|
|
393
|
+
}
|
|
394
|
+
clearTimeout(typingTimeout.current);
|
|
395
|
+
typingTimeout.current = setTimeout(() => {
|
|
396
|
+
typingBuffer.current = "";
|
|
397
|
+
}, TYPING_RESET_DELAY_MS);
|
|
398
|
+
}
|
|
399
|
+
if (event.key === "Backspace") {
|
|
400
|
+
event.preventDefault();
|
|
401
|
+
typingBuffer.current = typingBuffer.current.slice(0, -1);
|
|
402
|
+
if (typingBuffer.current && typingBuffer.current !== ".") {
|
|
403
|
+
updateActivePosition(Math.min(SLIDER_MAX, parseFloat(typingBuffer.current)));
|
|
404
|
+
setInputValue(typingBuffer.current);
|
|
405
|
+
}
|
|
406
|
+
clearTimeout(typingTimeout.current);
|
|
407
|
+
typingTimeout.current = setTimeout(() => {
|
|
408
|
+
typingBuffer.current = "";
|
|
409
|
+
}, TYPING_RESET_DELAY_MS);
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
413
|
+
return () => {
|
|
414
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
415
|
+
clearTimeout(typingTimeout.current);
|
|
416
|
+
};
|
|
417
|
+
}, [
|
|
418
|
+
activeMod,
|
|
419
|
+
picking,
|
|
420
|
+
activeIndex,
|
|
421
|
+
updateActivePosition
|
|
422
|
+
]);
|
|
423
|
+
useEffect(() => {
|
|
424
|
+
const handleKeyDown = (event) => {
|
|
425
|
+
const target = event.target;
|
|
426
|
+
if (target.tagName === "INPUT" || target.tagName === "TEXTAREA") return;
|
|
427
|
+
if (event.key === "t") {
|
|
428
|
+
event.preventDefault();
|
|
429
|
+
setPicking(true);
|
|
430
|
+
}
|
|
431
|
+
if (hasModifications && (event.key === "b" || event.key === "f" || event.key === "d")) {
|
|
432
|
+
event.preventDefault();
|
|
433
|
+
const property = event.key === "b" ? "bg" : event.key === "f" ? "text" : "border";
|
|
434
|
+
const index = activeIndexRef.current;
|
|
435
|
+
if (index < 0) return;
|
|
436
|
+
setModifications((previous) => {
|
|
437
|
+
const updated = [...previous];
|
|
438
|
+
restoreModification(updated[index]);
|
|
439
|
+
updated[index] = {
|
|
440
|
+
...updated[index],
|
|
441
|
+
property
|
|
442
|
+
};
|
|
443
|
+
applyModification(updated[index], scalesRef.current, activeScaleRef.current);
|
|
444
|
+
return updated;
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
449
|
+
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
450
|
+
}, [hasModifications]);
|
|
451
|
+
useEffect(() => {
|
|
452
|
+
return () => {
|
|
453
|
+
modifications.forEach(restoreModification);
|
|
454
|
+
};
|
|
455
|
+
}, []);
|
|
456
|
+
useEffect(() => {
|
|
457
|
+
if (activeMod) applyModification(activeMod, scales, activeScale);
|
|
458
|
+
}, [
|
|
459
|
+
activeMod?.position,
|
|
460
|
+
activeScale,
|
|
461
|
+
scales
|
|
462
|
+
]);
|
|
463
|
+
useEffect(() => {
|
|
464
|
+
if (!picking) return;
|
|
465
|
+
let hoveredElement = null;
|
|
466
|
+
const handleMouseOver = (event) => {
|
|
467
|
+
const target = event.target;
|
|
468
|
+
if (target.closest("[data-tweaker]")) return;
|
|
469
|
+
hoveredElement = target;
|
|
470
|
+
target.style.outline = "2px solid #3b82f6";
|
|
471
|
+
target.style.outlineOffset = "2px";
|
|
472
|
+
};
|
|
473
|
+
const handleMouseOut = (event) => {
|
|
474
|
+
const target = event.target;
|
|
475
|
+
target.style.outline = "";
|
|
476
|
+
target.style.outlineOffset = "";
|
|
477
|
+
if (hoveredElement === target) hoveredElement = null;
|
|
478
|
+
};
|
|
479
|
+
const handleClick = (event) => {
|
|
480
|
+
event.preventDefault();
|
|
481
|
+
event.stopPropagation();
|
|
482
|
+
const target = event.target;
|
|
483
|
+
if (target.closest("[data-tweaker]")) return;
|
|
484
|
+
target.style.outline = "";
|
|
485
|
+
target.style.outlineOffset = "";
|
|
486
|
+
const computed = getComputedStyle(target);
|
|
487
|
+
const [bgRed, bgGreen, bgBlue, bgAlpha] = parseRgb(computed.backgroundColor);
|
|
488
|
+
const [textRed, textGreen, textBlue] = parseRgb(computed.color);
|
|
489
|
+
const [borderRed, borderGreen, borderBlue, borderAlpha] = parseRgb(computed.borderColor);
|
|
490
|
+
const hasBorder = borderAlpha > 0 && parseFloat(computed.borderWidth) > 0;
|
|
491
|
+
const defaultProperty = bgAlpha > 0 ? "bg" : hasBorder ? "border" : "text";
|
|
492
|
+
const position = findClosestPosition(scales, activeScale, defaultProperty === "bg" ? rgbToOklch(bgRed, bgGreen, bgBlue) : defaultProperty === "border" ? rgbToOklch(borderRed, borderGreen, borderBlue) : rgbToOklch(textRed, textGreen, textBlue));
|
|
493
|
+
const newModification = {
|
|
494
|
+
element: target,
|
|
495
|
+
selector: getSelector(target),
|
|
496
|
+
componentName: null,
|
|
497
|
+
sourceFile: null,
|
|
498
|
+
textPreview: getTextPreview(target),
|
|
499
|
+
originalInlineBg: target.style.backgroundColor,
|
|
500
|
+
originalInlineColor: target.style.color,
|
|
501
|
+
originalInlineBorderColor: target.style.borderColor,
|
|
502
|
+
property: defaultProperty,
|
|
503
|
+
position
|
|
504
|
+
};
|
|
505
|
+
setModifications((previous) => [...previous, newModification]);
|
|
506
|
+
setActiveIndex(modifications.length);
|
|
507
|
+
setInputValue(String(position));
|
|
508
|
+
setPicking(false);
|
|
509
|
+
};
|
|
510
|
+
document.addEventListener("mouseover", handleMouseOver, true);
|
|
511
|
+
document.addEventListener("mouseout", handleMouseOut, true);
|
|
512
|
+
document.addEventListener("click", handleClick, true);
|
|
513
|
+
return () => {
|
|
514
|
+
document.removeEventListener("mouseover", handleMouseOver, true);
|
|
515
|
+
document.removeEventListener("mouseout", handleMouseOut, true);
|
|
516
|
+
document.removeEventListener("click", handleClick, true);
|
|
517
|
+
if (hoveredElement) {
|
|
518
|
+
hoveredElement.style.outline = "";
|
|
519
|
+
hoveredElement.style.outlineOffset = "";
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
}, [
|
|
523
|
+
picking,
|
|
524
|
+
activeScale,
|
|
525
|
+
scales,
|
|
526
|
+
modifications.length
|
|
527
|
+
]);
|
|
528
|
+
const fillColor = activeMod ? oklchToCssString(getColorAtPosition(scales, activeScale, activeMod.position)) : scales[activeScale]?.shades["500"] ?? "rgba(255,255,255,0.3)";
|
|
529
|
+
const propertyLabel = activeMod?.property === "text" ? "F" : activeMod?.property === "border" ? "D" : "B";
|
|
530
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(AnimatePresence, { children: hasModifications && !picking && /* @__PURE__ */ jsx(motion.div, {
|
|
531
|
+
initial: { opacity: 0 },
|
|
532
|
+
animate: { opacity: 1 },
|
|
533
|
+
exit: { opacity: 0 },
|
|
534
|
+
transition: { duration: .2 },
|
|
535
|
+
"data-tweaker": true,
|
|
536
|
+
style: {
|
|
537
|
+
position: "fixed",
|
|
538
|
+
top: 0,
|
|
539
|
+
right: 0,
|
|
540
|
+
width: BAR_WIDTH_PX,
|
|
541
|
+
height: "100vh",
|
|
542
|
+
zIndex: 9999,
|
|
543
|
+
pointerEvents: "none"
|
|
544
|
+
},
|
|
545
|
+
children: /* @__PURE__ */ jsx(motion.div, { style: {
|
|
546
|
+
position: "absolute",
|
|
547
|
+
left: 0,
|
|
548
|
+
right: 0,
|
|
549
|
+
bottom: 0,
|
|
550
|
+
height: fillHeight,
|
|
551
|
+
background: fillColor
|
|
552
|
+
} })
|
|
553
|
+
}, "bar") }), /* @__PURE__ */ jsx(motion.div, {
|
|
554
|
+
"data-tweaker": true,
|
|
555
|
+
layout: true,
|
|
556
|
+
style: {
|
|
557
|
+
...pillStyle,
|
|
558
|
+
...!hasModifications ? { cursor: "pointer" } : {}
|
|
559
|
+
},
|
|
560
|
+
animate: { backgroundColor: scales[activeScale]?.shades["900"] ?? "#1A1A1A" },
|
|
561
|
+
transition: {
|
|
562
|
+
layout: {
|
|
563
|
+
type: "spring",
|
|
564
|
+
visualDuration: .3,
|
|
565
|
+
bounce: .15
|
|
566
|
+
},
|
|
567
|
+
backgroundColor: { duration: .3 }
|
|
568
|
+
},
|
|
569
|
+
onClick: !hasModifications ? () => setPicking(!picking) : void 0,
|
|
570
|
+
children: /* @__PURE__ */ jsx(AnimatePresence, {
|
|
571
|
+
mode: "wait",
|
|
572
|
+
children: /* @__PURE__ */ jsx(motion.span, {
|
|
573
|
+
initial: { opacity: 0 },
|
|
574
|
+
animate: { opacity: 1 },
|
|
575
|
+
exit: { opacity: 0 },
|
|
576
|
+
transition: { duration: .1 },
|
|
577
|
+
style: pillTextStyle,
|
|
578
|
+
children: picking ? "Picking…" : hasModifications ? `${propertyLabel} ${inputValue || "0"}` : "Tweaker"
|
|
579
|
+
}, picking ? "picking" : hasModifications ? "value" : "idle")
|
|
580
|
+
})
|
|
581
|
+
})] });
|
|
582
|
+
};
|
|
583
|
+
const pillStyle = {
|
|
584
|
+
position: "fixed",
|
|
585
|
+
left: 16,
|
|
586
|
+
bottom: 16,
|
|
587
|
+
zIndex: 9999,
|
|
588
|
+
background: "#1A1A1A",
|
|
589
|
+
color: "color(display-p3 1 1 1)",
|
|
590
|
+
borderRadius: 9999,
|
|
591
|
+
height: 28,
|
|
592
|
+
padding: "0 12px",
|
|
593
|
+
fontSize: 11,
|
|
594
|
+
fontFamily: "system-ui, sans-serif",
|
|
595
|
+
fontWeight: 500,
|
|
596
|
+
letterSpacing: "-0.03em",
|
|
597
|
+
fontSynthesis: "none",
|
|
598
|
+
WebkitFontSmoothing: "antialiased",
|
|
599
|
+
width: "fit-content",
|
|
600
|
+
boxShadow: "0 4px 24px #0000004D",
|
|
601
|
+
display: "flex",
|
|
602
|
+
alignItems: "center",
|
|
603
|
+
justifyContent: "center"
|
|
604
|
+
};
|
|
605
|
+
const pillTextStyle = {
|
|
606
|
+
fontSize: 11,
|
|
607
|
+
fontWeight: 500,
|
|
608
|
+
color: "color(display-p3 1 1 1)",
|
|
609
|
+
whiteSpace: "nowrap"
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
//#endregion
|
|
613
|
+
export { GRAY_SCALES, Tweaker };
|
|
614
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/gray-scales.ts","../src/constants.ts","../src/utils/color.ts","../src/utils/dom.ts","../src/utils/modification.ts","../src/utils/prompt.ts","../src/tweaker.tsx"],"sourcesContent":["import type { GrayScale } from \"./types\";\n\nexport const GRAY_SCALES: Record<string, GrayScale> = {\n neutral: {\n label: \"Neutral\",\n shades: {\n \"50\": \"oklch(0.985 0 0)\",\n \"100\": \"oklch(0.97 0 0)\",\n \"200\": \"oklch(0.922 0 0)\",\n \"300\": \"oklch(0.87 0 0)\",\n \"400\": \"oklch(0.708 0 0)\",\n \"500\": \"oklch(0.556 0 0)\",\n \"600\": \"oklch(0.439 0 0)\",\n \"700\": \"oklch(0.371 0 0)\",\n \"800\": \"oklch(0.269 0 0)\",\n \"900\": \"oklch(0.205 0 0)\",\n \"950\": \"oklch(0.145 0 0)\",\n },\n },\n slate: {\n label: \"Slate\",\n shades: {\n \"50\": \"oklch(0.984 0.003 247.858)\",\n \"100\": \"oklch(0.968 0.007 247.896)\",\n \"200\": \"oklch(0.929 0.013 255.508)\",\n \"300\": \"oklch(0.869 0.022 252.894)\",\n \"400\": \"oklch(0.704 0.04 256.788)\",\n \"500\": \"oklch(0.554 0.046 257.417)\",\n \"600\": \"oklch(0.446 0.043 257.281)\",\n \"700\": \"oklch(0.372 0.044 257.287)\",\n \"800\": \"oklch(0.279 0.041 260.031)\",\n \"900\": \"oklch(0.208 0.042 265.755)\",\n \"950\": \"oklch(0.129 0.042 264.695)\",\n },\n },\n gray: {\n label: \"Gray\",\n shades: {\n \"50\": \"oklch(0.985 0.002 247.839)\",\n \"100\": \"oklch(0.967 0.003 264.542)\",\n \"200\": \"oklch(0.928 0.006 264.531)\",\n \"300\": \"oklch(0.872 0.01 258.338)\",\n \"400\": \"oklch(0.707 0.022 261.325)\",\n \"500\": \"oklch(0.551 0.027 264.364)\",\n \"600\": \"oklch(0.446 0.03 256.802)\",\n \"700\": \"oklch(0.373 0.034 259.733)\",\n \"800\": \"oklch(0.278 0.033 256.848)\",\n \"900\": \"oklch(0.21 0.034 264.665)\",\n \"950\": \"oklch(0.13 0.028 261.692)\",\n },\n },\n zinc: {\n label: \"Zinc\",\n shades: {\n \"50\": \"oklch(0.985 0 0)\",\n \"100\": \"oklch(0.967 0.001 286.375)\",\n \"200\": \"oklch(0.92 0.004 286.32)\",\n \"300\": \"oklch(0.871 0.006 286.286)\",\n \"400\": \"oklch(0.705 0.015 286.067)\",\n \"500\": \"oklch(0.552 0.016 285.938)\",\n \"600\": \"oklch(0.442 0.017 285.786)\",\n \"700\": \"oklch(0.37 0.013 285.805)\",\n \"800\": \"oklch(0.274 0.006 286.033)\",\n \"900\": \"oklch(0.21 0.006 285.885)\",\n \"950\": \"oklch(0.141 0.005 285.823)\",\n },\n },\n stone: {\n label: \"Stone\",\n shades: {\n \"50\": \"oklch(0.985 0.001 106.423)\",\n \"100\": \"oklch(0.97 0.001 106.424)\",\n \"200\": \"oklch(0.923 0.003 48.717)\",\n \"300\": \"oklch(0.869 0.005 56.366)\",\n \"400\": \"oklch(0.709 0.01 56.259)\",\n \"500\": \"oklch(0.553 0.013 58.071)\",\n \"600\": \"oklch(0.444 0.011 73.639)\",\n \"700\": \"oklch(0.374 0.01 67.558)\",\n \"800\": \"oklch(0.268 0.007 34.298)\",\n \"900\": \"oklch(0.216 0.006 56.043)\",\n \"950\": \"oklch(0.147 0.004 49.25)\",\n },\n },\n mauve: {\n label: \"Mauve\",\n shades: {\n \"50\": \"oklch(0.985 0.003 310)\",\n \"100\": \"oklch(0.968 0.006 310)\",\n \"200\": \"oklch(0.925 0.011 310)\",\n \"300\": \"oklch(0.87 0.017 310)\",\n \"400\": \"oklch(0.708 0.03 310)\",\n \"500\": \"oklch(0.556 0.03 310)\",\n \"600\": \"oklch(0.44 0.028 310)\",\n \"700\": \"oklch(0.372 0.025 310)\",\n \"800\": \"oklch(0.27 0.02 310)\",\n \"900\": \"oklch(0.208 0.018 310)\",\n \"950\": \"oklch(0.145 0.014 310)\",\n },\n },\n olive: {\n label: \"Olive\",\n shades: {\n \"50\": \"oklch(0.985 0.003 130)\",\n \"100\": \"oklch(0.968 0.006 130)\",\n \"200\": \"oklch(0.925 0.011 130)\",\n \"300\": \"oklch(0.87 0.017 130)\",\n \"400\": \"oklch(0.708 0.028 130)\",\n \"500\": \"oklch(0.556 0.028 130)\",\n \"600\": \"oklch(0.44 0.024 130)\",\n \"700\": \"oklch(0.372 0.02 130)\",\n \"800\": \"oklch(0.27 0.016 130)\",\n \"900\": \"oklch(0.208 0.014 130)\",\n \"950\": \"oklch(0.145 0.01 130)\",\n },\n },\n};\n","export const SLIDER_MAX = 10;\nexport const BAR_WIDTH_PX = 3;\nexport const TEXT_PREVIEW_MAX_LENGTH = 30;\nexport const TYPING_RESET_DELAY_MS = 1500;\nexport const SHADE_KEYS = [\"50\", \"100\", \"200\", \"300\", \"400\", \"500\", \"600\", \"700\", \"800\", \"900\", \"950\"];\n","import type { OKLCH } from \"../types\";\nimport { SHADE_KEYS, SLIDER_MAX } from \"../constants\";\nimport type { GrayScale } from \"../types\";\n\nexport const parseOklch = (oklchStr: string): OKLCH => {\n const match = oklchStr.match(/oklch\\(([\\d.]+)\\s+([\\d.]+)\\s+([\\d.]+)\\)/);\n if (!match) return [0, 0, 0];\n return [Number(match[1]), Number(match[2]), Number(match[3])];\n};\n\nexport const lerpOklch = (colorA: OKLCH, colorB: OKLCH, interpolation: number): OKLCH => [\n colorA[0] + (colorB[0] - colorA[0]) * interpolation,\n colorA[1] + (colorB[1] - colorA[1]) * interpolation,\n colorA[2] + (colorB[2] - colorA[2]) * interpolation,\n];\n\nexport const formatOklch = (oklch: OKLCH): string =>\n `oklch(${oklch[0].toFixed(3)} ${oklch[1].toFixed(3)} ${oklch[2].toFixed(1)})`;\n\nexport const oklchToCssString = (oklch: OKLCH): string =>\n `oklch(${oklch[0]} ${oklch[1]} ${oklch[2]})`;\n\nexport const getColorAtPosition = (scales: Record<string, GrayScale>, scaleKey: string, position: number): OKLCH => {\n const scale = scales[scaleKey];\n if (!scale) return [0.5, 0, 0];\n\n const inverted = SLIDER_MAX - position;\n const segment = (inverted / SLIDER_MAX) * (SHADE_KEYS.length - 1);\n const index = Math.min(Math.floor(segment), SHADE_KEYS.length - 2);\n const interpolation = segment - index;\n\n const lower = parseOklch(scale.shades[SHADE_KEYS[index]]);\n const upper = parseOklch(scale.shades[SHADE_KEYS[index + 1]]);\n\n return lerpOklch(lower, upper, interpolation);\n};\n\nexport const getClosestShadeLabel = (position: number): string => {\n const inverted = SLIDER_MAX - position;\n const segment = (inverted / SLIDER_MAX) * (SHADE_KEYS.length - 1);\n const index = Math.round(segment);\n return SHADE_KEYS[Math.min(index, SHADE_KEYS.length - 1)];\n};\n\nexport const parseRgb = (color: string): [number, number, number, number] => {\n const match = color.match(\n /rgba?\\(\\s*([\\d.]+),\\s*([\\d.]+),\\s*([\\d.]+)(?:,\\s*([\\d.]+))?\\s*\\)/\n );\n if (!match) return [0, 0, 0, 0];\n return [\n Number(match[1]),\n Number(match[2]),\n Number(match[3]),\n match[4] !== undefined ? Number(match[4]) : 1,\n ];\n};\n\nexport const rgbToOklch = (red: number, green: number, blue: number): OKLCH => {\n const linearize = (channel: number): number => {\n const normalized = channel / 255;\n return normalized <= 0.04045\n ? normalized / 12.92\n : Math.pow((normalized + 0.055) / 1.055, 2.4);\n };\n const linearRed = linearize(red);\n const linearGreen = linearize(green);\n const linearBlue = linearize(blue);\n\n const lmsL = Math.cbrt(0.4122214708 * linearRed + 0.5363325363 * linearGreen + 0.0514459929 * linearBlue);\n const lmsM = Math.cbrt(0.2119034982 * linearRed + 0.6806995451 * linearGreen + 0.1073969566 * linearBlue);\n const lmsS = Math.cbrt(0.0883024619 * linearRed + 0.2817188376 * linearGreen + 0.6299787005 * linearBlue);\n\n const lightness = 0.2104542553 * lmsL + 0.793617785 * lmsM - 0.0040720468 * lmsS;\n const labA = 1.9779984951 * lmsL - 2.428592205 * lmsM + 0.4505937099 * lmsS;\n const labB = 0.0259040371 * lmsL + 0.7827717662 * lmsM - 0.808675766 * lmsS;\n\n const chroma = Math.sqrt(labA * labA + labB * labB);\n const hue = Math.atan2(labB, labA) * (180 / Math.PI);\n\n return [lightness, chroma, hue < 0 ? hue + 360 : hue];\n};\n\nexport const findClosestPosition = (scales: Record<string, GrayScale>, scaleKey: string, targetOklch: OKLCH): number => {\n let bestPosition = 0;\n let bestDistance = Infinity;\n\n for (let position = 0; position <= SLIDER_MAX; position++) {\n const color = getColorAtPosition(scales, scaleKey, position);\n const distance =\n (color[0] - targetOklch[0]) ** 2 +\n (color[1] - targetOklch[1]) ** 2 +\n ((color[2] - targetOklch[2]) / 360) ** 2;\n if (distance < bestDistance) {\n bestDistance = distance;\n bestPosition = position;\n }\n }\n\n return bestPosition;\n};\n","import { TEXT_PREVIEW_MAX_LENGTH } from \"../constants\";\n\nexport const getSelector = (element: HTMLElement): string => {\n const tag = element.tagName.toLowerCase();\n const classes = Array.from(element.classList)\n .filter((className) => !className.startsWith(\"__\"))\n .slice(0, 2)\n .join(\".\");\n return classes ? `${tag}.${classes}` : tag;\n};\n\nexport const getTextPreview = (element: HTMLElement): string => {\n const text = element.textContent?.trim() || \"\";\n return text.length > TEXT_PREVIEW_MAX_LENGTH\n ? `${text.slice(0, TEXT_PREVIEW_MAX_LENGTH)}…`\n : text;\n};\n","import type { GrayScale, Modification } from \"../types\";\nimport { getColorAtPosition, oklchToCssString } from \"./color\";\n\nexport const applyModification = (\n modification: Modification,\n scales: Record<string, GrayScale>,\n scaleKey: string,\n) => {\n const oklch = getColorAtPosition(scales, scaleKey, modification.position);\n const colorValue = oklchToCssString(oklch);\n if (modification.property === \"bg\") {\n modification.element.style.backgroundColor = colorValue;\n } else if (modification.property === \"text\") {\n modification.element.style.color = colorValue;\n } else {\n modification.element.style.borderColor = colorValue;\n }\n};\n\nexport const restoreModification = (modification: Modification) => {\n modification.element.style.backgroundColor = modification.originalInlineBg;\n modification.element.style.color = modification.originalInlineColor;\n modification.element.style.borderColor = modification.originalInlineBorderColor;\n};\n\nexport const roundToStep = (value: number): number =>\n parseFloat((Math.round(value * 10) / 10).toFixed(1));\n","import type { GrayScale, Modification } from \"../types\";\nimport { formatOklch, getColorAtPosition, getClosestShadeLabel } from \"./color\";\n\nexport const generatePrompt = (\n modifications: Modification[],\n scales: Record<string, GrayScale>,\n scaleKey: string,\n): string => {\n if (modifications.length === 0) return \"\";\n\n const scaleName = scales[scaleKey]?.label || scaleKey;\n const lines: string[] = [];\n\n modifications.forEach((modification) => {\n const shade = getClosestShadeLabel(modification.position);\n const oklch = getColorAtPosition(scales, scaleKey, modification.position);\n const nameParts = [modification.selector];\n if (modification.componentName) nameParts.unshift(`<${modification.componentName}>`);\n if (modification.textPreview) nameParts.push(`(\"${modification.textPreview}\")`);\n const description = nameParts.join(\" \");\n const property =\n modification.property === \"bg\"\n ? \"background color\"\n : modification.property === \"text\"\n ? \"text color\"\n : \"border color\";\n lines.push(`- ${property} of ${description} → ${scaleName} ${shade} (${formatOklch(oklch)})`);\n if (modification.sourceFile) lines.push(` Source: ${modification.sourceFile}`);\n });\n\n return [\n \"Change the following colors using the design system's gray scale:\",\n \"\",\n ...lines,\n ].join(\"\\n\");\n};\n","import { useState, useCallback, useEffect, useRef } from \"react\";\nimport { motion, useMotionValue, useTransform, AnimatePresence } from \"motion/react\";\nimport type { Modification, TweakerProps } from \"./types\";\nimport { GRAY_SCALES } from \"./gray-scales\";\nimport { SLIDER_MAX, BAR_WIDTH_PX, TYPING_RESET_DELAY_MS } from \"./constants\";\nimport { getColorAtPosition, oklchToCssString, parseRgb, rgbToOklch, findClosestPosition } from \"./utils/color\";\nimport { getSelector, getTextPreview } from \"./utils/dom\";\nimport { applyModification, restoreModification, roundToStep } from \"./utils/modification\";\nimport { generatePrompt } from \"./utils/prompt\";\n\nexport const Tweaker = ({ scales = GRAY_SCALES, activeScale = \"neutral\" }: TweakerProps) => {\n const [picking, setPicking] = useState(false);\n const [modifications, setModifications] = useState<Modification[]>([]);\n const [activeIndex, setActiveIndex] = useState(-1);\n const [inputValue, setInputValue] = useState(\"\");\n const typingBuffer = useRef(\"\");\n const typingTimeout = useRef<ReturnType<typeof setTimeout>>(undefined);\n const fillPercent = useMotionValue(0);\n const fillHeight = useTransform(fillPercent, (percent) => `${percent}%`);\n\n const activeMod = activeIndex >= 0 ? modifications[activeIndex] : null;\n const hasModifications = modifications.length > 0;\n\n const activeIndexRef = useRef(activeIndex);\n const activeScaleRef = useRef(activeScale);\n const modificationsRef = useRef(modifications);\n const scalesRef = useRef(scales);\n activeIndexRef.current = activeIndex;\n activeScaleRef.current = activeScale;\n modificationsRef.current = modifications;\n scalesRef.current = scales;\n\n useEffect(() => {\n if (activeMod) {\n fillPercent.jump((activeMod.position / SLIDER_MAX) * 100);\n }\n }, [activeMod?.position, fillPercent]);\n\n const updateActivePosition = useCallback(\n (newPosition: number) => {\n if (activeIndex < 0) return;\n setModifications((previous) => {\n const updated = [...previous];\n updated[activeIndex] = { ...updated[activeIndex], position: newPosition };\n applyModification(updated[activeIndex], scales, activeScale);\n return updated;\n });\n },\n [activeIndex, activeScale, scales],\n );\n\n useEffect(() => {\n if (!hasModifications || picking) return;\n\n const updateFromY = (clientY: number) => {\n const percent = Math.max(0, Math.min(1, 1 - clientY / window.innerHeight));\n const value = roundToStep(percent * SLIDER_MAX);\n const index = activeIndexRef.current;\n if (index < 0) return;\n fillPercent.jump(percent * 100);\n setModifications((previous) => {\n const updated = [...previous];\n updated[index] = { ...updated[index], position: value };\n applyModification(updated[index], scalesRef.current, activeScaleRef.current);\n return updated;\n });\n setInputValue(String(value));\n };\n\n const handlePointerMove = (event: PointerEvent) => {\n updateFromY(event.clientY);\n };\n\n document.addEventListener(\"pointermove\", handlePointerMove, true);\n return () => {\n document.removeEventListener(\"pointermove\", handlePointerMove, true);\n };\n }, [hasModifications, picking, fillPercent]);\n\n useEffect(() => {\n if (!hasModifications) return;\n\n const handleKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n event.preventDefault();\n const prompt = generatePrompt(modificationsRef.current, scalesRef.current, activeScaleRef.current);\n navigator.clipboard.writeText(prompt);\n modificationsRef.current.forEach(restoreModification);\n setModifications([]);\n setActiveIndex(-1);\n setInputValue(\"\");\n }\n\n if (event.key === \" \") {\n event.preventDefault();\n const prompt = generatePrompt(modificationsRef.current, scalesRef.current, activeScaleRef.current);\n navigator.clipboard.writeText(prompt);\n setPicking(true);\n }\n };\n\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [hasModifications]);\n\n useEffect(() => {\n if (!activeMod || picking) return;\n\n const handleKeyDown = (event: KeyboardEvent) => {\n const target = event.target as HTMLElement;\n if (target.tagName === \"INPUT\" || target.tagName === \"TEXTAREA\") return;\n if (event.key === \"Escape\") return;\n\n if ((event.key >= \"0\" && event.key <= \"9\") || event.key === \".\") {\n event.preventDefault();\n const next = typingBuffer.current + event.key;\n\n if (event.key === \".\" && typingBuffer.current.includes(\".\")) return;\n\n const hasDecimal = typingBuffer.current.includes(\".\");\n if (hasDecimal && event.key !== \".\" && typingBuffer.current.split(\".\")[1]?.length >= 1) {\n return;\n }\n\n const parsed = parseFloat(next);\n if (!isNaN(parsed) && parsed > SLIDER_MAX) {\n typingBuffer.current = event.key === \".\" ? \".\" : event.key;\n } else {\n typingBuffer.current = next;\n }\n\n const value = parseFloat(typingBuffer.current);\n if (!isNaN(value)) {\n const clamped = Math.min(SLIDER_MAX, value);\n updateActivePosition(clamped);\n setInputValue(typingBuffer.current);\n }\n\n clearTimeout(typingTimeout.current);\n typingTimeout.current = setTimeout(() => {\n typingBuffer.current = \"\";\n }, TYPING_RESET_DELAY_MS);\n }\n\n if (event.key === \"Backspace\") {\n event.preventDefault();\n typingBuffer.current = typingBuffer.current.slice(0, -1);\n if (typingBuffer.current && typingBuffer.current !== \".\") {\n const value = Math.min(SLIDER_MAX, parseFloat(typingBuffer.current));\n updateActivePosition(value);\n setInputValue(typingBuffer.current);\n }\n clearTimeout(typingTimeout.current);\n typingTimeout.current = setTimeout(() => {\n typingBuffer.current = \"\";\n }, TYPING_RESET_DELAY_MS);\n }\n };\n\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => {\n document.removeEventListener(\"keydown\", handleKeyDown);\n clearTimeout(typingTimeout.current);\n };\n }, [activeMod, picking, activeIndex, updateActivePosition]);\n\n useEffect(() => {\n const handleKeyDown = (event: KeyboardEvent) => {\n const target = event.target as HTMLElement;\n if (target.tagName === \"INPUT\" || target.tagName === \"TEXTAREA\") return;\n if (event.key === \"t\") {\n event.preventDefault();\n setPicking(true);\n }\n if (hasModifications && (event.key === \"b\" || event.key === \"f\" || event.key === \"d\")) {\n event.preventDefault();\n const property: \"bg\" | \"text\" | \"border\" =\n event.key === \"b\" ? \"bg\" : event.key === \"f\" ? \"text\" : \"border\";\n const index = activeIndexRef.current;\n if (index < 0) return;\n setModifications((previous) => {\n const updated = [...previous];\n restoreModification(updated[index]);\n updated[index] = { ...updated[index], property };\n applyModification(updated[index], scalesRef.current, activeScaleRef.current);\n return updated;\n });\n }\n };\n\n document.addEventListener(\"keydown\", handleKeyDown);\n return () => document.removeEventListener(\"keydown\", handleKeyDown);\n }, [hasModifications]);\n\n useEffect(() => {\n return () => {\n modifications.forEach(restoreModification);\n };\n }, []);\n\n useEffect(() => {\n if (activeMod) {\n applyModification(activeMod, scales, activeScale);\n }\n }, [activeMod?.position, activeScale, scales]);\n\n useEffect(() => {\n if (!picking) return;\n\n let hoveredElement: HTMLElement | null = null;\n\n const handleMouseOver = (event: MouseEvent) => {\n const target = event.target as HTMLElement;\n if (target.closest(\"[data-tweaker]\")) return;\n hoveredElement = target;\n target.style.outline = \"2px solid #3b82f6\";\n target.style.outlineOffset = \"2px\";\n };\n\n const handleMouseOut = (event: MouseEvent) => {\n const target = event.target as HTMLElement;\n target.style.outline = \"\";\n target.style.outlineOffset = \"\";\n if (hoveredElement === target) hoveredElement = null;\n };\n\n const handleClick = (event: MouseEvent) => {\n event.preventDefault();\n event.stopPropagation();\n const target = event.target as HTMLElement;\n if (target.closest(\"[data-tweaker]\")) return;\n\n target.style.outline = \"\";\n target.style.outlineOffset = \"\";\n\n const computed = getComputedStyle(target);\n const [bgRed, bgGreen, bgBlue, bgAlpha] = parseRgb(computed.backgroundColor);\n const [textRed, textGreen, textBlue] = parseRgb(computed.color);\n const [borderRed, borderGreen, borderBlue, borderAlpha] = parseRgb(computed.borderColor);\n const hasBorder = borderAlpha > 0 && parseFloat(computed.borderWidth) > 0;\n\n const hasBackground = bgAlpha > 0;\n const defaultProperty: \"bg\" | \"text\" | \"border\" = hasBackground ? \"bg\" : hasBorder ? \"border\" : \"text\";\n const targetOklch =\n defaultProperty === \"bg\"\n ? rgbToOklch(bgRed, bgGreen, bgBlue)\n : defaultProperty === \"border\"\n ? rgbToOklch(borderRed, borderGreen, borderBlue)\n : rgbToOklch(textRed, textGreen, textBlue);\n\n const position = findClosestPosition(scales, activeScale, targetOklch);\n\n const newModification: Modification = {\n element: target,\n selector: getSelector(target),\n componentName: null,\n sourceFile: null,\n textPreview: getTextPreview(target),\n originalInlineBg: target.style.backgroundColor,\n originalInlineColor: target.style.color,\n originalInlineBorderColor: target.style.borderColor,\n property: defaultProperty,\n position,\n };\n\n setModifications((previous) => [...previous, newModification]);\n setActiveIndex(modifications.length);\n setInputValue(String(position));\n setPicking(false);\n };\n\n document.addEventListener(\"mouseover\", handleMouseOver, true);\n document.addEventListener(\"mouseout\", handleMouseOut, true);\n document.addEventListener(\"click\", handleClick, true);\n\n return () => {\n document.removeEventListener(\"mouseover\", handleMouseOver, true);\n document.removeEventListener(\"mouseout\", handleMouseOut, true);\n document.removeEventListener(\"click\", handleClick, true);\n if (hoveredElement) {\n hoveredElement.style.outline = \"\";\n hoveredElement.style.outlineOffset = \"\";\n }\n };\n }, [picking, activeScale, scales, modifications.length]);\n\n const fillColor = activeMod\n ? oklchToCssString(getColorAtPosition(scales, activeScale, activeMod.position))\n : scales[activeScale]?.shades[\"500\"] ?? \"rgba(255,255,255,0.3)\";\n\n const propertyLabel =\n activeMod?.property === \"text\" ? \"F\" : activeMod?.property === \"border\" ? \"D\" : \"B\";\n\n return (\n <>\n <AnimatePresence>\n {hasModifications && !picking && (\n <motion.div\n key=\"bar\"\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.2 }}\n data-tweaker\n style={{\n position: \"fixed\",\n top: 0,\n right: 0,\n width: BAR_WIDTH_PX,\n height: \"100vh\",\n zIndex: 9999,\n pointerEvents: \"none\",\n }}\n >\n <motion.div\n style={{\n position: \"absolute\",\n left: 0,\n right: 0,\n bottom: 0,\n height: fillHeight,\n background: fillColor,\n }}\n />\n </motion.div>\n )}\n </AnimatePresence>\n\n <motion.div\n data-tweaker\n layout\n style={{\n ...pillStyle,\n ...(!hasModifications ? { cursor: \"pointer\" } : {}),\n }}\n animate={{\n backgroundColor: scales[activeScale]?.shades[\"900\"] ?? \"#1A1A1A\",\n }}\n transition={{\n layout: { type: \"spring\", visualDuration: 0.3, bounce: 0.15 },\n backgroundColor: { duration: 0.3 },\n }}\n onClick={!hasModifications ? () => setPicking(!picking) : undefined}\n >\n <AnimatePresence mode=\"wait\">\n <motion.span\n key={picking ? \"picking\" : hasModifications ? \"value\" : \"idle\"}\n initial={{ opacity: 0 }}\n animate={{ opacity: 1 }}\n exit={{ opacity: 0 }}\n transition={{ duration: 0.1 }}\n style={pillTextStyle}\n >\n {picking\n ? \"Picking…\"\n : hasModifications\n ? `${propertyLabel} ${inputValue || \"0\"}`\n : \"Tweaker\"}\n </motion.span>\n </AnimatePresence>\n </motion.div>\n </>\n );\n};\n\nconst pillStyle: React.CSSProperties = {\n position: \"fixed\",\n left: 16,\n bottom: 16,\n zIndex: 9999,\n background: \"#1A1A1A\",\n color: \"color(display-p3 1 1 1)\",\n borderRadius: 9999,\n height: 28,\n padding: \"0 12px\",\n fontSize: 11,\n fontFamily: \"system-ui, sans-serif\",\n fontWeight: 500,\n letterSpacing: \"-0.03em\",\n fontSynthesis: \"none\",\n WebkitFontSmoothing: \"antialiased\",\n width: \"fit-content\",\n boxShadow: \"0 4px 24px #0000004D\",\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n};\n\nconst pillTextStyle: React.CSSProperties = {\n fontSize: 11,\n fontWeight: 500,\n color: \"color(display-p3 1 1 1)\",\n whiteSpace: \"nowrap\",\n};\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ben-million/tweaker",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"description": "A dev tool for tweaking colors along gray scales in React apps",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"react",
|
|
7
|
+
"dev-tools",
|
|
8
|
+
"color",
|
|
9
|
+
"gray-scale",
|
|
10
|
+
"design-system"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://github.com/millionco/tweaker#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/millionco/tweaker/issues"
|
|
15
|
+
},
|
|
16
|
+
"license": "MIT",
|
|
17
|
+
"author": "Million Co.",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/millionco/tweaker.git",
|
|
21
|
+
"directory": "packages/tweaker"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
26
|
+
"type": "module",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"default": "./dist/index.js"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"dev": "tsdown --watch",
|
|
35
|
+
"build": "rm -rf dist && NODE_ENV=production tsdown",
|
|
36
|
+
"typecheck": "tsc --noEmit"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"motion": "^12.0.0"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"react": ">=18",
|
|
43
|
+
"react-dom": ">=18"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/react": "^19",
|
|
47
|
+
"@types/react-dom": "^19",
|
|
48
|
+
"tsdown": "^0.20.3"
|
|
49
|
+
}
|
|
50
|
+
}
|