@fluixi/utils 1.0.0-alpha.53

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 (354) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +73 -0
  3. package/dist/index.cjs +1 -0
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.d.ts.map +1 -0
  6. package/dist/index.js +1 -0
  7. package/dist/index.mjs +1 -0
  8. package/dist/lib/array/advanced.cjs +1 -0
  9. package/dist/lib/array/advanced.d.ts +1116 -0
  10. package/dist/lib/array/advanced.d.ts.map +1 -0
  11. package/dist/lib/array/advanced.js +2429 -0
  12. package/dist/lib/array/advanced.mjs +1 -0
  13. package/dist/lib/array/array.cjs +1 -0
  14. package/dist/lib/array/array.d.ts +6 -0
  15. package/dist/lib/array/array.d.ts.map +1 -0
  16. package/dist/lib/array/array.js +31 -0
  17. package/dist/lib/array/array.mjs +1 -0
  18. package/dist/lib/array/index.cjs +1 -0
  19. package/dist/lib/array/index.d.ts +4 -0
  20. package/dist/lib/array/index.d.ts.map +1 -0
  21. package/dist/lib/array/index.js +3 -0
  22. package/dist/lib/array/index.mjs +1 -0
  23. package/dist/lib/array/utils.cjs +1 -0
  24. package/dist/lib/array/utils.d.ts +100 -0
  25. package/dist/lib/array/utils.d.ts.map +1 -0
  26. package/dist/lib/array/utils.js +165 -0
  27. package/dist/lib/array/utils.mjs +1 -0
  28. package/dist/lib/color/color.cjs +1 -0
  29. package/dist/lib/color/color.d.ts +366 -0
  30. package/dist/lib/color/color.d.ts.map +1 -0
  31. package/dist/lib/color/color.js +722 -0
  32. package/dist/lib/color/color.mjs +1 -0
  33. package/dist/lib/color/constants.cjs +1 -0
  34. package/dist/lib/color/constants.d.ts +361 -0
  35. package/dist/lib/color/constants.d.ts.map +1 -0
  36. package/dist/lib/color/constants.js +358 -0
  37. package/dist/lib/color/constants.mjs +1 -0
  38. package/dist/lib/color/conversions.cjs +1 -0
  39. package/dist/lib/color/conversions.d.ts +166 -0
  40. package/dist/lib/color/conversions.d.ts.map +1 -0
  41. package/dist/lib/color/conversions.js +766 -0
  42. package/dist/lib/color/conversions.mjs +1 -0
  43. package/dist/lib/color/helpers.cjs +1 -0
  44. package/dist/lib/color/helpers.d.ts +75 -0
  45. package/dist/lib/color/helpers.d.ts.map +1 -0
  46. package/dist/lib/color/helpers.js +419 -0
  47. package/dist/lib/color/helpers.mjs +1 -0
  48. package/dist/lib/color/index.cjs +1 -0
  49. package/dist/lib/color/index.d.ts +12 -0
  50. package/dist/lib/color/index.d.ts.map +1 -0
  51. package/dist/lib/color/index.js +15 -0
  52. package/dist/lib/color/index.mjs +1 -0
  53. package/dist/lib/color/types.cjs +1 -0
  54. package/dist/lib/color/types.d.ts +179 -0
  55. package/dist/lib/color/types.d.ts.map +1 -0
  56. package/dist/lib/color/types.js +1 -0
  57. package/dist/lib/color/types.mjs +0 -0
  58. package/dist/lib/color/utils.cjs +1 -0
  59. package/dist/lib/color/utils.d.ts +199 -0
  60. package/dist/lib/color/utils.d.ts.map +1 -0
  61. package/dist/lib/color/utils.js +858 -0
  62. package/dist/lib/color/utils.mjs +1 -0
  63. package/dist/lib/compare/comparators.cjs +1 -0
  64. package/dist/lib/compare/comparators.d.ts +65 -0
  65. package/dist/lib/compare/comparators.d.ts.map +1 -0
  66. package/dist/lib/compare/comparators.js +210 -0
  67. package/dist/lib/compare/comparators.mjs +1 -0
  68. package/dist/lib/compare/index.cjs +1 -0
  69. package/dist/lib/compare/index.d.ts +2 -0
  70. package/dist/lib/compare/index.d.ts.map +1 -0
  71. package/dist/lib/compare/index.js +1 -0
  72. package/dist/lib/compare/index.mjs +1 -0
  73. package/dist/lib/crypto/hash.cjs +1 -0
  74. package/dist/lib/crypto/hash.d.ts +6 -0
  75. package/dist/lib/crypto/hash.d.ts.map +1 -0
  76. package/dist/lib/crypto/hash.js +53 -0
  77. package/dist/lib/crypto/hash.mjs +1 -0
  78. package/dist/lib/crypto/index.cjs +1 -0
  79. package/dist/lib/crypto/index.d.ts +2 -0
  80. package/dist/lib/crypto/index.d.ts.map +1 -0
  81. package/dist/lib/crypto/index.js +1 -0
  82. package/dist/lib/crypto/index.mjs +1 -0
  83. package/dist/lib/dom/events.cjs +1 -0
  84. package/dist/lib/dom/events.d.ts +159 -0
  85. package/dist/lib/dom/events.d.ts.map +1 -0
  86. package/dist/lib/dom/events.js +50 -0
  87. package/dist/lib/dom/events.mjs +1 -0
  88. package/dist/lib/dom/index.cjs +1 -0
  89. package/dist/lib/dom/index.d.ts +3 -0
  90. package/dist/lib/dom/index.d.ts.map +1 -0
  91. package/dist/lib/dom/index.js +2 -0
  92. package/dist/lib/dom/index.mjs +1 -0
  93. package/dist/lib/dom/utils.cjs +1 -0
  94. package/dist/lib/dom/utils.d.ts +6 -0
  95. package/dist/lib/dom/utils.d.ts.map +1 -0
  96. package/dist/lib/dom/utils.js +37 -0
  97. package/dist/lib/dom/utils.mjs +1 -0
  98. package/dist/lib/functions/functions.cjs +1 -0
  99. package/dist/lib/functions/functions.d.ts +329 -0
  100. package/dist/lib/functions/functions.d.ts.map +1 -0
  101. package/dist/lib/functions/functions.js +585 -0
  102. package/dist/lib/functions/functions.mjs +1 -0
  103. package/dist/lib/functions/index.cjs +1 -0
  104. package/dist/lib/functions/index.d.ts +3 -0
  105. package/dist/lib/functions/index.d.ts.map +1 -0
  106. package/dist/lib/functions/index.js +1 -0
  107. package/dist/lib/functions/index.mjs +1 -0
  108. package/dist/lib/functions/types.cjs +1 -0
  109. package/dist/lib/functions/types.d.ts +2 -0
  110. package/dist/lib/functions/types.d.ts.map +1 -0
  111. package/dist/lib/functions/types.js +1 -0
  112. package/dist/lib/functions/types.mjs +0 -0
  113. package/dist/lib/helpers.cjs +1 -0
  114. package/dist/lib/helpers.d.ts +13 -0
  115. package/dist/lib/helpers.d.ts.map +1 -0
  116. package/dist/lib/helpers.js +66 -0
  117. package/dist/lib/helpers.mjs +1 -0
  118. package/dist/lib/index.cjs +1 -0
  119. package/dist/lib/index.d.ts +6 -0
  120. package/dist/lib/index.d.ts.map +1 -0
  121. package/dist/lib/index.js +8 -0
  122. package/dist/lib/index.mjs +1 -0
  123. package/dist/lib/maths/constants.cjs +1 -0
  124. package/dist/lib/maths/constants.d.ts +84 -0
  125. package/dist/lib/maths/constants.d.ts.map +1 -0
  126. package/dist/lib/maths/constants.js +79 -0
  127. package/dist/lib/maths/constants.mjs +1 -0
  128. package/dist/lib/maths/fn/advanced.cjs +1 -0
  129. package/dist/lib/maths/fn/advanced.d.ts +149 -0
  130. package/dist/lib/maths/fn/advanced.d.ts.map +1 -0
  131. package/dist/lib/maths/fn/advanced.js +264 -0
  132. package/dist/lib/maths/fn/advanced.mjs +1 -0
  133. package/dist/lib/maths/fn/basic.cjs +1 -0
  134. package/dist/lib/maths/fn/basic.d.ts +182 -0
  135. package/dist/lib/maths/fn/basic.d.ts.map +1 -0
  136. package/dist/lib/maths/fn/basic.js +343 -0
  137. package/dist/lib/maths/fn/basic.mjs +1 -0
  138. package/dist/lib/maths/fn/complex.cjs +1 -0
  139. package/dist/lib/maths/fn/complex.d.ts +153 -0
  140. package/dist/lib/maths/fn/complex.d.ts.map +1 -0
  141. package/dist/lib/maths/fn/complex.js +383 -0
  142. package/dist/lib/maths/fn/complex.mjs +1 -0
  143. package/dist/lib/maths/fn/index.cjs +1 -0
  144. package/dist/lib/maths/fn/index.d.ts +10 -0
  145. package/dist/lib/maths/fn/index.d.ts.map +1 -0
  146. package/dist/lib/maths/fn/index.js +9 -0
  147. package/dist/lib/maths/fn/index.mjs +1 -0
  148. package/dist/lib/maths/fn/trigo.cjs +1 -0
  149. package/dist/lib/maths/fn/trigo.d.ts +147 -0
  150. package/dist/lib/maths/fn/trigo.d.ts.map +1 -0
  151. package/dist/lib/maths/fn/trigo.js +227 -0
  152. package/dist/lib/maths/fn/trigo.mjs +1 -0
  153. package/dist/lib/maths/fn/vector.cjs +1 -0
  154. package/dist/lib/maths/fn/vector.d.ts +512 -0
  155. package/dist/lib/maths/fn/vector.d.ts.map +1 -0
  156. package/dist/lib/maths/fn/vector.js +1136 -0
  157. package/dist/lib/maths/fn/vector.mjs +1 -0
  158. package/dist/lib/maths/formatter.cjs +1 -0
  159. package/dist/lib/maths/formatter.d.ts +35 -0
  160. package/dist/lib/maths/formatter.d.ts.map +1 -0
  161. package/dist/lib/maths/formatter.js +56 -0
  162. package/dist/lib/maths/formatter.mjs +1 -0
  163. package/dist/lib/maths/guards.cjs +1 -0
  164. package/dist/lib/maths/guards.d.ts +166 -0
  165. package/dist/lib/maths/guards.d.ts.map +1 -0
  166. package/dist/lib/maths/guards.js +315 -0
  167. package/dist/lib/maths/guards.mjs +1 -0
  168. package/dist/lib/maths/index.cjs +1 -0
  169. package/dist/lib/maths/index.d.ts +24 -0
  170. package/dist/lib/maths/index.d.ts.map +1 -0
  171. package/dist/lib/maths/index.js +29 -0
  172. package/dist/lib/maths/index.mjs +1 -0
  173. package/dist/lib/maths/parser.cjs +1 -0
  174. package/dist/lib/maths/parser.d.ts +20 -0
  175. package/dist/lib/maths/parser.d.ts.map +1 -0
  176. package/dist/lib/maths/parser.js +78 -0
  177. package/dist/lib/maths/parser.mjs +1 -0
  178. package/dist/lib/maths/random.cjs +1 -0
  179. package/dist/lib/maths/random.d.ts +27 -0
  180. package/dist/lib/maths/random.d.ts.map +1 -0
  181. package/dist/lib/maths/random.js +49 -0
  182. package/dist/lib/maths/random.mjs +1 -0
  183. package/dist/lib/maths/types/advanced.cjs +1 -0
  184. package/dist/lib/maths/types/advanced.d.ts +357 -0
  185. package/dist/lib/maths/types/advanced.d.ts.map +1 -0
  186. package/dist/lib/maths/types/advanced.js +5 -0
  187. package/dist/lib/maths/types/advanced.mjs +0 -0
  188. package/dist/lib/maths/types/basic.cjs +1 -0
  189. package/dist/lib/maths/types/basic.d.ts +95 -0
  190. package/dist/lib/maths/types/basic.d.ts.map +1 -0
  191. package/dist/lib/maths/types/basic.js +5 -0
  192. package/dist/lib/maths/types/basic.mjs +0 -0
  193. package/dist/lib/maths/types/index.cjs +1 -0
  194. package/dist/lib/maths/types/index.d.ts +4 -0
  195. package/dist/lib/maths/types/index.d.ts.map +1 -0
  196. package/dist/lib/maths/types/index.js +3 -0
  197. package/dist/lib/maths/types/index.mjs +0 -0
  198. package/dist/lib/maths/types/negative.cjs +1 -0
  199. package/dist/lib/maths/types/negative.d.ts +51 -0
  200. package/dist/lib/maths/types/negative.d.ts.map +1 -0
  201. package/dist/lib/maths/types/negative.js +5 -0
  202. package/dist/lib/maths/types/negative.mjs +0 -0
  203. package/dist/lib/maths/types/operations.cjs +1 -0
  204. package/dist/lib/maths/types/operations.d.ts +188 -0
  205. package/dist/lib/maths/types/operations.d.ts.map +1 -0
  206. package/dist/lib/maths/types/operations.js +6 -0
  207. package/dist/lib/maths/types/operations.mjs +0 -0
  208. package/dist/lib/object/clone.cjs +1 -0
  209. package/dist/lib/object/clone.d.ts +46 -0
  210. package/dist/lib/object/clone.d.ts.map +1 -0
  211. package/dist/lib/object/clone.js +104 -0
  212. package/dist/lib/object/clone.mjs +1 -0
  213. package/dist/lib/object/diff.cjs +1 -0
  214. package/dist/lib/object/diff.d.ts +203 -0
  215. package/dist/lib/object/diff.d.ts.map +1 -0
  216. package/dist/lib/object/diff.js +805 -0
  217. package/dist/lib/object/diff.mjs +1 -0
  218. package/dist/lib/object/getter-setter.cjs +1 -0
  219. package/dist/lib/object/getter-setter.d.ts +361 -0
  220. package/dist/lib/object/getter-setter.d.ts.map +1 -0
  221. package/dist/lib/object/getter-setter.js +1771 -0
  222. package/dist/lib/object/getter-setter.mjs +1 -0
  223. package/dist/lib/object/index.cjs +6 -0
  224. package/dist/lib/object/index.d.ts +13 -0
  225. package/dist/lib/object/index.d.ts.map +1 -0
  226. package/dist/lib/object/index.js +12 -0
  227. package/dist/lib/object/index.mjs +6 -0
  228. package/dist/lib/object/merge.cjs +1 -0
  229. package/dist/lib/object/merge.d.ts +93 -0
  230. package/dist/lib/object/merge.d.ts.map +1 -0
  231. package/dist/lib/object/merge.js +474 -0
  232. package/dist/lib/object/merge.mjs +1 -0
  233. package/dist/lib/object/object.cjs +1 -0
  234. package/dist/lib/object/object.d.ts +128 -0
  235. package/dist/lib/object/object.d.ts.map +1 -0
  236. package/dist/lib/object/object.js +432 -0
  237. package/dist/lib/object/object.mjs +1 -0
  238. package/dist/lib/object/omit.cjs +1 -0
  239. package/dist/lib/object/omit.d.ts +351 -0
  240. package/dist/lib/object/omit.d.ts.map +1 -0
  241. package/dist/lib/object/omit.js +656 -0
  242. package/dist/lib/object/omit.mjs +1 -0
  243. package/dist/lib/object/reconcile.cjs +1 -0
  244. package/dist/lib/object/reconcile.d.ts +7 -0
  245. package/dist/lib/object/reconcile.d.ts.map +1 -0
  246. package/dist/lib/object/reconcile.js +122 -0
  247. package/dist/lib/object/reconcile.mjs +1 -0
  248. package/dist/lib/object/selector.cjs +1 -0
  249. package/dist/lib/object/selector.d.ts +50 -0
  250. package/dist/lib/object/selector.d.ts.map +1 -0
  251. package/dist/lib/object/selector.js +371 -0
  252. package/dist/lib/object/selector.mjs +1 -0
  253. package/dist/lib/object/sort.cjs +1 -0
  254. package/dist/lib/object/sort.d.ts +149 -0
  255. package/dist/lib/object/sort.d.ts.map +1 -0
  256. package/dist/lib/object/sort.js +298 -0
  257. package/dist/lib/object/sort.mjs +1 -0
  258. package/dist/lib/object/transform.cjs +1 -0
  259. package/dist/lib/object/transform.d.ts +620 -0
  260. package/dist/lib/object/transform.d.ts.map +1 -0
  261. package/dist/lib/object/transform.js +1058 -0
  262. package/dist/lib/object/transform.mjs +1 -0
  263. package/dist/lib/object/types.cjs +1 -0
  264. package/dist/lib/object/types.d.ts +250 -0
  265. package/dist/lib/object/types.d.ts.map +1 -0
  266. package/dist/lib/object/types.js +1 -0
  267. package/dist/lib/object/types.mjs +0 -0
  268. package/dist/lib/object/utils.cjs +6 -0
  269. package/dist/lib/object/utils.d.ts +165 -0
  270. package/dist/lib/object/utils.d.ts.map +1 -0
  271. package/dist/lib/object/utils.js +444 -0
  272. package/dist/lib/object/utils.mjs +6 -0
  273. package/dist/lib/primitive/boolean/boolean.cjs +1 -0
  274. package/dist/lib/primitive/boolean/boolean.d.ts +276 -0
  275. package/dist/lib/primitive/boolean/boolean.d.ts.map +1 -0
  276. package/dist/lib/primitive/boolean/boolean.js +615 -0
  277. package/dist/lib/primitive/boolean/boolean.mjs +1 -0
  278. package/dist/lib/primitive/boolean/index.cjs +1 -0
  279. package/dist/lib/primitive/boolean/index.d.ts +3 -0
  280. package/dist/lib/primitive/boolean/index.d.ts.map +1 -0
  281. package/dist/lib/primitive/boolean/index.js +2 -0
  282. package/dist/lib/primitive/boolean/index.mjs +1 -0
  283. package/dist/lib/primitive/boolean/types.cjs +1 -0
  284. package/dist/lib/primitive/boolean/types.d.ts +14 -0
  285. package/dist/lib/primitive/boolean/types.d.ts.map +1 -0
  286. package/dist/lib/primitive/boolean/types.js +1 -0
  287. package/dist/lib/primitive/boolean/types.mjs +0 -0
  288. package/dist/lib/primitive/date/date.cjs +1 -0
  289. package/dist/lib/primitive/date/date.d.ts +641 -0
  290. package/dist/lib/primitive/date/date.d.ts.map +1 -0
  291. package/dist/lib/primitive/date/date.js +1998 -0
  292. package/dist/lib/primitive/date/date.mjs +1 -0
  293. package/dist/lib/primitive/date/index.cjs +1 -0
  294. package/dist/lib/primitive/date/index.d.ts +3 -0
  295. package/dist/lib/primitive/date/index.d.ts.map +1 -0
  296. package/dist/lib/primitive/date/index.js +2 -0
  297. package/dist/lib/primitive/date/index.mjs +1 -0
  298. package/dist/lib/primitive/date/types.cjs +1 -0
  299. package/dist/lib/primitive/date/types.d.ts +205 -0
  300. package/dist/lib/primitive/date/types.d.ts.map +1 -0
  301. package/dist/lib/primitive/date/types.js +7 -0
  302. package/dist/lib/primitive/date/types.mjs +0 -0
  303. package/dist/lib/primitive/index.cjs +5 -0
  304. package/dist/lib/primitive/index.d.ts +5 -0
  305. package/dist/lib/primitive/index.d.ts.map +1 -0
  306. package/dist/lib/primitive/index.js +4 -0
  307. package/dist/lib/primitive/index.mjs +5 -0
  308. package/dist/lib/primitive/string/index.cjs +5 -0
  309. package/dist/lib/primitive/string/index.d.ts +3 -0
  310. package/dist/lib/primitive/string/index.d.ts.map +1 -0
  311. package/dist/lib/primitive/string/index.js +2 -0
  312. package/dist/lib/primitive/string/index.mjs +5 -0
  313. package/dist/lib/primitive/string/parser-2.cjs +8 -0
  314. package/dist/lib/primitive/string/parser-2.d.ts +17 -0
  315. package/dist/lib/primitive/string/parser-2.d.ts.map +1 -0
  316. package/dist/lib/primitive/string/parser-2.js +656 -0
  317. package/dist/lib/primitive/string/parser-2.mjs +8 -0
  318. package/dist/lib/primitive/string/parser.cjs +3 -0
  319. package/dist/lib/primitive/string/parser.d.ts +9 -0
  320. package/dist/lib/primitive/string/parser.d.ts.map +1 -0
  321. package/dist/lib/primitive/string/parser.js +775 -0
  322. package/dist/lib/primitive/string/parser.mjs +3 -0
  323. package/dist/lib/primitive/string/string.cjs +5 -0
  324. package/dist/lib/primitive/string/string.d.ts +535 -0
  325. package/dist/lib/primitive/string/string.d.ts.map +1 -0
  326. package/dist/lib/primitive/string/string.js +1301 -0
  327. package/dist/lib/primitive/string/string.mjs +5 -0
  328. package/dist/lib/primitive/string/types.cjs +1 -0
  329. package/dist/lib/primitive/string/types.d.ts +318 -0
  330. package/dist/lib/primitive/string/types.d.ts.map +1 -0
  331. package/dist/lib/primitive/string/types.js +5 -0
  332. package/dist/lib/primitive/string/types.mjs +0 -0
  333. package/dist/lib/primitive/types.cjs +1 -0
  334. package/dist/lib/primitive/types.d.ts +27 -0
  335. package/dist/lib/primitive/types.d.ts.map +1 -0
  336. package/dist/lib/primitive/types.js +1 -0
  337. package/dist/lib/primitive/types.mjs +0 -0
  338. package/dist/lib/url/index.cjs +1 -0
  339. package/dist/lib/url/index.d.ts +3 -0
  340. package/dist/lib/url/index.d.ts.map +1 -0
  341. package/dist/lib/url/index.js +2 -0
  342. package/dist/lib/url/index.mjs +1 -0
  343. package/dist/lib/url/parser.cjs +1 -0
  344. package/dist/lib/url/parser.d.ts +216 -0
  345. package/dist/lib/url/parser.d.ts.map +1 -0
  346. package/dist/lib/url/parser.js +488 -0
  347. package/dist/lib/url/parser.mjs +1 -0
  348. package/dist/lib/url/types.cjs +1 -0
  349. package/dist/lib/url/types.d.ts +18 -0
  350. package/dist/lib/url/types.d.ts.map +1 -0
  351. package/dist/lib/url/types.js +1 -0
  352. package/dist/lib/url/types.mjs +0 -0
  353. package/dist/tsconfig.lib.tsbuildinfo +1 -0
  354. package/package.json +150 -0
