@a_ng_d/utils-ui-color-palette 1.6.4 → 1.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.
package/README.md CHANGED
@@ -27,6 +27,20 @@ UI Color Palette is available for:
27
27
  - Color mixing and blending
28
28
  - Brightness and saturation adjustments
29
29
 
30
+ - **Dominant Colors Extraction**:
31
+
32
+ - Extract dominant colors from images using K-means clustering
33
+ - Configurable number of colors to extract
34
+ - Automatic color frequency calculation
35
+ - Support for transparent pixel filtering
36
+
37
+ - **Color Harmony Generation**:
38
+
39
+ - Generate analogous, complementary, triadic, tetradic, and square color harmonies
40
+ - Configurable analogous spread angle
41
+ - Automatic color relationship calculations
42
+ - Support for all major color harmony types
43
+
30
44
  - **Palette Generation**:
31
45
  - Create harmonious color schemes
32
46
  - Generate accessible color combinations
@@ -43,7 +57,13 @@ yarn add @a_ng_d/utils-ui-color-palette
43
57
  ## Usage
44
58
 
45
59
  ```typescript
46
- import { Color, Contrast, Data } from '@a_ng_d/utils-ui-color-palette'
60
+ import {
61
+ Color,
62
+ Contrast,
63
+ Data,
64
+ DominantColors,
65
+ ColorHarmony,
66
+ } from '@a_ng_d/utils-ui-color-palette'
47
67
 
48
68
  // Use Color class for color manipulation
49
69
  const color = new Color({
@@ -120,6 +140,87 @@ const minSizes = contrast.getMinFontSizes() // Returns minimum font sizes
120
140
  const lightness = contrast.getLightnessForContrastRatio(4.5) // For WCAG AA
121
141
  ```
122
142
 
143
+ ### Dominant Colors Extraction
144
+
145
+ ```typescript
146
+ import { DominantColors } from '@a_ng_d/utils-ui-color-palette'
147
+
148
+ // Prepare your image data (from canvas, file, etc.)
149
+ const canvas = document.createElement('canvas')
150
+ const ctx = canvas.getContext('2d')
151
+ // ... load your image into the canvas
152
+ const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
153
+
154
+ // Create a dominant colors extractor
155
+ const dominantColors = new DominantColors({
156
+ imageData: imageData,
157
+ colorCount: 5, // Extract top 5 colors
158
+ maxIterations: 50, // K-means algorithm iterations
159
+ tolerance: 0.01, // Convergence tolerance
160
+ skipTransparent: true, // Skip transparent pixels
161
+ })
162
+
163
+ // Extract the dominant colors
164
+ const colors = dominantColors.extractDominantColors()
165
+
166
+ // Results array contains:
167
+ colors.forEach((result) => {
168
+ console.log({
169
+ color: result.color, // RGB array: [r, g, b]
170
+ hex: result.hex, // Hex string: "#ff0000"
171
+ percentage: result.percentage, // Percentage of image: 25.5
172
+ count: result.count, // Number of pixels: 1000
173
+ })
174
+ })
175
+
176
+ // Update settings dynamically
177
+ dominantColors.setColorCount(8) // Change to 8 colors
178
+ dominantColors.updateOptions({
179
+ maxIterations: 100,
180
+ tolerance: 0.005,
181
+ })
182
+
183
+ // Get current configuration
184
+ const options = dominantColors.getOptions()
185
+ ```
186
+
187
+ ### Color Harmony Generation
188
+
189
+ ```typescript
190
+ import { ColorHarmony } from '@a_ng_d/utils-ui-color-palette'
191
+
192
+ // Create a color harmony generator
193
+ const colorHarmony = new ColorHarmony({
194
+ baseColor: [255, 0, 0], // Red base color
195
+ analogousSpread: 30, // 30 degrees for analogous colors
196
+ })
197
+
198
+ // Generate specific harmony types
199
+ const analogous = colorHarmony.generateAnalogous()
200
+ const complementary = colorHarmony.generateComplementary()
201
+ const triadic = colorHarmony.generateTriadic()
202
+ const tetradic = colorHarmony.generateTetradic()
203
+ const square = colorHarmony.generateSquare()
204
+
205
+ // Generate harmony by type
206
+ const harmony = colorHarmony.generateHarmony('triadic')
207
+
208
+ // Generate all harmonies at once
209
+ const allHarmonies = colorHarmony.getAllHarmonies()
210
+
211
+ // Results contain both RGB and hex values
212
+ console.log(triadic.colors) // [[255, 0, 0], [0, 255, 0], [0, 0, 255]]
213
+ console.log(triadic.hexColors) // ['#ff0000', '#00ff00', '#0000ff']
214
+
215
+ // Update settings
216
+ colorHarmony.setBaseColor([0, 128, 255])
217
+ colorHarmony.setAnalogousSpread(45)
218
+ colorHarmony.updateOptions({
219
+ analogousSpread: 60,
220
+ returnFormat: 'hex',
221
+ })
222
+ ```
223
+
123
224
  ### Palette Generation
124
225
 
