@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.
@@ -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
+ }