tweakit 0.5.2 → 0.7.0

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.
Files changed (120) hide show
  1. package/AGENTS.md +32 -30
  2. package/README.md +55 -29
  3. package/THIRD-PARTY-NOTICES.md +0 -1
  4. package/dist/tweaks/auto.js +2 -0
  5. package/dist/tweaks/auto.js.map +6 -0
  6. package/dist/tweaks/bezier-S2BOXQBM.js +2 -0
  7. package/dist/tweaks/bezier-S2BOXQBM.js.map +6 -0
  8. package/dist/tweaks/chunk-3MZAAMC2.js +2 -0
  9. package/dist/tweaks/chunk-3MZAAMC2.js.map +6 -0
  10. package/dist/tweaks/chunk-ETB35KFV.js +2 -0
  11. package/dist/tweaks/chunk-ETB35KFV.js.map +6 -0
  12. package/dist/tweaks/chunk-YB5GKWZI.js +2 -0
  13. package/dist/tweaks/chunk-YB5GKWZI.js.map +6 -0
  14. package/dist/tweaks/clip-A3VI53BK.js +2 -0
  15. package/dist/tweaks/clip-A3VI53BK.js.map +6 -0
  16. package/dist/tweaks/color-UGYMAF3W.js +2 -0
  17. package/dist/tweaks/color-UGYMAF3W.js.map +6 -0
  18. package/dist/tweaks/core.js +2 -1
  19. package/dist/tweaks/core.js.map +6 -0
  20. package/dist/tweaks/fold-GY47HIQA.js +2 -0
  21. package/dist/tweaks/fold-GY47HIQA.js.map +6 -0
  22. package/dist/tweaks/gradient-BTX25WSH.js +2 -0
  23. package/dist/tweaks/gradient-BTX25WSH.js.map +6 -0
  24. package/dist/tweaks/heavy-NPFIGHRM.js +2 -0
  25. package/dist/tweaks/heavy-NPFIGHRM.js.map +6 -0
  26. package/dist/tweaks/image-HTQQ4WX6.js +4 -0
  27. package/dist/tweaks/image-HTQQ4WX6.js.map +6 -0
  28. package/dist/tweaks/image-store-QD5SMZXG.js +2 -0
  29. package/dist/tweaks/image-store-QD5SMZXG.js.map +6 -0
  30. package/dist/tweaks/interval-BHPI4LRN.js +2 -0
  31. package/dist/tweaks/interval-BHPI4LRN.js.map +6 -0
  32. package/dist/tweaks/markup-QM53TQJZ.js +2 -0
  33. package/dist/tweaks/markup-QM53TQJZ.js.map +6 -0
  34. package/dist/tweaks/monitor-722WIYGW.js +3 -0
  35. package/dist/tweaks/monitor-722WIYGW.js.map +6 -0
  36. package/dist/tweaks/motion-HMCZK6HJ.js +2 -0
  37. package/dist/tweaks/motion-HMCZK6HJ.js.map +6 -0
  38. package/dist/tweaks/plot-D3NQ6XNN.js +3 -0
  39. package/dist/tweaks/plot-D3NQ6XNN.js.map +6 -0
  40. package/dist/tweaks/point-XDXKMDXH.js +2 -0
  41. package/dist/tweaks/point-XDXKMDXH.js.map +6 -0
  42. package/dist/tweaks/presets-EUWNSCUW.js +2 -0
  43. package/dist/tweaks/presets-EUWNSCUW.js.map +6 -0
  44. package/dist/tweaks/shadow-AM7VSRNS.js +2 -0
  45. package/dist/tweaks/shadow-AM7VSRNS.js.map +6 -0
  46. package/dist/tweaks/shadow-root-RYUV3AGS.js +3 -0
  47. package/dist/tweaks/shadow-root-RYUV3AGS.js.map +6 -0
  48. package/dist/tweaks/sides-JWIURDA4.js +2 -0
  49. package/dist/tweaks/sides-JWIURDA4.js.map +6 -0
  50. package/dist/tweaks/spring-BF5GBTPM.js +2 -0
  51. package/dist/tweaks/spring-BF5GBTPM.js.map +6 -0
  52. package/dist/tweaks/tabs-PRVUUZXZ.js +2 -0
  53. package/dist/tweaks/tabs-PRVUUZXZ.js.map +6 -0
  54. package/dist/tweaks.css +1 -1
  55. package/dist/tweaks.js +7 -3
  56. package/dist/tweaks.js.map +6 -0
  57. package/dist/types/tweaks/easing.d.ts +8 -11
  58. package/dist/types/tweaks/enhance.d.ts +3 -2
  59. package/dist/types/tweaks/panel.d.ts +6 -4
  60. package/dist/types/tweaks/schema.d.ts +4 -8
  61. package/dist/types/tweaks/standalone.d.ts +3 -3
  62. package/dist/types/tweaks/types.d.ts +499 -116
  63. package/package.json +30 -5
  64. package/src/tweaks/auto.ts +16 -0
  65. package/src/tweaks/clip.ts +156 -0
  66. package/src/tweaks/controls/basic.ts +727 -0
  67. package/src/tweaks/controls/bezier.ts +204 -0
  68. package/src/tweaks/controls/color.ts +861 -0
  69. package/src/tweaks/controls/gradient.ts +419 -0
  70. package/src/tweaks/controls/image.ts +555 -0
  71. package/src/tweaks/controls/interval.ts +189 -0
  72. package/src/tweaks/controls/monitor.ts +211 -0
  73. package/src/tweaks/controls/motion.ts +316 -0
  74. package/src/tweaks/controls/plot.ts +440 -0
  75. package/src/tweaks/controls/point.ts +208 -0
  76. package/src/tweaks/controls/shadow.ts +156 -0
  77. package/src/tweaks/controls/sides.ts +196 -0
  78. package/src/tweaks/controls/spring.ts +308 -0
  79. package/src/tweaks/controls/tabs.ts +68 -0
  80. package/src/tweaks/core.ts +9 -0
  81. package/src/tweaks/easing.ts +118 -0
  82. package/src/tweaks/enhance.ts +35 -0
  83. package/src/tweaks/feedback.ts +218 -0
  84. package/src/tweaks/fold.ts +33 -0
  85. package/src/tweaks/heavy.ts +626 -0
  86. package/src/tweaks/icons.ts +26 -0
  87. package/src/tweaks/image-store.ts +189 -0
  88. package/src/tweaks/lazy.ts +63 -0
  89. package/src/tweaks/markup.ts +317 -0
  90. package/src/tweaks/panel.ts +1693 -0
  91. package/src/tweaks/presets.ts +85 -0
  92. package/src/tweaks/schema.ts +351 -0
  93. package/src/tweaks/shadow-root.ts +178 -0
  94. package/src/tweaks/shared.ts +1137 -0
  95. package/src/tweaks/single.ts +22 -0
  96. package/src/tweaks/standalone.ts +179 -0
  97. package/src/tweaks/types.ts +1027 -0
  98. package/src/tweaks/undo.ts +104 -0
  99. package/src/tweaks.css +965 -0
  100. package/src/wide-gamut.ts +658 -0
  101. package/dist/tweaks/bezier-2RABC6LD.js +0 -1
  102. package/dist/tweaks/chunk-4ATI34PB.js +0 -1
  103. package/dist/tweaks/chunk-7OSCMZ2J.js +0 -1
  104. package/dist/tweaks/chunk-W3JNIJ2F.js +0 -1
  105. package/dist/tweaks/clip-ECGCECEC.js +0 -1
  106. package/dist/tweaks/colour-2RE3FQ5E.js +0 -1
  107. package/dist/tweaks/fold-UGN7TXGX.js +0 -1
  108. package/dist/tweaks/gradient-DB6XTDHO.js +0 -1
  109. package/dist/tweaks/heavy-PKOMHB3C.js +0 -1
  110. package/dist/tweaks/image-7MCLBOZ3.js +0 -1
  111. package/dist/tweaks/interval-L2E4L3AZ.js +0 -1
  112. package/dist/tweaks/markup-ZJJ6CCWM.js +0 -1
  113. package/dist/tweaks/monitor-IEK742PG.js +0 -2
  114. package/dist/tweaks/motion-N5UNVR55.js +0 -1
  115. package/dist/tweaks/plot-VVTOY3RA.js +0 -2
  116. package/dist/tweaks/point-YMMHSLO7.js +0 -1
  117. package/dist/tweaks/shadow-IKYOV3VN.js +0 -1
  118. package/dist/tweaks/sides-FH227Z54.js +0 -1
  119. package/dist/tweaks/spring-3PKAFIH7.js +0 -1
  120. package/dist/tweaks/tabs-BKK5NIIY.js +0 -1