@@ -0,0 +1,858 @@
1
+ /**
2
+ * @fileoverview Comprehensive color management utility with support for multiple color formats,
3
+ * conversions, and color manipulation operations.
4
+ */
5
+ import { NAMED_COLORS } from './constants.js';
6
+ /**
7
+ * Comprehensive RGB/RGBA regex patterns
8
+ */
9
+ export const RGB_PATTERNS = {
10
+ /**
11
+ * Matches legacy comma-separated RGB
12
+ * @example rgb(255, 0, 0)
13
+ */
14
+ LEGACY_RGB: /^rgb\(\s*(\d{1,3}|\.\d+|\d+\.\d+%?)\s*,\s*(\d{1,3}|\.\d+|\d+\.\d+%?)\s*,\s*(\d{1,3}|\.\d+|\d+\.\d+%?)\s*\)$/i,
15
+ /**
16
+ * Matches modern space-separated RGB
17
+ * @example rgb(255 0 0)
18
+ */
19
+ MODERN_RGB: /^rgb\(\s*(\d{1,3}|\.\d+|\d+\.\d+%?)\s+(\d{1,3}|\.\d+|\d+\.\d+%?)\s+(\d{1,3}|\.\d+|\d+\.\d+%?)\s*\)$/i,
20
+ /**
21
+ * Matches legacy comma-separated RGBA
22
+ * @example rgba(255, 0, 0, 0.5)
23
+ * @example rgba(255, 0, 0, 50%)
24
+ */
25
+ LEGACY_RGBA: /^rgba\(\s*(\d{1,3}|\.\d+|\d+\.\d+%?)\s*,\s*(\d{1,3}|\.\d+|\d+\.\d+%?)\s*,\s*(\d{1,3}|\.\d+|\d+\.\d+%?)\s*,\s*(0|1|0?\.\d+|\d{1,3}%?|\.\d+%?)\s*\)$/i,
26
+ /**
27
+ * Matches modern space-separated RGBA with slash
28
+ * @example rgb(255 0 0 / 0.5)
29
+ * @example rgb(255 0 0 / 50%)
30
+ * @example rgba(255 0 0 / 0.5)
31
+ */
32
+ MODERN_RGBA: /^rgba?\(\s*(\d{1,3}|\.\d+|\d+\.\d+%?)\s+(\d{1,3}|\.\d+|\d+\.\d+%?)\s+(\d{1,3}|\.\d+|\d+\.\d+%?)\s*\/\s*(0|1|0?\.\d+|\d{1,3}%?|\.\d+%?)\s*\)$/i,
33
+ /**
34
+ * Combined pattern that matches all RGB/RGBA variations
35
+ * Supports:
36
+ * - Legacy comma-separated: rgb(255, 0, 0), rgba(255, 0, 0, 0.5)
37
+ * - Modern space-separated: rgb(255 0 0), rgb(255 0 0 / 0.5)
38
+ * - Mixed separators: rgb(255,0,0), rgb(255 0 0)
39
+ * - Percentages: rgb(100% 0% 0%), rgb(100% 0% 0% / 50%)
40
+ * - Decimal numbers: rgb(255.5 0 0), rgba(255, 0, 0, 0.5)
41
+ * - CSS variables: rgb(var(--red) var(--green) var(--blue))
42
+ */
43
+ COMPREHENSIVE: /^rgba?\(\s*((?:\d{1,3}(?:\.\d+)?%?|\.\d+%?|var\([^)]+\)))\s*(?:[,\s]\s*((?:\d{1,3}(?:\.\d+)?%?|\.\d+%?|var\([^)]+\))))?\s*(?:[,\s]\s*((?:\d{1,3}(?:\.\d+)?%?|\.\d+%?|var\([^)]+\))))?\s*(?:(?:\s*[,\s]?\s*\/?\s*|\s*\/\s*)((?:0|1|0?\.\d+|\.\d+|\d{1,3}%?|var\([^)]+\))))?\s*\)$/i,
44
+ /**
45
+ * Strict RGB pattern with validation
46
+ * Matches integers (0-255) or percentages (0-100%)
47
+ */
48
+ STRICT_RGB: /^rgb\(\s*((?:\d{1,2}|1\d{2}|2[0-4]\d|25[0-5]|\d{1,2}(?:\.\d+)?%|100%|\.\d+%?|var\([^)]+\)))\s*,\s*((?:\d{1,2}|1\d{2}|2[0-4]\d|25[0-5]|\d{1,2}(?:\.\d+)?%|100%|\.\d+%?|var\([^)]+\)))\s*,\s*((?:\d{1,2}|1\d{2}|2[0-4]\d|25[0-5]|\d{1,2}(?:\.\d+)?%|100%|\.\d+%?|var\([^)]+\)))\s*\)$/i,
49
+ /**
50
+ * Strict RGBA pattern with validation
51
+ */
52
+ STRICT_RGBA: /^rgba\(\s*((?:\d{1,2}|1\d{2}|2[0-4]\d|25[0-5]|\d{1,2}(?:\.\d+)?%|100%|\.\d+%?|var\([^)]+\)))\s*,\s*((?:\d{1,2}|1\d{2}|2[0-4]\d|25[0-5]|\d{1,2}(?:\.\d+)?%|100%|\.\d+%?|var\([^)]+\)))\s*,\s*((?:\d{1,2}|1\d{2}|2[0-4]\d|25[0-5]|\d{1,2}(?:\.\d+)?%|100%|\.\d+%?|var\([^)]+\)))\s*,\s*((?:0|1|0?\.\d+|\.\d+|\d{1,2}(?:\.\d+)?%|100%|var\([^)]+\)))\s*\)$/i,
53
+ /**
54
+ * Modern RGB with optional alpha
55
+ */
56
+ MODERN_RGB_ALPHA: /^rgba?\(\s*((?:\d{1,3}(?:\.\d+)?%?|\.\d+%?|var\([^)]+\)))\s+((?:\d{1,3}(?:\.\d+)?%?|\.\d+%?|var\([^)]+\)))\s+((?:\d{1,3}(?:\.\d+)?%?|\.\d+%?|var\([^)]+\)))\s*(?:\/\s*((?:0|1|0?\.\d+|\.\d+|\d{1,3}%?|var\([^)]+\))))?\s*\)$/i,
57
+ };
58
+ /**
59
+ * Enhanced RGB/RGBA parsing function
60
+ * @param colorString - RGB/RGBA color string
61
+ * @returns Parsed RGBA values or null if invalid
62
+ */
63
+ export const parseRgbaColor = (colorString) => {
64
+ const str = colorString.trim().toLowerCase();
65
+ // Return null for empty strings or CSS variables
66
+ if (!str || str.includes('var(')) {
67
+ return null;
68
+ }
69
+ // Helper to parse a single color component (r, g, or b)
70
+ const parseComponent = (component) => {
71
+ if (!component)
72
+ return null;
73
+ const trimmed = component.trim();
74
+ if (!trimmed || trimmed.includes('var('))
75
+ return null;
76
+ if (trimmed.endsWith('%')) {
77
+ const percent = parseFloat(trimmed);
78
+ if (isNaN(percent))
79
+ return null;
80
+ // Clamp percentage to 0-100 range, then convert to 0-255
81
+ const clamped = Math.max(0, Math.min(100, percent));
82
+ return Math.round((clamped * 255) / 100);
83
+ }
84
+ const num = parseFloat(trimmed);
85
+ if (isNaN(num))
86
+ return null;
87
+ return Math.round(Math.max(0, Math.min(255, num)));
88
+ };
89
+ // Helper to parse alpha component
90
+ const parseAlpha = (alpha) => {
91
+ if (!alpha)
92
+ return 1;
93
+ const trimmed = alpha.trim();
94
+ if (!trimmed || trimmed.includes('var('))
95
+ return 1;
96
+ if (trimmed.endsWith('%')) {
97
+ const percent = parseFloat(trimmed);
98
+ if (isNaN(percent))
99
+ return 1;
100
+ return Math.max(0, Math.min(100, percent)) / 100;
101
+ }
102
+ const num = parseFloat(trimmed);
103
+ if (isNaN(num))
104
+ return 1;
105
+ return Math.max(0, Math.min(1, num));
106
+ };
107
+ // Pattern 1: Legacy comma-separated (must come first to properly handle percentages)
108
+ // rgb(255, 0, 0) or rgba(255, 0, 0, 0.5) or rgba(255, 0, 0, 50%)
109
+ // Also handles rgb(100%, 0%, 0%)
110
+ const legacyCommaPattern = /^rgba?\(\s*([^,]+)\s*,\s*([^,]+)\s*,\s*([^,]+)\s*(?:,\s*([^\)]+))?\s*\)$/;
111
+ let match = str.match(legacyCommaPattern);
112
+ if (match) {
113
+ const r = parseComponent(match[1]);
114
+ const g = parseComponent(match[2]);
115
+ const b = parseComponent(match[3]);
116
+ const a = parseAlpha(match[4]);
117
+ if (r !== null && g !== null && b !== null) {
118
+ return [r, g, b, a];
119
+ }
120
+ }
121
+ // Pattern 2: Modern space-separated with slash alpha
122
+ // rgb(255 0 0 / 0.5) or rgba(255 0 0 / 50%)
123
+ const modernSlashPattern = /^rgba?\(\s*([^\s\/]+)\s+([^\s\/]+)\s+([^\s\/]+)\s*(?:\/\s*([^\)]+))?\s*\)$/;
124
+ match = str.match(modernSlashPattern);
125
+ if (match) {
126
+ const r = parseComponent(match[1]);
127
+ const g = parseComponent(match[2]);
128
+ const b = parseComponent(match[3]);
129
+ const a = parseAlpha(match[4]);
130
+ if (r !== null && g !== null && b !== null) {
131
+ return [r, g, b, a];
132
+ }
133
+ }
134
+ // Pattern 3: Modern space-separated without slash (rgb only, no alpha)
135
+ // rgb(255 0 0)
136
+ const modernSpacePattern = /^rgb\(\s*([^\s,]+)\s+([^\s,]+)\s+([^\s,]+)\s*\)$/;
137
+ match = str.match(modernSpacePattern);
138
+ if (match) {
139
+ const r = parseComponent(match[1]);
140
+ const g = parseComponent(match[2]);
141
+ const b = parseComponent(match[3]);
142
+ if (r !== null && g !== null && b !== null) {
143
+ return [r, g, b, 1];
144
+ }
145
+ }
146
+ return null;
147
+ };
148
+ /**
149
+ * Enhanced color pattern collection with improved RGB support
150
+ */
151
+ export const COLOR_PATTERNS = {
152
+ // Hex patterns (unchanged)
153
+ HEX: /^#([a-fA-F0-9]{3,4}|[a-fA-F0-9]{6}|[a-fA-F0-9]{8})$/,
154
+ // RGB patterns (enhanced)
155
+ RGB: RGB_PATTERNS.COMPREHENSIVE,
156
+ // HSL patterns (supports both hsl and hsla)
157
+ HSL: /^hsla?\(\s*(\d+(?:\.\d+)?)(?:deg|rad|grad|turn)?\s*,\s*(\d+(?:\.\d+)?)%\s*,\s*(\d+(?:\.\d+)?)%(?:\s*[,\s/]\s*(\d*\.?\d+%?))?\s*\)$/i,
158
+ // OKLCH pattern
159
+ OKLCH: /^oklch\(\s*(\d+(?:\.\d+)?%?)\s+(\d+(?:\.\d+)?)\s+(\d+(?:\.\d+)?)(?:\s+[,\s/]\s*(\d*\.?\d+%?))?\s*\)$/i,
160
+ // OKLAB pattern
161
+ OKLAB: /^oklab\(\s*(\d+(?:\.\d+)?%?)\s+([+-]?\d+(?:\.\d+)?)\s+([+-]?\d+(?:\.\d+)?)(?:\s+[,\s/]\s*(\d*\.?\d+%?))?\s*\)$/i,
162
+ // HSV pattern
163
+ HSV: /^hsv\(\s*(\d+(?:\.\d+)?)(?:deg|rad|grad|turn)?\s*,\s*(\d+(?:\.\d+)?)%\s*,\s*(\d+(?:\.\d+)?)%(?:\s*[,\s/]\s*(\d*\.?\d+%?))?\s*\)$/i,
164
+ // CMYK pattern
165
+ CMYK: /^cmyk\(\s*(\d+(?:\.\d+)?)%\s*,\s*(\d+(?:\.\d+)?)%\s*,\s*(\d+(?:\.\d+)?)%\s*,\s*(\d+(?:\.\d+)?)%(?:\s*[,\s/]\s*(\d*\.?\d+%?))?\s*\)$/i,
166
+ // CSS color() function
167
+ COLOR_FUNCTION: /^color\(\s*([a-z]+)\s+((?:\d+(?:\.\d+)?%?\s+)*\d+(?:\.\d+)?%?)(?:\s*[,\s/]\s*(\d*\.?\d+%?))?\s*\)$/i,
168
+ // Lab color
169
+ LAB: /^lab\(\s*(\d+(?:\.\d+)?%?)\s+([+-]?\d+(?:\.\d+)?)\s+([+-]?\d+(?:\.\d+)?)(?:\s*[,\s/]\s*(\d*\.?\d+%?))?\s*\)$/i,
170
+ // LCH color
171
+ LCH: /^lch\(\s*(\d+(?:\.\d+)?%?)\s+(\d+(?:\.\d+)?)\s+(\d+(?:\.\d+)?)(?:\s*[,\s/]\s*(\d*\.?\d+%?))?\s*\)$/i,
172
+ };
173
+ /**
174
+ * Test RGB/RGBA strings against all patterns
175
+ * @param colorString - Color string to test
176
+ * @returns Array of matching pattern names
177
+ */
178
+ export const testRgbaPatterns = (colorString) => {
179
+ const matches = [];
180
+ const str = colorString.trim();
181
+ Object.entries(RGB_PATTERNS).forEach(([name, pattern]) => {
182
+ if (pattern.test(str)) {
183
+ matches.push(name);
184
+ }
185
+ });
186
+ return matches;
187
+ };
188
+ /**
189
+ * Enhanced parseColor function with better RGB support
190
+ */
191
+ export const parseColor = (color) => {
192
+ if (!color || typeof color !== 'string') {
193
+ return null;
194
+ }
195
+ const str = color.trim().toLowerCase();
196
+ if (!str) {
197
+ return null;
198
+ }
199
+ // Handle transparent
200
+ if (str === 'transparent') {
201
+ return [0, 0, 0, 0];
202
+ }
203
+ // Handle named colors
204
+ const namedColor = str;
205
+ if (NAMED_COLORS[namedColor]) {
206
+ return parseColor(NAMED_COLORS[namedColor]);
207
+ }
208
+ // Try RGB/RGBA parsing first (most common for programmatic use)
209
+ const rgba = parseRgbaColor(str);
210
+ if (rgba) {
211
+ return rgba;
212
+ }
213
+ // Try hex colors
214
+ const hexMatch = str.match(COLOR_PATTERNS.HEX);
215
+ if (hexMatch) {
216
+ let hex = hexMatch[1];
217
+ // Expand shorthand hex (#RGB or #RGBA to #RRGGBB or #RRGGBBAA)
218
+ if (hex.length === 3 || hex.length === 4) {
219
+ hex = hex
220
+ .split('')
221
+ .map((c) => c + c)
222
+ .join('');
223
+ }
224
+ const r = parseInt(hex.substring(0, 2), 16);
225
+ const g = parseInt(hex.substring(2, 4), 16);
226
+ const b = parseInt(hex.substring(4, 6), 16);
227
+ const a = hex.length === 8 ? parseInt(hex.substring(6, 8), 16) / 255 : 1;
228
+ // Validate parsed values
229
+ if (!isNaN(r) && !isNaN(g) && !isNaN(b) && !isNaN(a)) {
230
+ return [r, g, b, a];
231
+ }
232
+ }
233
+ // Try HSL colors
234
+ const hslMatch = str.match(COLOR_PATTERNS.HSL);
235
+ if (hslMatch) {
236
+ const h = parseFloat(hslMatch[1]);
237
+ const s = parseFloat(hslMatch[2]) / 100;
238
+ const l = parseFloat(hslMatch[3]) / 100;
239
+ const a = hslMatch[4] ? parsePercentage(hslMatch[4]) : 1;
240
+ if (!isNaN(h) && !isNaN(s) && !isNaN(l) && !isNaN(a)) {
241
+ // Convert HSL to RGB
242
+ return hslToRgb(h, s, l, a);
243
+ }
244
+ }
245
+ // Try OKLCH colors
246
+ const oklchMatch = str.match(COLOR_PATTERNS.OKLCH);
247
+ if (oklchMatch) {
248
+ const L = parsePercentage(oklchMatch[1]);
249
+ const C = parseFloat(oklchMatch[2]);
250
+ const H = parseFloat(oklchMatch[3]);
251
+ const a = oklchMatch[4] ? parsePercentage(oklchMatch[4]) : 1;
252
+ if (!isNaN(L) && !isNaN(C) && !isNaN(H) && !isNaN(a)) {
253
+ // Convert OKLCH to RGB
254
+ return oklchToRgb(L, C, H, a);
255
+ }
256
+ }
257
+ return null;
258
+ };
259
+ /**
260
+ * Convert HSL to RGB
261
+ */
262
+ export const hslToRgb = (h, s, l, a = 1) => {
263
+ h = h % 360;
264
+ if (h < 0)
265
+ h += 360;
266
+ const c = (1 - Math.abs(2 * l - 1)) * s;
267
+ const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
268
+ const m = l - c / 2;
269
+ let r = 0, g = 0, b = 0;
270
+ if (h >= 0 && h < 60) {
271
+ [r, g, b] = [c, x, 0];
272
+ }
273
+ else if (h >= 60 && h < 120) {
274
+ [r, g, b] = [x, c, 0];
275
+ }
276
+ else if (h >= 120 && h < 180) {
277
+ [r, g, b] = [0, c, x];
278
+ }
279
+ else if (h >= 180 && h < 240) {
280
+ [r, g, b] = [0, x, c];
281
+ }
282
+ else if (h >= 240 && h < 300) {
283
+ [r, g, b] = [x, 0, c];
284
+ }
285
+ else if (h >= 300 && h < 360) {
286
+ [r, g, b] = [c, 0, x];
287
+ }
288
+ return [
289
+ Math.round((r + m) * 255),
290
+ Math.round((g + m) * 255),
291
+ Math.round((b + m) * 255),
292
+ a,
293
+ ];
294
+ };
295
+ /**
296
+ * Convert OKLCH to RGB (simplified approximation)
297
+ */
298
+ export const oklchToRgb = (L, C, H, a = 1) => {
299
+ // This is a simplified approximation
300
+ // Real conversion is complex and requires proper color space conversion
301
+ const hue = H % 360;
302
+ const saturation = Math.min(100, C * 100);
303
+ const lightness = L * 100;
304
+ return hslToRgb(hue, saturation / 100, lightness / 100, a);
305
+ };
306
+ /**
307
+ * Formats RGBA values into a CSS rgba() or rgb() string
308
+ * @param r - Red value (0-255) or CSS variable
309
+ * @param g - Green value (0-255) or CSS variable
310
+ * @param b - Blue value (0-255) or CSS variable
311
+ * @param a - Alpha value (0-1) or CSS variable (default: 1)
312
+ * @returns Formatted rgba string
313
+ * @example
314
+ * formatRgba(255, 0, 0, 0.5) // returns "rgba(255, 0, 0, 0.5)"
315
+ * formatRgba(255, 0, 0, 'var(--opacity)') // returns "rgba(255, 0, 0, var(--opacity))"
316
+ */
317
+ export const formatRgba = (r, g, b, a = 1) => {
318
+ // Handle non-numeric or CSS variable values
319
+ const isVariable = (value) => typeof value === 'string' && value.includes('var(');
320
+ const isAlphaVariable = isVariable(a);
321
+ const hasAlpha = isAlphaVariable || (typeof a === 'number' && a !== 1);
322
+ // Use modern CSS syntax with spaces and slash for alpha
323
+ if (hasAlpha) {
324
+ return `rgba(${r} ${g} ${b} / ${a})`;
325
+ }
326
+ return `rgb(${r} ${g} ${b})`;
327
+ };
328
+ /**
329
+ * Converts a numeric value to a two-digit hex string
330
+ * @param value - Number between 0-255
331
+ * @returns Two-digit hex string
332
+ * @private
333
+ */
334
+ const numToHex = (value) => {
335
+ const hex = Math.round(value).toString(16);
336
+ return hex.length === 1 ? `0${hex}` : hex;
337
+ };
338
+ /**
339
+ * Converts a percentage string to a number
340
+ * @param value - Percentage string (e.g., "50%")
341
+ * @returns Number between 0-1
342
+ * @private
343
+ */
344
+ const parsePercentage = (value) => {
345
+ if (value.endsWith('%')) {
346
+ return parseFloat(value) / 100;
347
+ }
348
+ return parseFloat(value);
349
+ };
350
+ /**
351
+ * Parses any color string to RGBA values
352
+ * @param color - Color string in any supported format
353
+ * @returns RGBA array [r, g, b, a] or null if invalid
354
+ */
355
+ // export const parseColor = (
356
+ // color: string
357
+ // ): [number, number, number, number] | null => {
358
+ // // Handle transparent
359
+ // if (color.toLowerCase() === 'transparent') {
360
+ // return [0, 0, 0, 0];
361
+ // }
362
+ // // Handle named colors
363
+ // const namedColor = color.toLowerCase() as NamedColor;
364
+ // if (NAMED_COLORS[namedColor]) {
365
+ // return parseColor(NAMED_COLORS[namedColor]);
366
+ // }
367
+ // // Handle hex colors
368
+ // const hexMatch = color.match(COLOR_PATTERNS.HEX);
369
+ // if (hexMatch) {
370
+ // let hex = hexMatch[1];
371
+ // // Expand shorthand hex
372
+ // if (hex.length === 3 || hex.length === 4) {
373
+ // hex = hex
374
+ // .split('')
375
+ // .map((c) => c + c)
376
+ // .join('');
377
+ // }
378
+ // const r = parseInt(hex.substring(0, 2), 16);
379
+ // const g = parseInt(hex.substring(2, 4), 16);
380
+ // const b = parseInt(hex.substring(4, 6), 16);
381
+ // const a =
382
+ // hex.length === 8 ? round(parseInt(hex.substring(6, 8), 16) / 255, 1) : 1;
383
+ // return [r, g, b, a];
384
+ // }
385
+ // // Handle rgb/rgba colors
386
+ // const rgbMatch = color.match(COLOR_PATTERNS.RGB);
387
+ // if (rgbMatch) {
388
+ // const r = parseInt(rgbMatch[1]);
389
+ // const g = parseInt(rgbMatch[2]);
390
+ // const b = parseInt(rgbMatch[3]);
391
+ // const a = rgbMatch[4] ? parsePercentage(rgbMatch[4]) : 1;
392
+ // return [r, g, b, a];
393
+ // }
394
+ // // Handle hsl/hsla colors
395
+ // const hslMatch = color.match(COLOR_PATTERNS.HSL);
396
+ // if (hslMatch) {
397
+ // let h = parseFloat(hslMatch[1]);
398
+ // const s = parseFloat(hslMatch[2]) / 100;
399
+ // const l = parseFloat(hslMatch[3]) / 100;
400
+ // const a = hslMatch[4] ? parsePercentage(hslMatch[4]) : 1;
401
+ // // Convert HSL to RGB
402
+ // const chroma = (1 - Math.abs(2 * l - 1)) * s;
403
+ // const x = chroma * (1 - Math.abs(((h / 60) % 2) - 1));
404
+ // const m = l - chroma / 2;
405
+ // let r = 0,
406
+ // g = 0,
407
+ // b = 0;
408
+ // if (h >= 0 && h < 60) {
409
+ // [r, g, b] = [chroma, x, 0];
410
+ // } else if (h >= 60 && h < 120) {
411
+ // [r, g, b] = [x, chroma, 0];
412
+ // } else if (h >= 120 && h < 180) {
413
+ // [r, g, b] = [0, chroma, x];
414
+ // } else if (h >= 180 && h < 240) {
415
+ // [r, g, b] = [0, x, chroma];
416
+ // } else if (h >= 240 && h < 300) {
417
+ // [r, g, b] = [x, 0, chroma];
418
+ // } else if (h >= 300 && h < 360) {
419
+ // [r, g, b] = [chroma, 0, x];
420
+ // }
421
+ // return [
422
+ // Math.round((r + m) * 255),
423
+ // Math.round((g + m) * 255),
424
+ // Math.round((b + m) * 255),
425
+ // a,
426
+ // ];
427
+ // }
428
+ // // Handle oklch colors
429
+ // const oklchMatch = color.match(COLOR_PATTERNS.OKLCH);
430
+ // if (oklchMatch) {
431
+ // // For simplicity, we'll convert oklch to rgb via hsl approximation
432
+ // // Note: This is a simplified conversion
433
+ // const L = parsePercentage(oklchMatch[1]);
434
+ // const C = parseFloat(oklchMatch[2]);
435
+ // const H = parseFloat(oklchMatch[3]);
436
+ // const a = oklchMatch[4] ? parsePercentage(oklchMatch[4]) : 1;
437
+ // // Approximate conversion from OKLCH to RGB
438
+ // // This is a simplified version - real conversion is more complex
439
+ // const h = H;
440
+ // const s = C * 100;
441
+ // const l = L * 100;
442
+ // const rgb = parseColor(`hsl(${h}, ${s}%, ${l}%)`);
443
+ // if (rgb) {
444
+ // rgb[3] = a;
445
+ // return rgb;
446
+ // }
447
+ // }
448
+ // return null;
449
+ // };
450
+ /**
451
+ * Validates if a string is a valid color
452
+ * @param color - Color string to validate
453
+ * @returns True if valid color
454
+ */
455
+ export const isValidColor = (color) => {
456
+ // Check named colors
457
+ if (color.toLowerCase() in NAMED_COLORS) {
458
+ return true;
459
+ }
460
+ // For RGB/RGBA, delegate to the actual parser to ensure proper validation
461
+ if (color.toLowerCase().trim().startsWith('rgb')) {
462
+ return parseRgbaColor(color) !== null;
463
+ }
464
+ // Check other patterns (HSL, hex, etc.)
465
+ const { RGB, ...otherPatterns } = COLOR_PATTERNS;
466
+ return Object.values(otherPatterns).some((pattern) => pattern.test(color));
467
+ };
468
+ /**
469
+ * Converts RGBA values to hex string
470
+ * @param r - Red value (0-255)
471
+ * @param g - Green value (0-255)
472
+ * @param b - Blue value (0-255)
473
+ * @param a - Alpha value (0-1)
474
+ * @returns Hex color string
475
+ */
476
+ export const rgbaToHex = (r, g, b, a = 1) => {
477
+ const hex = `#${numToHex(r)}${numToHex(g)}${numToHex(b)}`;
478
+ return a < 1 ? `${hex}${numToHex(Math.round(a * 255))}` : hex;
479
+ };
480
+ /**
481
+ * Converts RGBA values to HSL string
482
+ * @param r - Red value (0-255)
483
+ * @param g - Green value (0-255)
484
+ * @param b - Blue value (0-255)
485
+ * @param a - Alpha value (0-1)
486
+ * @returns HSL/HSLA color string
487
+ */
488
+ export const rgbaToHsl = (r, g, b, a = 1) => {
489
+ const rNorm = r / 255;
490
+ const gNorm = g / 255;
491
+ const bNorm = b / 255;
492
+ const max = Math.max(rNorm, gNorm, bNorm);
493
+ const min = Math.min(rNorm, gNorm, bNorm);
494
+ let h = 0;
495
+ let s = 0;
496
+ const l = (max + min) / 2;
497
+ if (max !== min) {
498
+ const d = max - min;
499
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
500
+ switch (max) {
501
+ case rNorm:
502
+ h = (gNorm - bNorm) / d + (gNorm < bNorm ? 6 : 0);
503
+ break;
504
+ case gNorm:
505
+ h = (bNorm - rNorm) / d + 2;
506
+ break;
507
+ case bNorm:
508
+ h = (rNorm - gNorm) / d + 4;
509
+ break;
510
+ }
511
+ h /= 6;
512
+ }
513
+ h = Math.round(h * 360);
514
+ s = Math.round(s * 100);
515
+ const lPercent = Math.round(l * 100);
516
+ if (a === 1) {
517
+ return `hsl(${h}, ${s}%, ${lPercent}%)`;
518
+ }
519
+ return `hsla(${h}, ${s}%, ${lPercent}%, ${a})`;
520
+ };
521
+ /**
522
+ * Converts RGBA values to HSV string
523
+ * @param r - Red value (0-255)
524
+ * @param g - Green value (0-255)
525
+ * @param b - Blue value (0-255)
526
+ * @param a - Alpha value (0-1)
527
+ * @returns HSV/HSVA color string
528
+ */
529
+ export const rgbaToHsv = (r, g, b, a = 1) => {
530
+ const rNorm = r / 255;
531
+ const gNorm = g / 255;
532
+ const bNorm = b / 255;
533
+ const max = Math.max(rNorm, gNorm, bNorm);
534
+ const min = Math.min(rNorm, gNorm, bNorm);
535
+ const d = max - min;
536
+ let h = 0;
537
+ if (d !== 0) {
538
+ switch (max) {
539
+ case rNorm:
540
+ h = (gNorm - bNorm) / d + (gNorm < bNorm ? 6 : 0);
541
+ break;
542
+ case gNorm:
543
+ h = (bNorm - rNorm) / d + 2;
544
+ break;
545
+ case bNorm:
546
+ h = (rNorm - gNorm) / d + 4;
547
+ break;
548
+ }
549
+ h /= 6;
550
+ }
551
+ const s = max === 0 ? 0 : d / max;
552
+ const v = max;
553
+ h = Math.round(h * 360);
554
+ const sPercent = Math.round(s * 100);
555
+ const vPercent = Math.round(v * 100);
556
+ if (a === 1) {
557
+ return `hsv(${h}, ${sPercent}%, ${vPercent}%)`;
558
+ }
559
+ return `hsva(${h}, ${sPercent}%, ${vPercent}%, ${a})`;
560
+ };
561
+ /**
562
+ * Converts RGBA values to CMYK string
563
+ * @param r - Red value (0-255)
564
+ * @param g - Green value (0-255)
565
+ * @param b - Blue value (0-255)
566
+ * @param a - Alpha value (0-1)
567
+ * @returns CMYK color string
568
+ */
569
+ export const rgbaToCmyk = (r, g, b, a = 1) => {
570
+ if (r === 0 && g === 0 && b === 0) {
571
+ return `cmyk(0%, 0%, 0%, 100%)`;
572
+ }
573
+ const rNorm = r / 255;
574
+ const gNorm = g / 255;
575
+ const bNorm = b / 255;
576
+ const k = 1 - Math.max(rNorm, gNorm, bNorm);
577
+ const c = (1 - rNorm - k) / (1 - k);
578
+ const m = (1 - gNorm - k) / (1 - k);
579
+ const y = (1 - bNorm - k) / (1 - k);
580
+ const cPercent = Math.round(c * 100);
581
+ const mPercent = Math.round(m * 100);
582
+ const yPercent = Math.round(y * 100);
583
+ const kPercent = Math.round(k * 100);
584
+ return `cmyk(${cPercent}%, ${mPercent}%, ${yPercent}%, ${kPercent}%)`;
585
+ };
586
+ /**
587
+ * Calculates relative luminance for contrast ratio
588
+ * @param r - Red value (0-255)
589
+ * @param g - Green value (0-255)
590
+ * @param b - Blue value (0-255)
591
+ * @returns Relative luminance (0-1)
592
+ * @private
593
+ */
594
+ export const calculateLuminance = (r, g, b) => {
595
+ const [rs, gs, bs] = [r, g, b].map((c) => {
596
+ c = c / 255;
597
+ return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
598
+ });
599
+ return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
600
+ };
601
+ /**
602
+ * Calculates contrast ratio between two colors
603
+ * @param color1 - First color string
604
+ * @param color2 - Second color string
605
+ * @returns Contrast ratio (1-21)
606
+ */
607
+ export const calculateContrastRatio = (color1, color2) => {
608
+ const rgb1 = parseColor(color1);
609
+ const rgb2 = parseColor(color2);
610
+ if (!rgb1 || !rgb2) {
611
+ return 1;
612
+ }
613
+ const l1 = calculateLuminance(rgb1[0], rgb1[1], rgb1[2]);
614
+ const l2 = calculateLuminance(rgb2[0], rgb2[1], rgb2[2]);
615
+ const lighter = Math.max(l1, l2);
616
+ const darker = Math.min(l1, l2);
617
+ return (lighter + 0.05) / (darker + 0.05);
618
+ };
619
+ /**
620
+ * Adjusts color based on options
621
+ * @param color - Input color string
622
+ * @param options - Adjustment options
623
+ * @returns Adjusted color string
624
+ */
625
+ export const adjustColor = (color, options) => {
626
+ const rgb = parseColor(color);
627
+ if (!rgb)
628
+ return color;
629
+ let [r, g, b, a] = rgb;
630
+ // Adjust alpha
631
+ if (options.alpha !== undefined) {
632
+ a = Math.max(0, Math.min(1, options.alpha));
633
+ }
634
+ // Convert to HSL for hue/saturation/lightness adjustments
635
+ let h = 0, s = 0, l = 0;
636
+ if (options.lightness !== undefined ||
637
+ options.saturation !== undefined ||
638
+ options.hue !== undefined) {
639
+ // Convert RGB to HSL
640
+ const rNorm = r / 255;
641
+ const gNorm = g / 255;
642
+ const bNorm = b / 255;
643
+ const max = Math.max(rNorm, gNorm, bNorm);
644
+ const min = Math.min(rNorm, gNorm, bNorm);
645
+ l = (max + min) / 2;
646
+ if (max !== min) {
647
+ const d = max - min;
648
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
649
+ switch (max) {
650
+ case rNorm:
651
+ h = (gNorm - bNorm) / d + (gNorm < bNorm ? 6 : 0);
652
+ break;
653
+ case gNorm:
654
+ h = (bNorm - rNorm) / d + 2;
655
+ break;
656
+ case bNorm:
657
+ h = (rNorm - gNorm) / d + 4;
658
+ break;
659
+ }
660
+ h /= 6;
661
+ }
662
+ h *= 360;
663
+ s *= 100;
664
+ l *= 100;
665
+ // Apply adjustments
666
+ if (options.hue !== undefined) {
667
+ h = (h + options.hue) % 360;
668
+ if (h < 0)
669
+ h += 360;
670
+ }
671
+ if (options.saturation !== undefined) {
672
+ s = Math.max(0, Math.min(100, s + options.saturation));
673
+ }
674
+ if (options.lightness !== undefined) {
675
+ l = Math.max(0, Math.min(100, l + options.lightness));
676
+ }
677
+ // Convert back to RGB
678
+ const hNorm = h / 360;
679
+ const sNorm = s / 100;
680
+ const lNorm = l / 100;
681
+ const chroma = (1 - Math.abs(2 * lNorm - 1)) * sNorm;
682
+ const x = chroma * (1 - Math.abs(((hNorm * 6) % 2) - 1));
683
+ const m = lNorm - chroma / 2;
684
+ let rNorm2 = 0, gNorm2 = 0, bNorm2 = 0;
685
+ if (hNorm < 1 / 6) {
686
+ [rNorm2, gNorm2, bNorm2] = [chroma, x, 0];
687
+ }
688
+ else if (hNorm < 2 / 6) {
689
+ [rNorm2, gNorm2, bNorm2] = [x, chroma, 0];
690
+ }
691
+ else if (hNorm < 3 / 6) {
692
+ [rNorm2, gNorm2, bNorm2] = [0, chroma, x];
693
+ }
694
+ else if (hNorm < 4 / 6) {
695
+ [rNorm2, gNorm2, bNorm2] = [0, x, chroma];
696
+ }
697
+ else if (hNorm < 5 / 6) {
698
+ [rNorm2, gNorm2, bNorm2] = [x, 0, chroma];
699
+ }
700
+ else {
701
+ [rNorm2, gNorm2, bNorm2] = [chroma, 0, x];
702
+ }
703
+ r = Math.round((rNorm2 + m) * 255);
704
+ g = Math.round((gNorm2 + m) * 255);
705
+ b = Math.round((bNorm2 + m) * 255);
706
+ }
707
+ // Adjust contrast
708
+ if (options.contrast !== undefined) {
709
+ const factor = (options.contrast / 100) * 2.55; // Convert to 0-255 range factor
710
+ const avg = (r + g + b) / 3;
711
+ r = Math.round(avg + (r - avg) * (1 + factor));
712
+ g = Math.round(avg + (g - avg) * (1 + factor));
713
+ b = Math.round(avg + (b - avg) * (1 + factor));
714
+ r = Math.max(0, Math.min(255, r));
715
+ g = Math.max(0, Math.min(255, g));
716
+ b = Math.max(0, Math.min(255, b));
717
+ }
718
+ return rgbaToHex(r, g, b, a);
719
+ };
720
+ /**
721
+ * Mixes two colors
722
+ * @param options - Mixing options
723
+ * @returns Mixed color string
724
+ */
725
+ export const mixColors = (options) => {
726
+ const { color, with: mixColor = 'transparent', percentage = 50, colorSpace = 'srgb', } = options;
727
+ const rgb1 = parseColor(color);
728
+ const rgb2 = parseColor(mixColor);
729
+ if (!rgb1 || !rgb2) {
730
+ return color;
731
+ }
732
+ const factor = percentage / 100;
733
+ let [r1, g1, b1, a1] = rgb1;
734
+ let [r2, g2, b2, a2] = rgb2;
735
+ // Apply opacities if provided
736
+ if (options.opacity !== undefined) {
737
+ a1 =
738
+ typeof options.opacity === 'number'
739
+ ? options.opacity
740
+ : parsePercentage(options.opacity);
741
+ }
742
+ if (options.withOpacity !== undefined) {
743
+ a2 =
744
+ typeof options.withOpacity === 'number'
745
+ ? options.withOpacity
746
+ : parsePercentage(options.withOpacity);
747
+ }
748
+ // Mix based on color space
749
+ switch (colorSpace) {
750
+ case 'srgb':
751
+ default:
752
+ // Simple linear interpolation in RGB
753
+ const r = Math.round(r1 + (r2 - r1) * factor);
754
+ const g = Math.round(g1 + (g2 - g1) * factor);
755
+ const b = Math.round(b1 + (b2 - b1) * factor);
756
+ const a = a1 + (a2 - a1) * factor;
757
+ return rgbaToHex(r, g, b, a);
758
+ case 'hsl':
759
+ // Mix in HSL space
760
+ const hsl1 = parseColor(rgbaToHsl(r1, g1, b1, a1));
761
+ const hsl2 = parseColor(rgbaToHsl(r2, g2, b2, a2));
762
+ if (!hsl1 || !hsl2) {
763
+ return color;
764
+ }
765
+ // Simplified HSL mixing - in practice would need proper HSL interpolation
766
+ const h = (hsl1[0] + (hsl2[0] - hsl1[0]) * factor) % 360;
767
+ const s = hsl1[1] + (hsl2[1] - hsl1[1]) * factor;
768
+ const l = hsl1[2] + (hsl2[2] - hsl1[2]) * factor;
769
+ return `hsl(${h}, ${s}%, ${l}%)`;
770
+ }
771
+ };
772
+ /**
773
+ * Generates a color palette from a base color
774
+ * @param baseColor - Base color string
775
+ * @param options - Palette generation options
776
+ * @returns Object with color shades
777
+ */
778
+ export const generatePalette = (baseColor, options = {}) => {
779
+ const rgb = parseColor(baseColor);
780
+ if (!rgb)
781
+ return {};
782
+ const shades = options.shades || 10;
783
+ const lightnessFactor = options.lightnessFactor || 0.1;
784
+ const saturationFactor = options.saturationFactor || 0.05;
785
+ const palette = {};
786
+ // Generate shades
787
+ for (let i = 0; i < shades; i++) {
788
+ const position = i / (shades - 1); // 0 to 1
789
+ const lightnessAdjustment = (position - 0.5) * 2 * lightnessFactor * 100; // -100 to 100
790
+ const shadeColor = adjustColor(baseColor, {
791
+ lightness: lightnessAdjustment,
792
+ saturation: (position - 0.5) * 2 * saturationFactor * 100,
793
+ });
794
+ const key = i === 0 ? '50' : `${i * 100}`;
795
+ palette[key] = shadeColor;
796
+ }
797
+ // Generate complementary colors if requested
798
+ if (options.includeComplementary) {
799
+ const hsl = parseColor(rgbaToHsl(rgb[0], rgb[1], rgb[2]));
800
+ if (hsl) {
801
+ let h = hsl[0];
802
+ h = (h + 180) % 360;
803
+ palette.complementary = `hsl(${h}, ${hsl[1]}%, ${hsl[2]}%)`;
804
+ }
805
+ }
806
+ // Generate analogous colors (30° apart)
807
+ if (options.includeAnalogous) {
808
+ const hsl = parseColor(rgbaToHsl(rgb[0], rgb[1], rgb[2]));
809
+ if (hsl) {
810
+ let h = hsl[0];
811
+ palette.analogous1 = `hsl(${(h - 30 + 360) % 360}, ${hsl[1]}%, ${hsl[2]}%)`;
812
+ palette.analogous2 = `hsl(${(h + 30) % 360}, ${hsl[1]}%, ${hsl[2]}%)`;
813
+ }
814
+ }
815
+ // Generate triadic colors (120° apart)
816
+ if (options.includeTriadic) {
817
+ const hsl = parseColor(rgbaToHsl(rgb[0], rgb[1], rgb[2]));
818
+ if (hsl) {
819
+ let h = hsl[0];
820
+ palette.triadic1 = `hsl(${(h + 120) % 360}, ${hsl[1]}%, ${hsl[2]}%)`;
821
+ palette.triadic2 = `hsl(${(h + 240) % 360}, ${hsl[1]}%, ${hsl[2]}%)`;
822
+ }
823
+ }
824
+ return palette;
825
+ };
826
+ /**
827
+ * Creates a contrasting color for accessibility
828
+ * @param color - Base color string
829
+ * @param options - Contrast options
830
+ * @returns Contrasting color string
831
+ */
832
+ export const createContrastColor = (color, options = {}) => {
833
+ const { lightColor = '#ffffff', darkColor = '#000000', targetRatio = 4.5, fallbackColor = '#000000', } = options;
834
+ const rgb = parseColor(color);
835
+ if (!rgb)
836
+ return fallbackColor;
837
+ // Calculate luminance
838
+ const luminance = calculateLuminance(rgb[0], rgb[1], rgb[2]);
839
+ // Calculate contrast with light and dark colors
840
+ const lightLuminance = calculateLuminance(...parseColor(lightColor).slice(0, 3));
841
+ const darkLuminance = calculateLuminance(...parseColor(darkColor).slice(0, 3));
842
+ const lightContrast = (Math.max(luminance, lightLuminance) + 0.05) /
843
+ (Math.min(luminance, lightLuminance) + 0.05);
844
+ const darkContrast = (Math.max(luminance, darkLuminance) + 0.05) /
845
+ (Math.min(luminance, darkLuminance) + 0.05);
846
+ // Return color with best contrast that meets target ratio
847
+ if (lightContrast >= targetRatio && darkContrast >= targetRatio) {
848
+ return lightContrast > darkContrast ? lightColor : darkColor;
849
+ }
850
+ else if (lightContrast >= targetRatio) {
851
+ return lightColor;
852
+ }
853
+ else if (darkContrast >= targetRatio) {
854
+ return darkColor;
855
+ }
856
+ // If neither meets target, return the one with better contrast
857
+ return lightContrast > darkContrast ? lightColor : darkColor;
858
+ };