125
226
  ```typescript
@@ -170,15 +271,15 @@ yarn test
170
271
 
171
272
  Current test coverage results:
172
273
 
173
- | File | % Stmts | % Branch | % Funcs | % Lines |
174
- | ----------- | ------- | -------- | ------- | ------- |
175
- | All files | 79.82 | 81.81 | 81.42 | 79.82 |
176
- | color | 72.02 | 72.36 | 69.04 | 72.02 |
177
- | color.ts | 72.02 | 72.36 | 69.04 | 72.02 |
178
- | contrast | 89 | 93.02 | 100 | 89 |
179
- | contrast.ts | 89 | 93.02 | 100 | 89 |
180
- | data | 85.99 | 83.73 | 100 | 85.99 |
181
- | data.ts | 85.99 | 83.73 | 100 | 85.99 |
274
+ | File | % Stmts | % Branch | % Funcs | % Lines |
275
+ | ----------------- | ------- | -------- | ------- | ------- |
276
+ | **All files** | **94.29** | **85.63** | **92.3** | **94.29** |
277
+ | code | 100 | 100 | 100 | 100 |
278
+ | color | 92.06 | 72.38 | 80.95 | 92.06 |
279
+ | color-harmony | 100 | 96.42 | 100 | 100 |
280
+ | contrast | 89 | 93.02 | 100 | 89 |
281
+ | data | 92.69 | 84.74 | 100 | 92.69 |
282
+ | dominant-colors | 100 | 100 | 100 | 100 |
182
283
 
183
284
  To run coverage tests:
184
285
 
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type { HexModel, RgbModel, HslModel, Channel, ChannelWithAlpha, } from './types/color.types';
1
+ export type { HexModel, RgbModel, HslModel, Channel, ChannelWithAlpha, ImageData, DominantColorResult, DominantColorsOptions, HarmonyType, ColorHarmonyResult, ColorHarmonyOptions, } from './types/color.types';
2
2
  export type { BaseConfiguration, SourceColorConfiguration, ExchangeConfiguration, ExtractOfBaseConfiguration, EasingConfiguration, PresetConfiguration, ShiftConfiguration, ScaleConfiguration, LockedSourceColorsConfiguration, ColorConfiguration, ThemeConfiguration, ExportConfiguration, ColorSpaceConfiguration, VisionSimulationModeConfiguration, TextColorsThemeConfiguration, ViewConfiguration, AlgorithmVersionConfiguration, DatesConfiguration, PublicationConfiguration, CreatorConfiguration, StatusConfiguration, UserConfiguration, MetaConfiguration, FullConfiguration, DocumentConfiguration, ThirdParty, } from './types/configuration.types';
3
3
  export type { PaletteData, LibraryData, PaletteDataThemeItem, PaletteDataColorItem, PaletteDataShadeItem, ExternalPalettes, ColourLovers, } from './types/data.types';
4
4
  export type { ColorFormat } from './types/model.types';
@@ -6,4 +6,6 @@ export { default as Color } from './modules/color/color';
6
6
  export { default as Contrast } from './modules/contrast/contrast';
7
7
  export { default as Data } from './modules/data/data';
8
8
  export { default as Code } from './modules/code/code';
9
+ export { default as DominantColors } from './modules/dominant-colors/dominant-colors';
10
+ export { default as ColorHarmony } from './modules/color-harmony/color-harmony';
9
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,gBAAgB,GACjB,MAAM,kBAAkB,CAAA;AACzB,YAAY,EACV,iBAAiB,EACjB,wBAAwB,EACxB,qBAAqB,EACrB,0BAA0B,EAC1B,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,+BAA+B,EAC/B,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,uBAAuB,EACvB,iCAAiC,EACjC,4BAA4B,EAC5B,iBAAiB,EACjB,6BAA6B,EAC7B,kBAAkB,EAClB,wBAAwB,EACxB,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,qBAAqB,EACrB,UAAU,GACX,MAAM,0BAA0B,CAAA;AACjC,YAAY,EACV,WAAW,EACX,WAAW,EACX,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,gBAAgB,EAChB,YAAY,GACb,MAAM,iBAAiB,CAAA;AACxB,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAEnD,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,4BAA4B,CAAA;AAChE,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,oBAAoB,CAAA;AACpD,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,QAAQ,EACR,QAAQ,EACR,QAAQ,EACR,OAAO,EACP,gBAAgB,EAChB,SAAS,EACT,mBAAmB,EACnB,qBAAqB,EACrB,WAAW,EACX,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,kBAAkB,CAAA;AACzB,YAAY,EACV,iBAAiB,EACjB,wBAAwB,EACxB,qBAAqB,EACrB,0BAA0B,EAC1B,mBAAmB,EACnB,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,+BAA+B,EAC/B,kBAAkB,EAClB,kBAAkB,EAClB,mBAAmB,EACnB,uBAAuB,EACvB,iCAAiC,EACjC,4BAA4B,EAC5B,iBAAiB,EACjB,6BAA6B,EAC7B,kBAAkB,EAClB,wBAAwB,EACxB,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,qBAAqB,EACrB,UAAU,GACX,MAAM,0BAA0B,CAAA;AACjC,YAAY,EACV,WAAW,EACX,WAAW,EACX,oBAAoB,EACpB,oBAAoB,EACpB,oBAAoB,EACpB,gBAAgB,EAChB,YAAY,GACb,MAAM,iBAAiB,CAAA;AACxB,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAEnD,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAA;AACvD,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,4BAA4B,CAAA;AAChE,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,oBAAoB,CAAA;AACpD,OAAO,EAAE,OAAO,IAAI,IAAI,EAAE,MAAM,oBAAoB,CAAA;AACpD,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,0CAA0C,CAAA;AACpF,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,sCAAsC,CAAA"}
package/dist/index.js CHANGED
@@ -1,11 +1,15 @@
1
- import { C as r } from "./color-1CXTuV84.js";
1
+ import { C as a } from "./color-1CXTuV84.js";
2
2
  import { default as e } from "./modules/contrast/contrast.js";
3
- import { default as s } from "./modules/data/data.js";
4
- import { default as l } from "./modules/code/code.js";
3
+ import { default as l } from "./modules/data/data.js";
4
+ import { default as s } from "./modules/code/code.js";
5
+ import { default as p } from "./modules/dominant-colors/dominant-colors.js";
6
+ import { default as C } from "./modules/color-harmony/color-harmony.js";
5
7
  export {
6
- l as Code,
7
- r as Color,
8
+ s as Code,
9
+ a as Color,
10
+ C as ColorHarmony,
8
11
  e as Contrast,
9
- s as Data
12
+ l as Data,
13
+ p as DominantColors
10
14
  };
11
15
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;"}
@@ -0,0 +1,25 @@
1
+ import { Channel, HarmonyType, ColorHarmonyResult, ColorHarmonyOptions } from '../../types/color.types';
2
+
3
+ export default class ColorHarmony {
4
+ private baseColor;
5
+ private analogousSpread;
6
+ private returnFormat;
7
+ constructor({ baseColor, analogousSpread, returnFormat, }: {
8
+ baseColor?: Channel;
9
+ } & ColorHarmonyOptions);
10
+ generateAnalogous: () => ColorHarmonyResult;
11
+ generateComplementary: () => ColorHarmonyResult;
12
+ generateTriadic: () => ColorHarmonyResult;
13
+ generateTetradic: () => ColorHarmonyResult;
14
+ generateSquare: () => ColorHarmonyResult;
15
+ generateHarmony: (type: HarmonyType) => ColorHarmonyResult;
16
+ getAllHarmonies: () => ColorHarmonyResult[];
17
+ private normalizeHue;
18
+ private hueToRgb;
19
+ private formatResult;
20
+ setBaseColor: (color: Channel) => void;
21
+ setAnalogousSpread: (spread: number) => void;
22
+ updateOptions: (options: Partial<ColorHarmonyOptions>) => void;
23
+ getOptions: () => ColorHarmonyOptions;
24
+ }
25
+ //# sourceMappingURL=color-harmony.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color-harmony.d.ts","sourceRoot":"","sources":["../../../src/modules/color-harmony/color-harmony.ts"],"names":[],"mappings":"AACA,OAAO,EACL,OAAO,EAEP,WAAW,EACX,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,kBAAkB,CAAA;AAEzB,MAAM,CAAC,OAAO,OAAO,YAAY;IAC/B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,eAAe,CAAQ;IAC/B,OAAO,CAAC,YAAY,CAAwB;gBAEhC,EACV,SAAuB,EACvB,eAAoB,EACpB,YAAqB,GACtB,EAAE;QACD,SAAS,CAAC,EAAE,OAAO,CAAA;KACpB,GAAG,mBAAmB;IAMvB,iBAAiB,QAAO,kBAAkB,CAmBzC;IAED,qBAAqB,QAAO,kBAAkB,CAU7C;IAED,eAAe,QAAO,kBAAkB,CAWvC;IAED,gBAAgB,QAAO,kBAAkB,CAYxC;IAED,cAAc,QAAO,kBAAkB,CAYtC;IAED,eAAe,SAAU,WAAW,KAAG,kBAAkB,CAexD;IAED,eAAe,QAAO,kBAAkB,EAAE,CAQzC;IAED,OAAO,CAAC,YAAY,CAInB;IAED,OAAO,CAAC,QAAQ,CAOf;IAED,OAAO,CAAC,YAAY,CA8BnB;IAED,YAAY,UAAW,OAAO,KAAG,IAAI,CAEpC;IAED,kBAAkB,WAAY,MAAM,KAAG,IAAI,CAE1C;IAED,aAAa,YAAa,OAAO,CAAC,mBAAmB,CAAC,KAAG,IAAI,CAO5D;IAED,UAAU,QAAO,mBAAmB,CAGlC;CACH"}
@@ -0,0 +1,136 @@
1
+ var i = Object.defineProperty;
2
+ var u = (n, e, t) => e in n ? i(n, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : n[e] = t;
3
+ var r = (n, e, t) => u(n, typeof e != "symbol" ? e + "" : e, t);
4
+ import { c as o } from "../../index-Beb8qoyd.js";
5
+ class m {
6
+ constructor({
7
+ baseColor: e = [255, 0, 0],
8
+ analogousSpread: t = 30,
9
+ returnFormat: s = "both"
10
+ }) {
11
+ r(this, "baseColor");
12
+ r(this, "analogousSpread");
13
+ r(this, "returnFormat");
14
+ r(this, "generateAnalogous", () => {
15
+ const e = o(this.baseColor).hsl(), t = e[0] || 0, s = [
16
+ this.baseColor,
17
+ this.hueToRgb(
18
+ this.normalizeHue(t - this.analogousSpread),
19
+ e[1],
20
+ e[2]
21
+ ),
22
+ this.hueToRgb(
23
+ this.normalizeHue(t + this.analogousSpread),
24
+ e[1],
25
+ e[2]
26
+ )
27
+ ];
28
+ return this.formatResult("analogous", s);
29
+ });
30
+ r(this, "generateComplementary", () => {
31
+ const e = o(this.baseColor).hsl(), t = e[0] || 0, s = [
32
+ this.baseColor,
33
+ this.hueToRgb(this.normalizeHue(t + 180), e[1], e[2])
34
+ ];
35
+ return this.formatResult("complementary", s);
36
+ });
37
+ r(this, "generateTriadic", () => {
38
+ const e = o(this.baseColor).hsl(), t = e[0] || 0, s = [
39
+ this.baseColor,
40
+ this.hueToRgb(this.normalizeHue(t + 120), e[1], e[2]),
41
+ this.hueToRgb(this.normalizeHue(t + 240), e[1], e[2])
42
+ ];
43
+ return this.formatResult("triadic", s);
44
+ });
45
+ r(this, "generateTetradic", () => {
46
+ const e = o(this.baseColor).hsl(), t = e[0] || 0, s = [
47
+ this.baseColor,
48
+ this.hueToRgb(this.normalizeHue(t + 90), e[1], e[2]),
49
+ this.hueToRgb(this.normalizeHue(t + 180), e[1], e[2]),
50
+ this.hueToRgb(this.normalizeHue(t + 270), e[1], e[2])
51
+ ];
52
+ return this.formatResult("tetradic", s);
53
+ });
54
+ r(this, "generateSquare", () => {
55
+ const e = o(this.baseColor).hsl(), t = e[0] || 0, s = [
56
+ this.baseColor,
57
+ this.hueToRgb(this.normalizeHue(t + 90), e[1], e[2]),
58
+ this.hueToRgb(this.normalizeHue(t + 180), e[1], e[2]),
59
+ this.hueToRgb(this.normalizeHue(t + 270), e[1], e[2])
60
+ ];
61
+ return this.formatResult("square", s);
62
+ });
63
+ r(this, "generateHarmony", (e) => {
64
+ switch (e) {
65
+ case "analogous":
66
+ return this.generateAnalogous();
67
+ case "complementary":
68
+ return this.generateComplementary();
69
+ case "triadic":
70
+ return this.generateTriadic();
71
+ case "tetradic":
72
+ return this.generateTetradic();
73
+ case "square":
74
+ return this.generateSquare();
75
+ default:
76
+ throw new Error(`Unknown harmony type: ${e}`);
77
+ }
78
+ });
79
+ r(this, "getAllHarmonies", () => [
80
+ this.generateAnalogous(),
81
+ this.generateComplementary(),
82
+ this.generateTriadic(),
83
+ this.generateTetradic(),
84
+ this.generateSquare()
85
+ ]);
86
+ r(this, "normalizeHue", (e) => {
87
+ for (; e < 0; ) e += 360;
88
+ for (; e >= 360; ) e -= 360;
89
+ return e;
90
+ });
91
+ r(this, "hueToRgb", (e, t, s) => {
92
+ const h = o.hsl(e, t, s).rgb();
93
+ return [Math.round(h[0]), Math.round(h[1]), Math.round(h[2])];
94
+ });
95
+ r(this, "formatResult", (e, t) => {
96
+ const s = t.map(
97
+ (a) => [
98
+ Math.round(a[0]),
99
+ Math.round(a[1]),
100
+ Math.round(a[2])
101
+ ]
102
+ ), h = s.map(
103
+ (a) => o.rgb(a[0], a[1], a[2]).hex()
104
+ );
105
+ return {
106
+ type: e,
107
+ baseColor: [
108
+ Math.round(this.baseColor[0]),
109
+ Math.round(this.baseColor[1]),
110
+ Math.round(this.baseColor[2])
111
+ ],
112
+ baseHex: o.rgb(this.baseColor[0], this.baseColor[1], this.baseColor[2]).hex(),
113
+ colors: s,
114
+ hexColors: h
115
+ };
116
+ });
117
+ r(this, "setBaseColor", (e) => {
118
+ this.baseColor = e;
119
+ });
120
+ r(this, "setAnalogousSpread", (e) => {
121
+ this.analogousSpread = Math.max(1, Math.min(e, 180));
122
+ });
123
+ r(this, "updateOptions", (e) => {
124
+ e.analogousSpread !== void 0 && this.setAnalogousSpread(e.analogousSpread), e.returnFormat !== void 0 && (this.returnFormat = e.returnFormat);
125
+ });
126
+ r(this, "getOptions", () => ({
127
+ analogousSpread: this.analogousSpread,
128
+ returnFormat: this.returnFormat
129
+ }));
130
+ this.baseColor = e, this.analogousSpread = t, this.returnFormat = s;
131
+ }
132
+ }
133
+ export {
134
+ m as default
135
+ };
136
+ //# sourceMappingURL=color-harmony.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color-harmony.js","sources":["../../../src/modules/color-harmony/color-harmony.ts"],"sourcesContent":["import chroma from 'chroma-js'\nimport {\n Channel,\n HexModel,\n HarmonyType,\n ColorHarmonyResult,\n ColorHarmonyOptions,\n} from '@tps/color.types'\n\nexport default class ColorHarmony {\n private baseColor: Channel\n private analogousSpread: number\n private returnFormat: 'rgb' | 'hex' | 'both'\n\n constructor({\n baseColor = [255, 0, 0],\n analogousSpread = 30,\n returnFormat = 'both',\n }: {\n baseColor?: Channel\n } & ColorHarmonyOptions) {\n this.baseColor = baseColor\n this.analogousSpread = analogousSpread\n this.returnFormat = returnFormat\n }\n\n generateAnalogous = (): ColorHarmonyResult => {\n const hsl = chroma(this.baseColor).hsl()\n const baseHue = hsl[0] || 0\n\n const colors = [\n this.baseColor,\n this.hueToRgb(\n this.normalizeHue(baseHue - this.analogousSpread),\n hsl[1],\n hsl[2]\n ),\n this.hueToRgb(\n this.normalizeHue(baseHue + this.analogousSpread),\n hsl[1],\n hsl[2]\n ),\n ]\n\n return this.formatResult('analogous', colors)\n }\n\n generateComplementary = (): ColorHarmonyResult => {\n const hsl = chroma(this.baseColor).hsl()\n const baseHue = hsl[0] || 0\n\n const colors = [\n this.baseColor,\n this.hueToRgb(this.normalizeHue(baseHue + 180), hsl[1], hsl[2]),\n ]\n\n return this.formatResult('complementary', colors)\n }\n\n generateTriadic = (): ColorHarmonyResult => {\n const hsl = chroma(this.baseColor).hsl()\n const baseHue = hsl[0] || 0\n\n const colors = [\n this.baseColor,\n this.hueToRgb(this.normalizeHue(baseHue + 120), hsl[1], hsl[2]),\n this.hueToRgb(this.normalizeHue(baseHue + 240), hsl[1], hsl[2]),\n ]\n\n return this.formatResult('triadic', colors)\n }\n\n generateTetradic = (): ColorHarmonyResult => {\n const hsl = chroma(this.baseColor).hsl()\n const baseHue = hsl[0] || 0\n\n const colors = [\n this.baseColor,\n this.hueToRgb(this.normalizeHue(baseHue + 90), hsl[1], hsl[2]),\n this.hueToRgb(this.normalizeHue(baseHue + 180), hsl[1], hsl[2]),\n this.hueToRgb(this.normalizeHue(baseHue + 270), hsl[1], hsl[2]),\n ]\n\n return this.formatResult('tetradic', colors)\n }\n\n generateSquare = (): ColorHarmonyResult => {\n const hsl = chroma(this.baseColor).hsl()\n const baseHue = hsl[0] || 0\n\n const colors = [\n this.baseColor,\n this.hueToRgb(this.normalizeHue(baseHue + 90), hsl[1], hsl[2]),\n this.hueToRgb(this.normalizeHue(baseHue + 180), hsl[1], hsl[2]),\n this.hueToRgb(this.normalizeHue(baseHue + 270), hsl[1], hsl[2]),\n ]\n\n return this.formatResult('square', colors)\n }\n\n generateHarmony = (type: HarmonyType): ColorHarmonyResult => {\n switch (type) {\n case 'analogous':\n return this.generateAnalogous()\n case 'complementary':\n return this.generateComplementary()\n case 'triadic':\n return this.generateTriadic()\n case 'tetradic':\n return this.generateTetradic()\n case 'square':\n return this.generateSquare()\n default:\n throw new Error(`Unknown harmony type: ${type}`)\n }\n }\n\n getAllHarmonies = (): ColorHarmonyResult[] => {\n return [\n this.generateAnalogous(),\n this.generateComplementary(),\n this.generateTriadic(),\n this.generateTetradic(),\n this.generateSquare(),\n ]\n }\n\n private normalizeHue = (hue: number): number => {\n while (hue < 0) hue += 360\n while (hue >= 360) hue -= 360\n return hue\n }\n\n private hueToRgb = (\n hue: number,\n saturation: number,\n lightness: number\n ): Channel => {\n const rgb = chroma.hsl(hue, saturation, lightness).rgb()\n return [Math.round(rgb[0]), Math.round(rgb[1]), Math.round(rgb[2])]\n }\n\n private formatResult = (\n type: HarmonyType,\n colors: Channel[]\n ): ColorHarmonyResult => {\n const cleanColors = colors.map(\n (color) =>\n [\n Math.round(color[0]),\n Math.round(color[1]),\n Math.round(color[2]),\n ] as Channel\n )\n\n const hexColors = cleanColors.map(\n (color) => chroma.rgb(color[0], color[1], color[2]).hex() as HexModel\n )\n\n return {\n type,\n baseColor: [\n Math.round(this.baseColor[0]),\n Math.round(this.baseColor[1]),\n Math.round(this.baseColor[2]),\n ],\n baseHex: chroma\n .rgb(this.baseColor[0], this.baseColor[1], this.baseColor[2])\n .hex() as HexModel,\n colors: cleanColors,\n hexColors,\n }\n }\n\n setBaseColor = (color: Channel): void => {\n this.baseColor = color\n }\n\n setAnalogousSpread = (spread: number): void => {\n this.analogousSpread = Math.max(1, Math.min(spread, 180))\n }\n\n updateOptions = (options: Partial<ColorHarmonyOptions>): void => {\n if (options.analogousSpread !== undefined) {\n this.setAnalogousSpread(options.analogousSpread)\n }\n if (options.returnFormat !== undefined) {\n this.returnFormat = options.returnFormat\n }\n }\n\n getOptions = (): ColorHarmonyOptions => ({\n analogousSpread: this.analogousSpread,\n returnFormat: this.returnFormat,\n })\n}\n"],"names":["ColorHarmony","baseColor","analogousSpread","returnFormat","__publicField","hsl","chroma","baseHue","colors","type","hue","saturation","lightness","rgb","cleanColors","color","hexColors","spread","options"],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { ImageData, DominantColorResult, DominantColorsOptions } from '../../types/color.types';
2
+
3
+ export default class DominantColors {
4
+ private imageData;
5
+ private colorCount;
6
+ private maxIterations;
7
+ private tolerance;
8
+ private skipTransparent;
9
+ constructor({ imageData, colorCount, maxIterations, tolerance, skipTransparent, }: {
10
+ imageData: ImageData;
11
+ } & DominantColorsOptions);
12
+ extractDominantColors: () => DominantColorResult[];
13
+ private extractPixels;
14
+ private performKMeans;
15
+ private initializeCentroids;
16
+ private findClosestCentroid;
17
+ private calculateDistance;
18
+ private calculateCentroid;
19
+ private hasConverged;
20
+ private calculateColorFrequencies;
21
+ setColorCount: (count: number) => void;
22
+ updateOptions: (options: Partial<DominantColorsOptions>) => void;
23
+ getOptions: () => DominantColorsOptions;
24
+ }
25
+ //# sourceMappingURL=dominant-colors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dominant-colors.d.ts","sourceRoot":"","sources":["../../../src/modules/dominant-colors/dominant-colors.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,SAAS,EACT,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,kBAAkB,CAAA;AAEzB,MAAM,CAAC,OAAO,OAAO,cAAc;IACjC,OAAO,CAAC,SAAS,CAAW;IAC5B,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,eAAe,CAAS;gBAEpB,EACV,SAAS,EACT,UAAc,EACd,aAAkB,EAClB,SAAgB,EAChB,eAAsB,GACvB,EAAE;QACD,SAAS,EAAE,SAAS,CAAA;KACrB,GAAG,qBAAqB;IAQzB,qBAAqB,QAAO,mBAAmB,EAAE,CAQhD;IAED,OAAO,CAAC,aAAa,CAgBpB;IAED,OAAO,CAAC,aAAa,CA+BpB;IAED,OAAO,CAAC,mBAAmB,CA8B1B;IAED,OAAO,CAAC,mBAAmB,CAgB1B;IAED,OAAO,CAAC,iBAAiB,CAOxB;IAED,OAAO,CAAC,iBAAiB,CAWxB;IAED,OAAO,CAAC,YAAY,CAUnB;IAED,OAAO,CAAC,yBAAyB,CAmBhC;IAED,aAAa,UAAW,MAAM,KAAG,IAAI,CAEpC;IAED,aAAa,YAAa,OAAO,CAAC,qBAAqB,CAAC,KAAG,IAAI,CAO9D;IAED,UAAU,QAAO,qBAAqB,CAKpC;CACH"}
@@ -0,0 +1,125 @@
1
+ var d = Object.defineProperty;
2
+ var m = (l, t, e) => t in l ? d(l, t, { enumerable: !0, configurable: !0, writable: !0, value: e }) : l[t] = e;
3
+ var o = (l, t, e) => m(l, typeof t != "symbol" ? t + "" : t, e);
4
+ import { c as f } from "../../index-Beb8qoyd.js";
5
+ class p {
6
+ constructor({
7
+ imageData: t,
8
+ colorCount: e = 5,
9
+ maxIterations: n = 50,
10
+ tolerance: a = 0.01,
11
+ skipTransparent: r = !0
12
+ }) {
13
+ o(this, "imageData");
14
+ o(this, "colorCount");
15
+ o(this, "maxIterations");
16
+ o(this, "tolerance");
17
+ o(this, "skipTransparent");
18
+ o(this, "extractDominantColors", () => {
19
+ const t = this.extractPixels();
20
+ if (t.length === 0) return [];
21
+ const e = this.performKMeans(t);
22
+ return this.calculateColorFrequencies(t, e).sort((a, r) => r.percentage - a.percentage);
23
+ });
24
+ o(this, "extractPixels", () => {
25
+ const t = [], e = this.imageData.data;
26
+ for (let n = 0; n < e.length; n += 4) {
27
+ const a = e[n], r = e[n + 1], s = e[n + 2], i = e[n + 3];
28
+ this.skipTransparent && i < 128 || t.push([a, r, s]);
29
+ }
30
+ return t;
31
+ });
32
+ o(this, "performKMeans", (t) => {
33
+ let e = this.initializeCentroids(t), n = [], a = 0;
34
+ for (; a < this.maxIterations && !this.hasConverged(e, n); ) {
35
+ n = e.map((s) => [...s]);
36
+ const r = Array.from(
37
+ { length: this.colorCount },
38
+ () => []
39
+ );
40
+ t.forEach((s) => {
41
+ const i = this.findClosestCentroid(s, e);
42
+ r[i].push(s);
43
+ }), e = r.map((s) => s.length === 0 ? n[e.indexOf(e[0])] : this.calculateCentroid(s)), a++;
44
+ }
45
+ return e;
46
+ });
47
+ o(this, "initializeCentroids", (t) => {
48
+ const e = [];
49
+ e.push(t[Math.floor(Math.random() * t.length)]);
50
+ for (let n = 1; n < this.colorCount; n++) {
51
+ const a = t.map((c) => {
52
+ const h = Math.min(
53
+ ...e.map(
54
+ (u) => this.calculateDistance(c, u)
55
+ )
56
+ );
57
+ return h * h;
58
+ }), r = a.reduce((c, h) => c + h, 0), s = Math.random() * r;
59
+ let i = 0;
60
+ for (let c = 0; c < t.length; c++)
61
+ if (i += a[c], i >= s) {
62
+ e.push(t[c]);
63
+ break;
64
+ }
65
+ }
66
+ return e;
67
+ });
68
+ o(this, "findClosestCentroid", (t, e) => {
69
+ let n = 1 / 0, a = 0;
70
+ return e.forEach((r, s) => {
71
+ const i = this.calculateDistance(t, r);
72
+ i < n && (n = i, a = s);
73
+ }), a;
74
+ });
75
+ o(this, "calculateDistance", (t, e) => {
76
+ const [n, a, r] = t, [s, i, c] = e;
77
+ return Math.sqrt(
78
+ Math.pow(n - s, 2) + Math.pow(a - i, 2) + Math.pow(r - c, 2)
79
+ );
80
+ });
81
+ o(this, "calculateCentroid", (t) => {
82
+ const e = t.reduce(
83
+ (n, a) => [n[0] + a[0], n[1] + a[1], n[2] + a[2]],
84
+ [0, 0, 0]
85
+ );
86
+ return [
87
+ Math.round(e[0] / t.length),
88
+ Math.round(e[1] / t.length),
89
+ Math.round(e[2] / t.length)
90
+ ];
91
+ });
92
+ o(this, "hasConverged", (t, e) => e.length === 0 ? !1 : t.every((n, a) => this.calculateDistance(n, e[a]) < this.tolerance));
93
+ o(this, "calculateColorFrequencies", (t, e) => {
94
+ const n = new Array(e.length).fill(0);
95
+ t.forEach((r) => {
96
+ const s = this.findClosestCentroid(r, e);
97
+ n[s]++;
98
+ });
99
+ const a = t.length;
100
+ return e.map((r, s) => ({
101
+ color: r,
102
+ hex: f.rgb(r[0], r[1], r[2]).hex(),
103
+ count: n[s],
104
+ percentage: parseFloat((n[s] / a * 100).toFixed(2))
105
+ }));
106
+ });
107
+ o(this, "setColorCount", (t) => {
108
+ this.colorCount = Math.max(1, Math.min(t, 20));
109
+ });
110
+ o(this, "updateOptions", (t) => {
111
+ t.colorCount !== void 0 && this.setColorCount(t.colorCount), t.maxIterations !== void 0 && (this.maxIterations = t.maxIterations), t.tolerance !== void 0 && (this.tolerance = t.tolerance), t.skipTransparent !== void 0 && (this.skipTransparent = t.skipTransparent);
112
+ });
113
+ o(this, "getOptions", () => ({
114
+ colorCount: this.colorCount,
115
+ maxIterations: this.maxIterations,
116
+ tolerance: this.tolerance,
117
+ skipTransparent: this.skipTransparent
118
+ }));
119
+ this.imageData = t, this.colorCount = e, this.maxIterations = n, this.tolerance = a, this.skipTransparent = r;
120
+ }
121
+ }
122
+ export {
123
+ p as default
124
+ };
125
+ //# sourceMappingURL=dominant-colors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dominant-colors.js","sources":["../../../src/modules/dominant-colors/dominant-colors.ts"],"sourcesContent":["import chroma from 'chroma-js'\nimport {\n Channel,\n HexModel,\n ImageData,\n DominantColorResult,\n DominantColorsOptions,\n} from '@tps/color.types'\n\nexport default class DominantColors {\n private imageData: ImageData\n private colorCount: number\n private maxIterations: number\n private tolerance: number\n private skipTransparent: boolean\n\n constructor({\n imageData,\n colorCount = 5,\n maxIterations = 50,\n tolerance = 0.01,\n skipTransparent = true,\n }: {\n imageData: ImageData\n } & DominantColorsOptions) {\n this.imageData = imageData\n this.colorCount = colorCount\n this.maxIterations = maxIterations\n this.tolerance = tolerance\n this.skipTransparent = skipTransparent\n }\n\n extractDominantColors = (): DominantColorResult[] => {\n const pixels = this.extractPixels()\n if (pixels.length === 0) return []\n\n const clusters = this.performKMeans(pixels)\n const results = this.calculateColorFrequencies(pixels, clusters)\n\n return results.sort((a, b) => b.percentage - a.percentage)\n }\n\n private extractPixels = (): Channel[] => {\n const pixels: Channel[] = []\n const data = this.imageData.data\n\n for (let i = 0; i < data.length; i += 4) {\n const r = data[i]\n const g = data[i + 1]\n const b = data[i + 2]\n const a = data[i + 3]\n\n if (this.skipTransparent && a < 128) continue\n\n pixels.push([r, g, b])\n }\n\n return pixels\n }\n\n private performKMeans = (pixels: Channel[]): Channel[] => {\n let centroids = this.initializeCentroids(pixels)\n let prevCentroids: Channel[] = []\n let iteration = 0\n\n while (\n iteration < this.maxIterations &&\n !this.hasConverged(centroids, prevCentroids)\n ) {\n prevCentroids = centroids.map((c) => [...c] as Channel)\n\n const clusters: Channel[][] = Array.from(\n { length: this.colorCount },\n () => []\n )\n\n pixels.forEach((pixel) => {\n const closestIndex = this.findClosestCentroid(pixel, centroids)\n clusters[closestIndex].push(pixel)\n })\n\n centroids = clusters.map((cluster) => {\n if (cluster.length === 0)\n return prevCentroids[centroids.indexOf(centroids[0])]\n return this.calculateCentroid(cluster)\n })\n\n iteration++\n }\n\n return centroids\n }\n\n private initializeCentroids = (pixels: Channel[]): Channel[] => {\n const centroids: Channel[] = []\n\n // K-means++ initialization\n centroids.push(pixels[Math.floor(Math.random() * pixels.length)])\n\n for (let i = 1; i < this.colorCount; i++) {\n const distances = pixels.map((pixel) => {\n const minDistance = Math.min(\n ...centroids.map((centroid) =>\n this.calculateDistance(pixel, centroid)\n )\n )\n return minDistance * minDistance\n })\n\n const totalDistance = distances.reduce((sum, d) => sum + d, 0)\n const threshold = Math.random() * totalDistance\n\n let cumulative = 0\n for (let j = 0; j < pixels.length; j++) {\n cumulative += distances[j]\n if (cumulative >= threshold) {\n centroids.push(pixels[j])\n break\n }\n }\n }\n\n return centroids\n }\n\n private findClosestCentroid = (\n pixel: Channel,\n centroids: Channel[]\n ): number => {\n let minDistance = Infinity\n let closestIndex = 0\n\n centroids.forEach((centroid, index) => {\n const distance = this.calculateDistance(pixel, centroid)\n if (distance < minDistance) {\n minDistance = distance\n closestIndex = index\n }\n })\n\n return closestIndex\n }\n\n private calculateDistance = (color1: Channel, color2: Channel): number => {\n const [r1, g1, b1] = color1\n const [r2, g2, b2] = color2\n\n return Math.sqrt(\n Math.pow(r1 - r2, 2) + Math.pow(g1 - g2, 2) + Math.pow(b1 - b2, 2)\n )\n }\n\n private calculateCentroid = (cluster: Channel[]): Channel => {\n const sum = cluster.reduce(\n (acc, pixel) => [acc[0] + pixel[0], acc[1] + pixel[1], acc[2] + pixel[2]],\n [0, 0, 0] as Channel\n )\n\n return [\n Math.round(sum[0] / cluster.length),\n Math.round(sum[1] / cluster.length),\n Math.round(sum[2] / cluster.length),\n ]\n }\n\n private hasConverged = (\n centroids: Channel[],\n prevCentroids: Channel[]\n ): boolean => {\n if (prevCentroids.length === 0) return false\n\n return centroids.every((centroid, index) => {\n const distance = this.calculateDistance(centroid, prevCentroids[index])\n return distance < this.tolerance\n })\n }\n\n private calculateColorFrequencies = (\n pixels: Channel[],\n centroids: Channel[]\n ): DominantColorResult[] => {\n const counts = new Array(centroids.length).fill(0)\n\n pixels.forEach((pixel) => {\n const closestIndex = this.findClosestCentroid(pixel, centroids)\n counts[closestIndex]++\n })\n\n const totalPixels = pixels.length\n\n return centroids.map((centroid, index) => ({\n color: centroid,\n hex: chroma.rgb(centroid[0], centroid[1], centroid[2]).hex() as HexModel,\n count: counts[index],\n percentage: parseFloat(((counts[index] / totalPixels) * 100).toFixed(2)),\n }))\n }\n\n setColorCount = (count: number): void => {\n this.colorCount = Math.max(1, Math.min(count, 20))\n }\n\n updateOptions = (options: Partial<DominantColorsOptions>): void => {\n if (options.colorCount !== undefined) this.setColorCount(options.colorCount)\n if (options.maxIterations !== undefined)\n this.maxIterations = options.maxIterations\n if (options.tolerance !== undefined) this.tolerance = options.tolerance\n if (options.skipTransparent !== undefined)\n this.skipTransparent = options.skipTransparent\n }\n\n getOptions = (): DominantColorsOptions => ({\n colorCount: this.colorCount,\n maxIterations: this.maxIterations,\n tolerance: this.tolerance,\n skipTransparent: this.skipTransparent,\n })\n}\n"],"names":["DominantColors","imageData","colorCount","maxIterations","tolerance","skipTransparent","__publicField","pixels","clusters","b","data","i","r","g","a","centroids","prevCentroids","iteration","c","pixel","closestIndex","cluster","distances","minDistance","centroid","totalDistance","sum","d","threshold","cumulative","j","index","distance","color1","color2","r1","g1","b1","r2","g2","b2","acc","counts","totalPixels","chroma","count","options"],"mappings":"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"}
@@ -13,4 +13,33 @@ export interface HslModel {
13
13
  }
14
14
  export type Channel = [number, number, number];
15
15
  export type ChannelWithAlpha = [number, number, number, number];
16
+ export interface ImageData {
17
+ data: Uint8ClampedArray;
18
+ width: number;
19
+ height: number;
20
+ }
21
+ export interface DominantColorResult {
22
+ color: Channel;
23
+ hex: HexModel;
24
+ percentage: number;
25
+ count: number;
26
+ }
27
+ export interface DominantColorsOptions {
28
+ colorCount?: number;
29
+ maxIterations?: number;
30
+ tolerance?: number;
31
+ skipTransparent?: boolean;
32
+ }
33
+ export type HarmonyType = 'analogous' | 'complementary' | 'triadic' | 'tetradic' | 'square';
34
+ export interface ColorHarmonyResult {
35
+ type: HarmonyType;
36
+ baseColor: Channel;
37
+ baseHex: HexModel;
38
+ colors: Channel[];
39
+ hexColors: HexModel[];
40
+ }
41
+ export interface ColorHarmonyOptions {
42
+ analogousSpread?: number;
43
+ returnFormat?: 'rgb' | 'hex' | 'both';
44
+ }
16
45
  //# sourceMappingURL=color.types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"color.types.d.ts","sourceRoot":"","sources":["../../src/types/color.types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,IAAI,MAAM,EAAE,GAAG,MAAM,CAAA;AAE5C,MAAM,WAAW,QAAQ;IACvB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,CAAC,EAAE,MAAM,CAAA;CACX;AAED,MAAM,WAAW,QAAQ;IACvB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACV;AAED,MAAM,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAC9C,MAAM,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA"}
1
+ {"version":3,"file":"color.types.d.ts","sourceRoot":"","sources":["../../src/types/color.types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,IAAI,MAAM,EAAE,GAAG,MAAM,CAAA;AAE5C,MAAM,WAAW,QAAQ;IACvB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,CAAC,EAAE,MAAM,CAAA;CACX;AAED,MAAM,WAAW,QAAQ;IACvB,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,MAAM,CAAA;CACV;AAED,MAAM,MAAM,OAAO,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAC9C,MAAM,MAAM,gBAAgB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAA;AAE/D,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,iBAAiB,CAAA;IACvB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,OAAO,CAAA;IACd,GAAG,EAAE,QAAQ,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B;AAED,MAAM,MAAM,WAAW,GACnB,WAAW,GACX,eAAe,GACf,SAAS,GACT,UAAU,GACV,QAAQ,CAAA;AAEZ,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,WAAW,CAAA;IACjB,SAAS,EAAE,OAAO,CAAA;IAClB,OAAO,EAAE,QAAQ,CAAA;IACjB,MAAM,EAAE,OAAO,EAAE,CAAA;IACjB,SAAS,EAAE,QAAQ,EAAE,CAAA;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,YAAY,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,CAAA;CACtC"}
package/package.json CHANGED
@@ -1,12 +1,9 @@
1
1
  {
2
2
  "name": "@a_ng_d/utils-ui-color-palette",
3
3
  "private": false,
4
- "version": "1.6.4",
4
+ "version": "1.7.0",
5
5
  "description": "Core utilities library for UI Color Palette - a color management plugin for design tools. This library provides the foundational color manipulation, contrast calculation, and palette generation features used in the plugin.",
6
- "repository": {
7
- "type": "git",
8
- "url": "git+https://github.com/a-ng-d/utils-ui-color-palette.git"
9
- },
6
+ "repository": "https://github.com/a-ng-d/utils-ui-color-palette",
10
7
  "keywords": [
11
8
  "color",
12
9
  "contrast",
@@ -61,4 +58,4 @@
61
58
  "chroma-js": "^2.4.2",
62
59
  "hsluv": "^1.0.1"
63
60
  }
64
- }
61
+ }