@@ -0,0 +1,658 @@
1
+ /* Wide-gamut color engine and mode model for the OKLCH color picker: CSS Color 4
2
+ * conversions hubbed through CIE XYZ, gamut mapping, and the channel/mode tables. */
3
+ import type { ColorMode, GradientInterpolation } from "./tweaks/types.js";
4
+
5
+ /** Three channels in some space — RGB, XYZ, OKLab/OKLCH, Lab/LCH, HSL/HWB. */
6
+ export type Vec3 = [number, number, number];
7
+ type Mat3 = [Vec3, Vec3, Vec3];
8
+ /** The spaces convert() hubs between, through CIE XYZ. */
9
+ export type Space =
10
+ | "srgb"
11
+ | "p3"
12
+ | "rec2020"
13
+ | "prophoto-rgb"
14
+ | "a98-rgb"
15
+ | "srgb-linear"
16
+ | "xyz-d65"
17
+ | "xyz-d50"
18
+ | "oklab"
19
+ | "oklch"
20
+ | "lab"
21
+ | "lch"
22
+ | "hsl"
23
+ | "hwb";
24
+ /** The RGB gamuts a color can be mapped into. */
25
+ export type Gamut = "srgb" | "p3" | "rec2020";
26
+ /** One editable channel of a mode: label, display range and step, engine-to-display scale. */
27
+ export interface Channel {
28
+ k: string;
29
+ min: number;
30
+ max: number;
31
+ step: number;
32
+ scale: number;
33
+ }
34
+
35
+ const DEG = 180 / Math.PI;
36
+ const RAD = Math.PI / 180;
37
+ const mul = (m: Mat3, v: Vec3): Vec3 => [
38
+ m[0][0] * v[0] + m[0][1] * v[1] + m[0][2] * v[2],
39
+ m[1][0] * v[0] + m[1][1] * v[1] + m[1][2] * v[2],
40
+ m[2][0] * v[0] + m[2][1] * v[1] + m[2][2] * v[2],
41
+ ];
42
+ const mulMat = (a: Mat3, b: Mat3): Mat3 => {
43
+ const o: Mat3 = [
44
+ [0, 0, 0],
45
+ [0, 0, 0],
46
+ [0, 0, 0],
47
+ ];
48
+ for (let i = 0; i < 3; i++)
49
+ for (let j = 0; j < 3; j++) o[i][j] = a[i][0] * b[0][j] + a[i][1] * b[1][j] + a[i][2] * b[2][j];
50
+ return o;
51
+ };
52
+ // Non-finite channels (null, NaN, ±Infinity) fall to 0; an infinite chroma would hang toGamut's bisection.
53
+ export const num = (x: number) => (Number.isFinite(x) ? x : 0);
54
+ const clamp = (x: number, lo: number, hi: number) => Math.min(hi, Math.max(lo, x));
55
+
56
+ // ── transfer functions ──
57
+ const srgbLin = (c: number) => {
58
+ const a = Math.abs(c);
59
+ return a <= 0.04045 ? c / 12.92 : Math.sign(c) * ((a + 0.055) / 1.055) ** 2.4;
60
+ };
61
+ const srgbGam = (c: number) => {
62
+ const a = Math.abs(c);
63
+ return a <= 0.0031308 ? c * 12.92 : Math.sign(c) * (1.055 * a ** (1 / 2.4) - 0.055);
64
+ };
65
+ const REC_A = 1.09929682680944,
66
+ REC_B = 0.018053968510807; // BT.2020 piecewise OETF constants, not a pure gamma
67
+ const rec2020Lin = (c: number) => {
68
+ const a = Math.abs(c);
69
+ return a < REC_B * 4.5 ? c / 4.5 : Math.sign(c) * ((a + REC_A - 1) / REC_A) ** (1 / 0.45);
70
+ };
71
+ const rec2020Gam = (c: number) => {
72
+ const a = Math.abs(c);
73
+ return a < REC_B ? c * 4.5 : Math.sign(c) * (REC_A * a ** 0.45 - (REC_A - 1));
74
+ };
75
+ const PRO_ET = 1 / 512;
76
+ const prophotoLin = (c: number) => {
77
+ const a = Math.abs(c);
78
+ return a <= PRO_ET * 16 ? c / 16 : Math.sign(c) * a ** 1.8;
79
+ };
80
+ const prophotoGam = (c: number) => {
81
+ const a = Math.abs(c);
82
+ return a >= PRO_ET ? Math.sign(c) * a ** (1 / 1.8) : 16 * c;
83
+ };
84
+
85
+ // ── matrices (D65 unless noted) ──
86
+ const LIN_SRGB_TO_XYZ: Mat3 = [
87
+ [0.41239079926595934, 0.357584339383878, 0.1804807884018343],
88
+ [0.21263900587151027, 0.715168678767756, 0.07219231536073371],
89
+ [0.01933081871559182, 0.11919477979462598, 0.9505321522496607],
90
+ ];
91
+ const XYZ_TO_LIN_SRGB: Mat3 = [
92
+ [3.2409699419045226, -1.537383177570094, -0.4986107602930034],
93
+ [-0.9692436362808796, 1.8759675015077202, 0.04155505740717559],
94
+ [0.05563007969699366, -0.20397695888897652, 1.0569715142428786],
95
+ ];
96
+ const LIN_P3_TO_XYZ: Mat3 = [
97
+ [0.4865709486482162, 0.26566769316909306, 0.19821728523436247],
98
+ [0.2289745640697488, 0.6917385218365064, 0.079286914093745],
99
+ [0, 0.04511338185890264, 1.043944368900976],
100
+ ];
101
+ const XYZ_TO_LIN_P3: Mat3 = [
102
+ [2.493496911941425, -0.9313836179191239, -0.40271078445071684],
103
+ [-0.8294889695615747, 1.7626640603183463, 0.023624685841943577],
104
+ [0.03584583024378447, -0.07617238926804182, 0.9568845240076872],
105
+ ];
106
+ const LIN_REC2020_TO_XYZ: Mat3 = [
107
+ [0.6369580483012914, 0.14461690358620832, 0.16888097516417205],
108
+ [0.2627002120112671, 0.6779980715188708, 0.05930171646986196],
109
+ [0, 0.028072693049087428, 1.060985057710791],
110
+ ];
111
+ const XYZ_TO_LIN_REC2020: Mat3 = [
112
+ [1.7166511879712674, -0.35567078377639233, -0.25336628137365974],
113
+ [-0.6666843518324892, 1.6164812366349395, 0.01576854581391113],
114
+ [0.017639857445310783, -0.042770613257808524, 0.9421031212354738],
115
+ ];
116
+ const LIN_PRO_TO_XYZ_D50: Mat3 = [
117
+ [0.7977604896723027, 0.13518583717574031, 0.0313493495815248],
118
+ [0.2880711282292934, 0.7118432178101014, 0.00008565396060525902],
119
+ [0, 0, 0.8251046025104601],
120
+ ];
121
+ const XYZ_D50_TO_LIN_PRO: Mat3 = [
122
+ [1.3457989731028281, -0.25558010007997534, -0.05110628506753401],
123
+ [-0.5446224939028347, 1.5082327413132781, 0.02053603239147973],
124
+ [0, 0, 1.2119675456389454],
125
+ ];
126
+ // CSS Color 4's a98-rgb (Adobe RGB 1998): a pure 563/256 gamma over these primaries, D65.
127
+ const A98_GAMMA = 563 / 256,
128
+ a98Lin = (c: number) => Math.sign(c) * Math.abs(c) ** A98_GAMMA,
129
+ a98Gam = (c: number) => Math.sign(c) * Math.abs(c) ** (1 / A98_GAMMA);
130
+ const LIN_A98_TO_XYZ: Mat3 = [
131
+ [573536 / 994567, 263643 / 1420810, 187206 / 994567],
132
+ [591459 / 1989134, 6239551 / 9945670, 374412 / 4972835],
133
+ [53769 / 1989134, 351524 / 4972835, 4929758 / 4972835],
134
+ ];
135
+ const XYZ_TO_LIN_A98: Mat3 = [
136
+ [1829569 / 896150, -506331 / 896150, -308931 / 896150],
137
+ [-851781 / 878810, 1648619 / 878810, 36519 / 878810],
138
+ [16779 / 1248040, -147721 / 1248040, 1266979 / 1248040],
139
+ ];
140
+ const same = (c: number) => c;
141
+ const XYZ_D65_TO_D50: Mat3 = [
142
+ [1.0479298208405488, 0.022946793341019088, -0.05019222954313557],
143
+ [0.029627815688159344, 0.990434484573249, -0.01707382502938514],
144
+ [-0.009243058152591178, 0.015055144896577895, 0.7518742899580008],
145
+ ];
146
+ const XYZ_D50_TO_D65: Mat3 = [
147
+ [0.9554734527042182, -0.023098536874261423, 0.0632593086610217],
148
+ [-0.028369706963208136, 1.0099954580058226, 0.021041398966943008],
149
+ [0.012314001688319899, -0.020507696433477912, 1.3303659366080753],
150
+ ];
151
+ const XYZ_TO_LMS: Mat3 = [
152
+ [0.819022437996703, 0.3619062600528904, -0.1288737815209879],
153
+ [0.0329836539323885, 0.9292868615863434, 0.0361446663506424],
154
+ [0.0481771893596242, 0.2642395317527308, 0.6335478284694309],
155
+ ];
156
+ const LMS_TO_XYZ: Mat3 = [
157
+ [1.2268798758459243, -0.5578149944602171, 0.2813910456659647],
158
+ [-0.0405757452148008, 1.112286803280317, -0.0717110580655164],
159
+ [-0.0763729366746601, -0.4214933324022432, 1.5869240198367816],
160
+ ];
161
+ const LMS_TO_OKLAB: Mat3 = [
162
+ [0.210454268309314, 0.7936177747023054, -0.0040720430116193],
163
+ [1.9779985324311684, -2.42859224204858, 0.450593709617411],
164
+ [0.0259040424655478, 0.7827717124575296, -0.8086757549230774],
165
+ ];
166
+ const OKLAB_TO_LMS: Mat3 = [
167
+ [1.0, 0.3963377773761749, 0.2158037573099136],
168
+ [1.0, -0.1055613458156586, -0.0638541728258133],
169
+ [1.0, -0.0894841775298119, -1.2914855480194092],
170
+ ];
171
+ const LAB_E = 216 / 24389,
172
+ LAB_K = 24389 / 27;
173
+ const WHITE_D50: Vec3 = [0.3457 / 0.3585, 1.0, (1.0 - 0.3457 - 0.3585) / 0.3585];
174
+
175
+ const rgbToXyz = (c: Vec3, lin: (c: number) => number, m: Mat3) => mul(m, [lin(c[0]), lin(c[1]), lin(c[2])]);
176
+ const xyzToRgb = (xyz: Vec3, gam: (c: number) => number, m: Mat3): Vec3 => {
177
+ const l = mul(m, xyz);
178
+ return [gam(l[0]), gam(l[1]), gam(l[2])];
179
+ };
180
+ const oklabToXyz = (lab: Vec3) => {
181
+ const p = mul(OKLAB_TO_LMS, lab);
182
+ return mul(LMS_TO_XYZ, [p[0] ** 3, p[1] ** 3, p[2] ** 3]);
183
+ };
184
+ const xyzToOklab = (xyz: Vec3) => {
185
+ const lms = mul(XYZ_TO_LMS, xyz);
186
+ return mul(LMS_TO_OKLAB, [Math.cbrt(lms[0]), Math.cbrt(lms[1]), Math.cbrt(lms[2])]);
187
+ };
188
+ function labToXyz(lab: Vec3): Vec3 {
189
+ const [L, a, b] = lab;
190
+ const fy = (L + 16) / 116,
191
+ fx = a / 500 + fy,
192
+ fz = fy - b / 200;
193
+ const x = fx ** 3 > LAB_E ? fx ** 3 : (116 * fx - 16) / LAB_K;
194
+ const y = L > LAB_K * LAB_E ? fy ** 3 : L / LAB_K;
195
+ const z = fz ** 3 > LAB_E ? fz ** 3 : (116 * fz - 16) / LAB_K;
196
+ return mul(XYZ_D50_TO_D65, [x * WHITE_D50[0], y * WHITE_D50[1], z * WHITE_D50[2]]);
197
+ }
198
+ function xyzToLab(xyz: Vec3): Vec3 {
199
+ const d50 = mul(XYZ_D65_TO_D50, xyz);
200
+ const f = (t: number) => (t > LAB_E ? Math.cbrt(t) : (LAB_K * t + 16) / 116);
201
+ const fx = f(d50[0] / WHITE_D50[0]),
202
+ fy = f(d50[1] / WHITE_D50[1]),
203
+ fz = f(d50[2] / WHITE_D50[2]);
204
+ return [116 * fy - 16, 500 * (fx - fy), 200 * (fy - fz)];
205
+ }
206
+ const toPolar = (r: Vec3): Vec3 => {
207
+ const C = Math.hypot(r[1], r[2]);
208
+ let h = Math.atan2(r[2], r[1]) * DEG;
209
+ if (h < 0) h += 360;
210
+ return [r[0], C, h];
211
+ };
212
+ const toRect = (p: Vec3): Vec3 => [p[0], p[1] * Math.cos(p[2] * RAD), p[1] * Math.sin(p[2] * RAD)];
213
+ function hslToSrgb(hsl: Vec3): Vec3 {
214
+ const h = (((Number.isNaN(hsl[0]) ? 0 : hsl[0]) % 360) + 360) % 360,
215
+ s = hsl[1] / 100,
216
+ l = hsl[2] / 100;
217
+ const f = (n: number) => {
218
+ const k = (n + h / 30) % 12,
219
+ a = s * Math.min(l, 1 - l);
220
+ return l - a * Math.max(-1, Math.min(k - 3, 9 - k, 1));
221
+ };
222
+ return [f(0), f(8), f(4)];
223
+ }
224
+ function srgbHue(rgb: Vec3) {
225
+ const max = Math.max(...rgb),
226
+ min = Math.min(...rgb),
227
+ d = max - min;
228
+ if (d < 1e-9) return NaN; // a gray; the hue would be float noise
229
+ let h: number;
230
+ if (max === rgb[0]) h = (rgb[1] - rgb[2]) / d + (rgb[1] < rgb[2] ? 6 : 0);
231
+ else if (max === rgb[1]) h = (rgb[2] - rgb[0]) / d + 2;
232
+ else h = (rgb[0] - rgb[1]) / d + 4;
233
+ return h * 60;
234
+ }
235
+ function srgbToHsl(rgb: Vec3): Vec3 {
236
+ const max = Math.max(...rgb),
237
+ min = Math.min(...rgb),
238
+ l = (min + max) / 2;
239
+ let h = srgbHue(rgb),
240
+ s = max === min || l === 0 || l === 1 ? 0 : (max - l) / Math.min(l, 1 - l);
241
+ if (s < 0) {
242
+ h += 180;
243
+ s = -s;
244
+ }
245
+ if (h >= 360) h -= 360;
246
+ return [h, s * 100, l * 100];
247
+ }
248
+ function hwbToSrgb(hwb: Vec3): Vec3 {
249
+ const w = hwb[1] / 100,
250
+ b = hwb[2] / 100;
251
+ if (w + b >= 1) {
252
+ const g = w / (w + b);
253
+ return [g, g, g];
254
+ }
255
+ const rgb = hslToSrgb([hwb[0], 100, 50]),
256
+ scale = 1 - w - b;
257
+ return [rgb[0] * scale + w, rgb[1] * scale + w, rgb[2] * scale + w];
258
+ }
259
+ const srgbToHwb = (rgb: Vec3): Vec3 => [srgbHue(rgb), Math.min(...rgb) * 100, (1 - Math.max(...rgb)) * 100];
260
+
261
+ function toXyz(c: Vec3, space: Space): Vec3 {
262
+ switch (space) {
263
+ case "srgb":
264
+ return rgbToXyz(c, srgbLin, LIN_SRGB_TO_XYZ);
265
+ case "p3":
266
+ return rgbToXyz(c, srgbLin, LIN_P3_TO_XYZ);
267
+ case "rec2020":
268
+ return rgbToXyz(c, rec2020Lin, LIN_REC2020_TO_XYZ);
269
+ case "prophoto-rgb":
270
+ return mul(XYZ_D50_TO_D65, rgbToXyz(c, prophotoLin, LIN_PRO_TO_XYZ_D50));
271
+ case "a98-rgb":
272
+ return rgbToXyz(c, a98Lin, LIN_A98_TO_XYZ);
273
+ case "srgb-linear":
274
+ return mul(LIN_SRGB_TO_XYZ, c);
275
+ case "xyz-d65":
276
+ return [c[0], c[1], c[2]];
277
+ case "xyz-d50":
278
+ return mul(XYZ_D50_TO_D65, c);
279
+ case "oklab":
280
+ return oklabToXyz(c);
281
+ case "oklch":
282
+ return oklabToXyz(toRect(c));
283
+ case "lab":
284
+ return labToXyz(c);
285
+ case "lch":
286
+ return labToXyz(toRect(c));
287
+ case "hsl":
288
+ return rgbToXyz(hslToSrgb(c), srgbLin, LIN_SRGB_TO_XYZ);
289
+ case "hwb":
290
+ return rgbToXyz(hwbToSrgb(c), srgbLin, LIN_SRGB_TO_XYZ);
291
+ }
292
+ }
293
+ function fromXyz(xyz: Vec3, space: Space): Vec3 {
294
+ switch (space) {
295
+ case "srgb":
296
+ return xyzToRgb(xyz, srgbGam, XYZ_TO_LIN_SRGB);
297
+ case "p3":
298
+ return xyzToRgb(xyz, srgbGam, XYZ_TO_LIN_P3);
299
+ case "rec2020":
300
+ return xyzToRgb(xyz, rec2020Gam, XYZ_TO_LIN_REC2020);
301
+ case "prophoto-rgb":
302
+ return xyzToRgb(mul(XYZ_D65_TO_D50, xyz), prophotoGam, XYZ_D50_TO_LIN_PRO);
303
+ case "a98-rgb":
304
+ return xyzToRgb(xyz, a98Gam, XYZ_TO_LIN_A98);
305
+ case "srgb-linear":
306
+ return xyzToRgb(xyz, same, XYZ_TO_LIN_SRGB);
307
+ case "xyz-d65":
308
+ return [xyz[0], xyz[1], xyz[2]];
309
+ case "xyz-d50":
310
+ return mul(XYZ_D65_TO_D50, xyz);
311
+ case "oklab":
312
+ return xyzToOklab(xyz);
313
+ case "oklch":
314
+ return toPolar(xyzToOklab(xyz));
315
+ case "lab":
316
+ return xyzToLab(xyz);
317
+ case "lch":
318
+ return toPolar(xyzToLab(xyz));
319
+ case "hsl":
320
+ return srgbToHsl(xyzToRgb(xyz, srgbGam, XYZ_TO_LIN_SRGB));
321
+ case "hwb":
322
+ return srgbToHwb(xyzToRgb(xyz, srgbGam, XYZ_TO_LIN_SRGB));
323
+ }
324
+ }
325
+ export function convert(coords: Vec3, from: Space, to: Space): Vec3 {
326
+ if (from === to) return [coords[0], coords[1], coords[2]];
327
+ if (from === "oklch" && to === "oklab") return toRect(coords);
328
+ if (from === "oklab" && to === "oklch") return toPolar(coords);
329
+ if (from === "lch" && to === "lab") return toRect(coords);
330
+ if (from === "lab" && to === "lch") return toPolar(coords);
331
+ return fromXyz(toXyz(coords, from), to);
332
+ }
333
+
334
+ // ── gamut ──
335
+ const SLACK = 0.000075;
336
+ const inRange = (rgb: Vec3) => rgb.every((c) => c >= -SLACK && c <= 1 + SLACK);
337
+ const inGamut = (oklch: Vec3, gamut: Gamut) => inRange(convert(oklch, "oklch", gamut));
338
+ // map() on a tuple reads back as an array
339
+ const clip = (rgb: Vec3) => rgb.map((c) => Math.min(1, Math.max(0, c))) as Vec3;
340
+ const deltaEOK = (a: Vec3, b: Vec3) => Math.hypot(a[0] - b[0], a[1] - b[1], a[2] - b[2]);
341
+ /** Map OKLCH into an RGB gamut (CSS Color 4 chroma reduction + local clip). */
342
+ export function toGamut(oklch: Vec3, dest: Gamut): Vec3 {
343
+ if (inGamut(oklch, dest)) return convert(oklch, "oklch", dest);
344
+ const L = oklch[0];
345
+ if (L >= 1) return [1, 1, 1];
346
+ if (L <= 0) return [0, 0, 0];
347
+ // An unbounded chroma can't be bisected; fall to the achromatic color at this lightness.
348
+ if (!Number.isFinite(oklch[1])) return clip(convert([L, 0, num(oklch[2])], "oklch", dest));
349
+ const JND = 0.02,
350
+ EPS = 0.0001;
351
+ const cur: Vec3 = [oklch[0], oklch[1], oklch[2]];
352
+ let min = 0,
353
+ max = oklch[1],
354
+ minIn = true,
355
+ clipped = clip(convert(cur, "oklch", dest));
356
+ if (deltaEOK(convert(clipped, dest, "oklab"), convert(oklch, "oklch", "oklab")) < JND) return clipped;
357
+ while (max - min > EPS) {
358
+ const chroma = (min + max) / 2;
359
+ cur[1] = chroma;
360
+ const inDest = convert(cur, "oklch", dest);
361
+ if (minIn && inRange(inDest)) {
362
+ min = chroma;
363
+ continue;
364
+ }
365
+ clipped = clip(inDest);
366
+ const e = deltaEOK(convert(clipped, dest, "oklab"), convert(cur, "oklch", "oklab"));
367
+ if (e < JND) {
368
+ if (JND - e < EPS) return clipped;
369
+ minIn = false;
370
+ min = chroma;
371
+ } else max = chroma;
372
+ }
373
+ return clipped;
374
+ }
375
+ // At a fixed hue OKLab→LMS collapses to p_i = L + C·d_i and LMS→XYZ→RGB to one matrix F, so the
376
+ // two per-pixel helpers run trig- and allocation-free; identical to convert([L,C,hue], "oklch", gamut).
377
+ const hueFold = (hue: number, gamut: Gamut) => {
378
+ const h = hue * RAD,
379
+ cos = Math.cos(h),
380
+ sin = Math.sin(h);
381
+ const d = OKLAB_TO_LMS.map((row) => cos * row[1] + sin * row[2]);
382
+ const F = mulMat(
383
+ gamut === "p3" ? XYZ_TO_LIN_P3 : gamut === "rec2020" ? XYZ_TO_LIN_REC2020 : XYZ_TO_LIN_SRGB,
384
+ LMS_TO_XYZ,
385
+ );
386
+ return { d, F, gam: gamut === "rec2020" ? rec2020Gam : srgbGam };
387
+ };
388
+ /** OKLCH→RGB at a fixed hue for per-pixel rasterizing; `(L, C, out)` writes into `out`. */
389
+ export function oklchToRgbFn(hue: number, gamut: Gamut) {
390
+ const {
391
+ d: [d0, d1, d2],
392
+ F,
393
+ gam,
394
+ } = hueFold(hue, gamut);
395
+ return (L: number, C: number, out: number[]) => {
396
+ const p0 = L + C * d0,
397
+ p1 = L + C * d1,
398
+ p2 = L + C * d2,
399
+ c0 = p0 ** 3,
400
+ c1 = p1 ** 3,
401
+ c2 = p2 ** 3;
402
+ out[0] = gam(F[0][0] * c0 + F[0][1] * c1 + F[0][2] * c2);
403
+ out[1] = gam(F[1][0] * c0 + F[1][1] * c1 + F[1][2] * c2);
404
+ out[2] = gam(F[2][0] * c0 + F[2][1] * c1 + F[2][2] * c2);
405
+ };
406
+ }
407
+ /** Fast in-gamut probe at a fixed hue: `(L, C) => inside?`. */
408
+ export function oklchGamutProbe(hue: number, gamut: Gamut) {
409
+ const toRgb = oklchToRgbFn(hue, gamut),
410
+ rgb = [0, 0, 0],
411
+ lo = -SLACK,
412
+ hi = 1 + SLACK,
413
+ inside = (v: number) => v >= lo && v <= hi;
414
+ return (L: number, C: number) => {
415
+ toRgb(L, C, rgb);
416
+ return rgb.every(inside);
417
+ };
418
+ }
419
+ /** Bisect the chroma ceiling (highest in-gamut C) at lightness L for an `oklchGamutProbe`. */
420
+ export function chromaCeil(probe: (L: number, C: number) => boolean, L: number, hi = 0.5) {
421
+ if (!probe(L, 0)) return 0;
422
+ let lo = 0;
423
+ for (let k = 0; k < 16; k++) {
424
+ const m = (lo + hi) / 2;
425
+ probe(L, m) ? (lo = m) : (hi = m);
426
+ }
427
+ return lo;
428
+ }
429
+
430
+ // ── hex ──
431
+ /** One 0–1 channel → a 2-digit hex byte (also the alpha suffix for #RRGGBBAA), upper-case as the readout shows it. */
432
+ export const hexByte = (n: number) =>
433
+ Math.round(Math.min(1, Math.max(0, n)) * 255)
434
+ .toString(16)
435
+ .padStart(2, "0")
436
+ .toUpperCase();
437
+ const rgbToHex = (rgb: Vec3) => `#${hexByte(rgb[0])}${hexByte(rgb[1])}${hexByte(rgb[2])}`;
438
+ export const oklchToHex = (L: number, C: number, H: number) => rgbToHex(toGamut([L, C, H], "srgb"));
439
+ export function hexToOklch(hex: string): Vec3 {
440
+ let h = String(hex).replace("#", "").trim();
441
+ if (h.length === 3)
442
+ h = h
443
+ .split("")
444
+ .map((c) => c + c)
445
+ .join("");
446
+ if (h.length < 6) return [0.7, 0.1, 280];
447
+ const rgb: Vec3 = [
448
+ parseInt(h.slice(0, 2), 16) / 255,
449
+ parseInt(h.slice(2, 4), 16) / 255,
450
+ parseInt(h.slice(4, 6), 16) / 255,
451
+ ];
452
+ return convert(rgb, "srgb", "oklch").map(num) as Vec3;
453
+ }
454
+
455
+ // ── mode model ──
456
+ export const EDIT_MODES: readonly ColorMode[] = [
457
+ "hex",
458
+ "srgb",
459
+ "css",
460
+ "hsl",
461
+ "hwb",
462
+ "oklch",
463
+ "oklab",
464
+ "lch",
465
+ "lab",
466
+ "p3",
467
+ "rec2020",
468
+ ];
469
+ export const MODE_LABELS: Record<ColorMode, string> = {
470
+ hex: "HEX",
471
+ srgb: "RGB",
472
+ css: "CSS",
473
+ hsl: "HSL",
474
+ hwb: "HWB",
475
+ oklch: "OKLCH",
476
+ oklab: "OKLab",
477
+ lch: "LCH",
478
+ lab: "Lab",
479
+ p3: "P3",
480
+ rec2020: "Rec2020",
481
+ };
482
+ const SRGB_BOUND: readonly ColorMode[] = ["srgb", "css", "hsl", "hwb", "hex"];
483
+ export const showsGamutBoundary = (mode: ColorMode) => !SRGB_BOUND.includes(mode);
484
+ const modeSpaceId = (mode: ColorMode): Space => (mode === "hex" || mode === "css" ? "srgb" : mode);
485
+ // The CSS <color-interpolation-method> a gradient authored in each mode blends through.
486
+ // Null-prototype, so a mode named "constructor" misses and falls to the default.
487
+ const MODE_INTERP: Record<string, GradientInterpolation> = Object.assign(Object.create(null), {
488
+ hex: "srgb",
489
+ srgb: "srgb",
490
+ css: "srgb",
491
+ hsl: "hsl",
492
+ hwb: "hwb",
493
+ oklch: "oklch",
494
+ oklab: "oklab",
495
+ lch: "lch",
496
+ lab: "lab",
497
+ p3: "display-p3",
498
+ rec2020: "rec2020",
499
+ });
500
+ export const modeInterpolation = (mode: string) => MODE_INTERP[mode] || "oklch";
501
+ // The reverse: the mode that produced a stored gradient's blend; unknown falls to OKLCH.
502
+ const INTERP_MODE: Record<string, ColorMode> = Object.assign(Object.create(null), {
503
+ srgb: "srgb",
504
+ hsl: "hsl",
505
+ hwb: "hwb",
506
+ oklch: "oklch",
507
+ oklab: "oklab",
508
+ lch: "lch",
509
+ lab: "lab",
510
+ "display-p3": "p3",
511
+ rec2020: "rec2020",
512
+ });
513
+ export const interpolationMode = (interp: string) => INTERP_MODE[interp] || "oklch";
514
+ /** The picker's chroma ceiling: the C field's max and the parser's cap on any chroma it reads. */
515
+ export const MAX_CHROMA = 0.5;
516
+ // Each triple is shared by two modes and frozen, so a one-mode tweak can't leak into its twin.
517
+ const rgbTriple = (max: number, step: number, scale: number): readonly Channel[] =>
518
+ Object.freeze(["R", "G", "B"].map((k) => Object.freeze({ k, min: 0, max, step, scale })));
519
+ const RGB_255 = rgbTriple(255, 1, 255),
520
+ RGB_UNIT = rgbTriple(1, 0.01, 1);
521
+ export const MODE_CHANNELS: Partial<Record<ColorMode, readonly Channel[]>> = {
522
+ oklch: [
523
+ { k: "L", min: 0, max: 100, step: 1, scale: 100 },
524
+ { k: "C", min: 0, max: MAX_CHROMA, step: 0.01, scale: 1 },
525
+ { k: "H", min: 0, max: 360, step: 1, scale: 1 },
526
+ ],
527
+ oklab: [
528
+ { k: "L", min: 0, max: 100, step: 1, scale: 100 },
529
+ { k: "a", min: -0.4, max: 0.4, step: 0.01, scale: 1 },
530
+ { k: "b", min: -0.4, max: 0.4, step: 0.01, scale: 1 },
531
+ ],
532
+ lch: [
533
+ { k: "L", min: 0, max: 100, step: 1, scale: 1 },
534
+ { k: "C", min: 0, max: 150, step: 1, scale: 1 },
535
+ { k: "H", min: 0, max: 360, step: 1, scale: 1 },
536
+ ],
537
+ lab: [
538
+ { k: "L", min: 0, max: 100, step: 1, scale: 1 },
539
+ { k: "a", min: -125, max: 125, step: 1, scale: 1 },
540
+ { k: "b", min: -125, max: 125, step: 1, scale: 1 },
541
+ ],
542
+ srgb: RGB_255,
543
+ css: RGB_255,
544
+ hsl: [
545
+ { k: "H", min: 0, max: 360, step: 1, scale: 1 },
546
+ { k: "S", min: 0, max: 100, step: 1, scale: 1 },
547
+ { k: "L", min: 0, max: 100, step: 1, scale: 1 },
548
+ ],
549
+ hwb: [
550
+ { k: "H", min: 0, max: 360, step: 1, scale: 1 },
551
+ { k: "W", min: 0, max: 100, step: 1, scale: 1 },
552
+ { k: "B", min: 0, max: 100, step: 1, scale: 1 },
553
+ ],
554
+ p3: RGB_UNIT,
555
+ rec2020: RGB_UNIT,
556
+ };
557
+ const digitsFor = (step: number) => (step >= 1 ? 0 : step >= 0.1 ? 1 : step >= 0.01 ? 2 : 3);
558
+
559
+ // Display coords for `mode` from canonical OKLCH. sRGB-bound modes read off the same
560
+ // chroma-reduced sRGB hex uses, so every narrow-gamut mode agrees on the color.
561
+ const modeCoords = (oklch: Vec3, sid: Space, mode: ColorMode): Vec3 =>
562
+ sid === "oklch"
563
+ ? [oklch[0], oklch[1], oklch[2]]
564
+ : SRGB_BOUND.includes(mode)
565
+ ? sid === "srgb"
566
+ ? toGamut(oklch, "srgb")
567
+ : convert(toGamut(oklch, "srgb"), "srgb", sid)
568
+ : convert(oklch, "oklch", sid);
569
+ /** Display-unit channel values for `mode` from canonical OKLCH. */
570
+ export function channelValues(oklch: Vec3, mode: ColorMode) {
571
+ const c = modeCoords(oklch, modeSpaceId(mode), mode);
572
+ return MODE_CHANNELS[mode].map((ch, i) => {
573
+ const v = num(c[i]) * ch.scale;
574
+ return clamp(Math.abs(v) < 0.5 * ch.step ? 0 : v, ch.min, ch.max);
575
+ });
576
+ }
577
+ /** New OKLCH with channel `index` of `mode` set to `displayValue`. */
578
+ export function withChannel(oklch: Vec3, mode: ColorMode, index: number, displayValue: number): Vec3 {
579
+ const sid = modeSpaceId(mode);
580
+ const c = modeCoords(oklch, sid, mode).map(num) as Vec3;
581
+ c[index] = displayValue / MODE_CHANNELS[mode][index].scale;
582
+ const k = convert(c, sid, "oklch");
583
+ return [num(k[0]), num(k[1]), num(k[2])];
584
+ }
585
+ /** Plain-text gamut label: sRGB / P3 / wide. */
586
+ export function gamutLabel(oklch: Vec3, mode: ColorMode) {
587
+ if (!showsGamutBoundary(mode)) return "sRGB";
588
+ const shown = toGamut(oklch, "srgb");
589
+ if (Math.max(...shown) < 0.03 || Math.min(...shown) > 0.97) return "sRGB";
590
+ if (inGamut(oklch, "srgb")) return "sRGB";
591
+ if (inGamut(oklch, "p3")) return "P3";
592
+ return "wide";
593
+ }
594
+ // The three channels in `mode`'s own syntax: commas for legacy css, a % on lightness or on the last two of hsl/hwb.
595
+ const joinChannels = (mode: ColorMode, s: (i: number) => string) =>
596
+ mode === "css"
597
+ ? `${s(0)}, ${s(1)}, ${s(2)}`
598
+ : mode === "oklch" || mode === "oklab" || mode === "lch" || mode === "lab"
599
+ ? `${s(0)}% ${s(1)} ${s(2)}`
600
+ : mode === "hsl" || mode === "hwb"
601
+ ? `${s(0)} ${s(1)}% ${s(2)}%`
602
+ : `${s(0)} ${s(1)} ${s(2)}`; // srgb / p3 / rec2020
603
+ /** The collapsed-row value string for `mode` (bare channels w/ units, or hex). */
604
+ export function readout(oklch: Vec3, mode: ColorMode) {
605
+ if (mode === "hex") return oklchToHex(oklch[0], oklch[1], oklch[2]);
606
+ const chans = MODE_CHANNELS[mode],
607
+ v = channelValues(oklch, mode);
608
+ // 360 is 0; only 0 reads back as itself
609
+ return joinChannels(mode, (i) => {
610
+ const t = v[i].toFixed(digitsFor(chans[i].step));
611
+ return chans[i].k === "H" && +t === 360 ? (0).toFixed(digitsFor(chans[i].step)) : t;
612
+ });
613
+ }
614
+ // css is legacy comma rgba with an explicit alpha slot; the rest take modern space syntax.
615
+ const wrapCss = (t: string, mode: ColorMode) =>
616
+ mode === "hex"
617
+ ? t
618
+ : mode === "srgb"
619
+ ? `rgb(${t})`
620
+ : mode === "css"
621
+ ? `rgba(${t}, 1)`
622
+ : mode === "hsl"
623
+ ? `hsl(${t})`
624
+ : mode === "hwb"
625
+ ? `hwb(${t})`
626
+ : mode === "p3"
627
+ ? `color(display-p3 ${t})`
628
+ : mode === "rec2020"
629
+ ? `color(rec2020 ${t})`
630
+ : `${mode}(${t})`;
631
+ // The value's channels, a digit finer than the readout (L 63.2%, C 0.257, H 29.2): rounded to the readout, 98% of
632
+ // colors came back different (#939796's faint green read as a neutral gray). A powerless hue is `none`, as CSS Color 4
633
+ // writes it: a float-noise hue on white (180, cyan) or a gray bent every oklch gradient and color-mix() through it.
634
+ // HSL's black and white are written S 0 and hue none: S divides by min(L, 1 - L), so next to either end it read noise
635
+ // as 100% and a hue.
636
+ function precise(oklch: Vec3, mode: ColorMode) {
637
+ const chans = MODE_CHANNELS[mode],
638
+ c = modeCoords(oklch, modeSpaceId(mode), mode);
639
+ // + 0: no -0
640
+ const v = chans.map((ch, i) => {
641
+ const d = digitsFor(ch.step) + 1,
642
+ x = +clamp(num(c[i]) * ch.scale, ch.min, ch.max).toFixed(d);
643
+ return ch.k === "H" && x === 360 ? 0 : x + 0;
644
+ });
645
+ if (mode === "hsl" && (v[2] === 0 || v[2] === 100)) v[1] = 0;
646
+ const gray = mode === "oklch" || mode === "lch" || mode === "hsl" ? v[1] === 0 : mode === "hwb" && v[1] + v[2] >= 100;
647
+ return joinChannels(mode, (i) => (gray && chans[i].k === "H" ? "none" : String(v[i])));
648
+ }
649
+ /** The emitted CSS value in `mode`'s own syntax, a digit finer than the readout, alpha appended. */
650
+ export function serialize(oklch: Vec3, mode: ColorMode, alpha: number) {
651
+ const a = num(alpha);
652
+ if (mode === "hex") return oklchToHex(oklch[0], oklch[1], oklch[2]) + (a < 0.999 ? hexByte(a) : "");
653
+ const t = precise(oklch, mode);
654
+ if (a >= 0.999) return wrapCss(t, mode);
655
+ const av = +a.toFixed(3);
656
+ // comma alpha for legacy rgba, slash otherwise
657
+ return mode === "css" ? `rgba(${t}, ${av})` : wrapCss(`${t} / ${av}`, mode);
658
+ }
@@ -1 +0,0 @@
1
- import{a as f,e as J,j as Q}from"./chunk-4ATI34PB.js";import{$ as q,g as A,u as M,v as N,w as I,y as K,z as U}from"./chunk-7OSCMZ2J.js";function ee(v,$){let R=[.25,.1,.25,1],b=-.25,w=1.25,H=w-b,o=8,Y=e=>e.map((n,i)=>A(n,i%2?b:0,i%2?w:1)),t=Array.isArray(v.value)&&v.value.length===4?v.value.map(Number):R.slice();t.some(e=>!Number.isFinite(e))&&(t=R.slice()),t=Y(t);let Z=t.slice(),E=M("div","tw-bezier");v.label&&E.append(I("div","tw-bezier-label",v.label));let d=M("div","tw-bezier-graph"),k=f("svg","tw-bezier-svg");k.setAttribute("preserveAspectRatio","none");let G=f("line","tw-bezier-guide"),P=f("line","tw-bezier-guide"),X=f("line","tw-bezier-diag"),j=f("line","tw-bezier-ctl"),O=f("line","tw-bezier-ctl"),S=f("path","tw-bezier-curve");k.append(G,P,X,j,O,S);let g=N("tw-bezier-handle");g.setAttribute("aria-label","Control point 1");let x=N("tw-bezier-handle");x.setAttribute("aria-label","Control point 2"),d.append(k,g,x);let W=M("div","tw-fields tw-bezier-fields");E.append(d,W);let h=0,C=0,F=e=>o+e*(h-2*o),D=e=>C-o-(e-b)/H*(C-2*o),z=(e,n,i,m,a)=>{e.setAttribute("x1",n),e.setAttribute("y1",i),e.setAttribute("x2",m),e.setAttribute("y2",a)},y=()=>{if(g.setAttribute("aria-label",`Control point 1, x ${t[0]}, y ${t[1]}`),x.setAttribute("aria-label",`Control point 2, x ${t[2]}, y ${t[3]}`),h=d.offsetWidth,C=d.offsetHeight,h<2)return;k.setAttribute("viewBox",`0 0 ${h} ${C}`);let[e,n,i,m]=t,a=F(0),c=D(0),r=F(1),l=D(1),u=F(e),p=D(n),s=F(i),B=D(m);z(G,o,c,h-o,c),z(P,o,l,h-o,l),z(X,a,c,r,l),z(j,a,c,u,p),z(O,r,l,s,B),S.setAttribute("d",`M${a},${c} C${u},${p} ${s},${B} ${r},${l}`),g.style.left=u+"px",g.style.top=p+"px",x.style.left=s+"px",x.style.top=B+"px"},T=[{label:"X1",i:0,lo:0,hi:1},{label:"Y1",i:1,lo:b,hi:w},{label:"X2",i:2,lo:0,hi:1},{label:"Y2",i:3,lo:b,hi:w}],_=T.map(e=>{let n=Q({label:e.label,value:t[e.i],step:.01,min:e.lo,max:e.hi},i=>{t[e.i]=i,y(),$(t.slice())});return W.append(n.el),n}),L=()=>_.forEach((e,n)=>e.set(t[T[n].i])),V=(e,n)=>{let i=null,m=0,a=0,c=1;J(e,{onDown:r=>{r.preventDefault(),i=d.getBoundingClientRect(),m=d.offsetWidth||i.width,a=d.offsetHeight||i.height,c=i.width/m,e.classList.add("is-dragging"),e.focus({focusVisible:!1})},onMove:r=>{let l=(r.clientX-i.left)/c,u=(r.clientY-i.top)/c,p=A((l-o)/(m-2*o),0,1),s=A(b+(a-o-u)/(a-2*o)*H,b,w);t[n*2]=+p.toFixed(2),t[n*2+1]=+s.toFixed(2),y(),L(),$(t.slice())},onEnd:()=>{i=null,e.classList.remove("is-dragging")}}),e.addEventListener("keydown",r=>{if(r.key==="Escape")return e.blur();if(r.key==="Home"){r.preventDefault(),t=Z.slice(),y(),L(),$(t.slice());return}let l=r.key==="ArrowRight"?1:r.key==="ArrowLeft"?-1:0,u=r.key==="ArrowUp"?1:r.key==="ArrowDown"?-1:0;if(!l&&!u)return;r.preventDefault();let p=r.shiftKey?.1:.01,s=n*2;l&&(t[s]=+A(t[s]+l*p,0,1).toFixed(2)),u&&(t[s+1]=+A(t[s+1]+u*p,b,w).toFixed(2)),y(),L(),$(t.slice())})};return V(g,0),V(x,1),K(y),U(E,[[window,"resize"],[window,"tw-reflow"]],y),{el:E,set:e=>{if(Array.isArray(e)&&e.length===4){let n=e.map(Number);if(n.some(i=>!Number.isFinite(i)))return;t=Y(n),y(),L()}},get:()=>t.slice()}}q("cubicbezier",ee);
@@ -1 +0,0 @@
1
- import{$ as P,C as T,D as S,I as $,Z as I,g as E,j as L,k as N,l as F,u as g,v as C,w as M}from"./chunk-7OSCMZ2J.js";var D="http://www.w3.org/2000/svg",k=(t,n)=>{let e=document.createElementNS(D,t);return n&&e.setAttribute("class",n),e},V=(t,n)=>getComputedStyle(t).getPropertyValue(n).trim(),B=t=>{let n=getComputedStyle(t),e=s=>n.getPropertyValue(s).trim();return e("--tw-accent")||e("--tw-text-primary")||"#fff"},O=t=>{let n=!1;t.addEventListener("pointerdown",e=>{n=e.button===0&&document.activeElement!==t}),t.addEventListener("focus",()=>t.select()),t.addEventListener("mouseup",e=>{n&&(n=!1,e.button===0&&document.activeElement===t&&t.selectionStart===0&&t.selectionEnd===t.value.length&&e.preventDefault())})};function H(t,{onDown:n,onMove:e,onEnd:s}={}){let r=null,o=i=>{r===null||i.pointerId!==r||(r=null,s&&s(i))};t.addEventListener("pointerdown",i=>{if(!(i.button!==0||r!==null)){r=i.pointerId;try{t.setPointerCapture(i.pointerId)}catch{}n&&n(i)}}),t.addEventListener("pointermove",i=>{if(i.pointerId===r){if(i.buttons===0)return o(i);e&&e(i)}}),t.addEventListener("pointerup",o),t.addEventListener("pointercancel",o),t.addEventListener("lostpointercapture",o)}var j=(t,n)=>H(t,{onDown:e=>{t.classList.add("is-grabbing"),n(e)},onMove:n,onEnd:()=>t.classList.remove("is-grabbing")}),z=(t,n)=>{let e=n.getBoundingClientRect();return[E((t.clientX-e.left)/e.width,0,1),E((t.clientY-e.top)/e.height,0,1)]},U=(t,n,e=1/0)=>{let s=t.getBoundingClientRect(),r=Math.min(window.devicePixelRatio||1,e);return t.width=s.width*r,t.height=s.height*r,n.setTransform(r,0,0,r,0,0),[s.width,s.height]},Y=(t,n)=>{let e=g("div",t),s=C("tw-trigger");s.setAttribute("aria-expanded","false");let r=g("span","tw-trigger-right");return s.append(M("span","tw-trigger-label",n),r),e.append(s),{root:e,trigger:s,right:r}},G=I('<path d="M6 4v8M10 4v8"/>',"",2,16);function X(){let t=null,n=0,e=0,s=0;return{show(r,o,i,m){t=g("div","tw-grab-guide tw-portal"),t.innerHTML='<span class="tw-grab-line"></span><span class="tw-grab-dot"></span><span class="tw-grab-arrow"></span><span class="tw-grab-bubble"></span>',n=o,e=r,s=i??r,t.children[1].style.cssText=`left:${r}px;top:${n}px`,$(t,m),document.body.appendChild(t)},move(r,o){t&&(t.children[0].style.cssText=`left:${Math.min(e,r)}px;top:${n}px;width:${Math.abs(r-e)}px`,t.children[2].style.cssText=`left:${r}px;top:${n}px;transform:translate(-50%,-50%) scaleX(${r>=e?1:-1})`,t.children[3].style.cssText=`left:${s}px;top:${n-16}px`,t.children[3].textContent=o)},hide(){t&&(t.remove(),t=null)}}}function R(t,n,e,s,r,o){let i=0,m=0,p=1,d=X();H(t,{onDown:u=>{u.preventDefault(),i=u.clientX,m=s(),p=1,t.classList.add("is-dragging");let a=n.getBoundingClientRect();d.show(u.clientX,a.top+a.height/2,a.left+a.width/2,n),d.move(u.clientX,o())},onMove:u=>{let a=u.shiftKey?10:u.altKey?.1:1;a!==p&&(p=a,i=u.clientX,m=s()),r(m+(u.clientX-i)*e*a),d.move(u.clientX,o())},onEnd:()=>{t.classList.remove("is-dragging"),d.hide()}})}function q(t,n){let e=Number.isFinite(+t.step)&&+t.step>0?+t.step:1,s=l=>l==null?NaN:+l,r=s(t.min),o=s(t.max);if(Number.isFinite(r)&&Number.isFinite(o)&&o<r){let l=r;r=o,o=l}let i=Number.isFinite(r)?r:0,m=L(e),[p,d]=F(Number.isFinite(r)?r:-1/0,Number.isFinite(o)?o:1/0,e,i),u=l=>{let h=N(l,i,e);return t.soft?h:E(h,p,d)},a=u(Number.isFinite(+t.value)?+t.value:0),f=g("div",t.row?"tw-row":"tw-field"),b=g("div","tw-num-wrap"),v=g("span","tw-num-grab",G);v.setAttribute("aria-hidden","true"),v.title="Drag to adjust";let c=g("input","tw-num");c.type="text",c.inputMode="decimal",c.setAttribute("aria-label",t.unit?`${t.label} (${t.unit})`:t.label),c.value=a.toFixed(m),T(c),b.append(v,c),t.unit&&b.append(M("span","tw-num-unit",t.unit)),f.append(M("span",t.row?"tw-row-label":"tw-field-label",t.label),b);let w=(l,h=!0)=>{l=+l,Number.isFinite(l)&&(a=u(l),c.value=a.toFixed(m),h&&n&&n(a))},x=()=>Number(c.value.replace(t.unit||"","").trim()||NaN);return c.addEventListener("change",()=>{let l=x();Number.isFinite(l)&&u(l)!==a?w(l):c.value=a.toFixed(m)}),S(c,()=>a.toFixed(m)),c.addEventListener("keydown",l=>{if(l.key==="Enter")return c.blur();let h=l.key==="ArrowUp"?1:l.key==="ArrowDown"?-1:0;if(!h)return;l.preventDefault();let y=x();w((Number.isFinite(y)?y:a)+h*e*(l.shiftKey?10:1))}),R(v,b,e,()=>a,w,()=>c.value),{el:f,set:l=>w(l,!1),get:()=>a}}P("number",(t,n)=>q({...t,row:!0},n));var A=(t,n,e)=>{let s=Math.sqrt(t/e),r=n/(2*Math.sqrt(t*e));return Math.min(2.2,9/Math.max(r*s,.5))};function _(t,n,e,s=64,r=A(t,n,e)){let o=Math.sqrt(t/e),i=n/(2*Math.sqrt(t*e)),m=[];for(let p=0;p<s;p++){let d=p/(s-1)*r,u;if(i<1-1e-4){let a=o*Math.sqrt(1-i*i);u=1-Math.exp(-i*o*d)*(Math.cos(a*d)+i*o/a*Math.sin(a*d))}else if(i<=1+1e-4)u=1-Math.exp(-o*d)*(1+o*d);else{let a=Math.sqrt(i*i-1),f=o*(i+a),b=o*(i-a);u=1-(f*Math.exp(-b*d)-b*Math.exp(-f*d))/(f-b)}m.push(u)}return m}export{k as a,V as b,B as c,O as d,H as e,j as f,z as g,U as h,Y as i,q as j,A as k,_ as l};