v-auto-color 1.0.4 → 1.0.6

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
@@ -81,6 +81,8 @@ interface ColorConfig {
81
81
  hue?: [number, number]; // 色相范围,默认 [0, 360]
82
82
  saturation?: [number, number]; // 饱和度范围,默认 [70, 100]
83
83
  lightness?: [number, number]; // 亮度范围,默认 [40, 60]
84
+ algorithm?: 'hash' | 'levenshtein' | 'cosine' | 'jaccard'; // 算法类型,默认 'hash'
85
+ similarityThreshold?: number; // 相似度阈值,默认 0.7
84
86
  }
85
87
 
86
88
  // 两种使用方式
@@ -88,6 +90,48 @@ const colorSet1 = useAutoColor('category'); // 使用字符串作为类别
88
90
  const colorSet2 = useAutoColor({ /* 完整配置 */ }); // 使用对象配置
89
91
  ```
90
92
 
93
+ ### 算法说明
94
+
95
+ | 算法 | 描述 | 适用场景 |
96
+ |------|------|----------|
97
+ | `hash` | 基于文本哈希值生成颜色,相同文本返回相同颜色 | 快速生成,适用于文本完全匹配的场景 |
98
+ | `levenshtein` | 基于编辑距离计算相似度,相似文本返回相似颜色 | 适用于拼写相似的文本,如 "hello" 和 "hello world" |
99
+ | `cosine` | 基于字符频率的余弦相似度,相似文本返回相似颜色 | 适用于长度不同但字符分布相似的文本 |
100
+ | `jaccard` | 基于字符集合的Jaccard相似度,相似文本返回相似颜色 | 适用于字符组成相似的文本 |
101
+
102
+ ### 算法配置示例
103
+
104
+ ```typescript
105
+ import { useAutoColor } from 'v-auto-color';
106
+
107
+ // 使用Levenshtein算法(编辑距离)
108
+ const colorSet1 = useAutoColor({
109
+ category: 'set1',
110
+ algorithm: 'levenshtein',
111
+ similarityThreshold: 0.6 // 降低相似度阈值,使更多文本被认为相似
112
+ });
113
+
114
+ // 使用余弦相似度算法
115
+ const colorSet2 = useAutoColor({
116
+ category: 'set2',
117
+ algorithm: 'cosine',
118
+ hue: [0, 180] // 限制色相范围为红色到青色
119
+ });
120
+
121
+ // 使用Jaccard相似度算法
122
+ const colorSet3 = useAutoColor({
123
+ category: 'set3',
124
+ algorithm: 'jaccard',
125
+ saturation: [80, 100], // 提高饱和度,使颜色更鲜艳
126
+ lightness: [50, 70] // 提高亮度,使颜色更明亮
127
+ });
128
+
129
+ // 获取颜色
130
+ const color1 = colorSet1.getColor('hello');
131
+ const color2 = colorSet1.getColor('hello world'); // 与 'hello' 相似,返回相似颜色
132
+ const color3 = colorSet1.getColor('test'); // 与 'hello' 不相似,返回不同颜色
133
+ ```
134
+
91
135
  ## 性能优化
92
136
 
93
137
  ### 编译时预计算
@@ -0,0 +1,142 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+
5
+ // src/core/hash.ts
6
+ function murmurHash3(text) {
7
+ let h1 = 3735928559;
8
+ const c1 = 3432918353;
9
+ const c2 = 461845907;
10
+ const r1 = 15;
11
+ const r2 = 13;
12
+ const m = 5;
13
+ const n = 3864292196;
14
+ let i = 0;
15
+ const length = text.length;
16
+ let k1 = 0;
17
+ while (i < length) {
18
+ const char = text.charCodeAt(i++);
19
+ k1 = k1 << 8 | char;
20
+ }
21
+ k1 = k1 * c1 >>> 0;
22
+ k1 = (k1 << r1 | k1 >>> 32 - r1) >>> 0;
23
+ k1 = k1 * c2 >>> 0;
24
+ h1 ^= k1;
25
+ h1 = (h1 << r2 | h1 >>> 32 - r2) >>> 0;
26
+ h1 = h1 * m + n >>> 0;
27
+ h1 ^= length;
28
+ h1 ^= h1 >>> 16;
29
+ h1 = h1 * 2246822507 >>> 0;
30
+ h1 ^= h1 >>> 13;
31
+ h1 = h1 * 3266489909 >>> 0;
32
+ h1 ^= h1 >>> 16;
33
+ return h1;
34
+ }
35
+ function getTextHash(text) {
36
+ return murmurHash3(text);
37
+ }
38
+
39
+ // src/core/color.ts
40
+ var ColorGenerator = class {
41
+ constructor(config = {}) {
42
+ __publicField(this, "config");
43
+ this.config = {
44
+ category: config.category || "default",
45
+ hue: config.hue || [0, 360],
46
+ saturation: config.saturation || [70, 100],
47
+ lightness: config.lightness || [40, 60]
48
+ };
49
+ }
50
+ // Generate color from hash value
51
+ generateColor(hash) {
52
+ const { hue, saturation, lightness } = this.config;
53
+ const hueRange = hue[1] - hue[0];
54
+ const calculatedHue = hue[0] + hash % hueRange;
55
+ const satRange = saturation[1] - saturation[0];
56
+ const calculatedSat = saturation[0] + (hash >> 8) % satRange;
57
+ const lightRange = lightness[1] - lightness[0];
58
+ const calculatedLight = lightness[0] + (hash >> 16) % lightRange;
59
+ return `hsl(${calculatedHue}, ${calculatedSat}%, ${calculatedLight}%)`;
60
+ }
61
+ // Get color for text (wrapper method)
62
+ getColor(text, hashFn) {
63
+ const hash = hashFn(text);
64
+ return this.generateColor(hash);
65
+ }
66
+ };
67
+
68
+ // src/vite-plugin.ts
69
+ function createFilter(include, exclude) {
70
+ return function(id) {
71
+ const included = include.some((pattern) => {
72
+ if (pattern === "**/*") return true;
73
+ const regex = new RegExp(pattern.replace(/\*/g, ".*"));
74
+ return regex.test(id);
75
+ });
76
+ const excluded = exclude && exclude.some((pattern) => {
77
+ const regex = new RegExp(pattern.replace(/\*/g, ".*"));
78
+ return regex.test(id);
79
+ });
80
+ return included && !excluded;
81
+ };
82
+ }
83
+ function viteAutoColorPlugin() {
84
+ const filter = createFilter(["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.vue"]);
85
+ return {
86
+ name: "v-auto-color",
87
+ // Analyze code during build and inject precomputed colors
88
+ transform(code, id) {
89
+ if (!filter(id)) return null;
90
+ if (!code.includes("useAutoColor")) return null;
91
+ const useAutoColorRegex = /useAutoColor\s*\(\s*(?:(['"])([^'"]+)\1|\{[^}]*\})\s*\)/g;
92
+ const getColorRegex = /\.getColor\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
93
+ let match;
94
+ const colorSets = /* @__PURE__ */ new Set();
95
+ while ((match = useAutoColorRegex.exec(code)) !== null) {
96
+ let category = "default";
97
+ if (match[2]) {
98
+ category = match[2];
99
+ } else {
100
+ const configMatch = match[0].match(/category\s*:\s*['"]([^'"]+)['"]/);
101
+ if (configMatch) {
102
+ category = configMatch[1];
103
+ }
104
+ }
105
+ colorSets.add(category);
106
+ }
107
+ const texts = /* @__PURE__ */ new Set();
108
+ while ((match = getColorRegex.exec(code)) !== null) {
109
+ texts.add(match[1]);
110
+ }
111
+ const colorUsage = {};
112
+ colorSets.forEach((category) => {
113
+ if (!colorUsage[category]) {
114
+ colorUsage[category] = {};
115
+ }
116
+ texts.forEach((text) => {
117
+ const generator = new ColorGenerator({ category });
118
+ const color = generator.getColor(text, getTextHash);
119
+ colorUsage[category][text] = color;
120
+ });
121
+ });
122
+ if (Object.keys(colorUsage).length > 0) {
123
+ const precomputedCode = `
124
+ // Precomputed colors for v-auto-color
125
+ import { __internal__setPrecomputedColors } from 'v-auto-color';
126
+ __internal__setPrecomputedColors(${JSON.stringify(colorUsage, null, 2)});
127
+ `;
128
+ code = precomputedCode + "\n" + code;
129
+ }
130
+ return code;
131
+ }
132
+ };
133
+ }
134
+ var vite_plugin_default = viteAutoColorPlugin;
135
+
136
+ export {
137
+ __publicField,
138
+ getTextHash,
139
+ ColorGenerator,
140
+ viteAutoColorPlugin,
141
+ vite_plugin_default
142
+ };
@@ -0,0 +1,222 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+
5
+ // src/core/algorithms/algorithm.ts
6
+ var DEFAULT_ALGORITHM = {
7
+ type: "hash"
8
+ };
9
+
10
+ // src/core/hash.ts
11
+ function murmurHash3(text) {
12
+ let h1 = 3735928559;
13
+ const c1 = 3432918353;
14
+ const c2 = 461845907;
15
+ const r1 = 15;
16
+ const r2 = 13;
17
+ const m = 5;
18
+ const n = 3864292196;
19
+ let i = 0;
20
+ const length = text.length;
21
+ let k1 = 0;
22
+ while (i < length) {
23
+ const char = text.charCodeAt(i++);
24
+ k1 = k1 << 8 | char;
25
+ }
26
+ k1 = k1 * c1 >>> 0;
27
+ k1 = (k1 << r1 | k1 >>> 32 - r1) >>> 0;
28
+ k1 = k1 * c2 >>> 0;
29
+ h1 ^= k1;
30
+ h1 = (h1 << r2 | h1 >>> 32 - r2) >>> 0;
31
+ h1 = h1 * m + n >>> 0;
32
+ h1 ^= length;
33
+ h1 ^= h1 >>> 16;
34
+ h1 = h1 * 2246822507 >>> 0;
35
+ h1 ^= h1 >>> 13;
36
+ h1 = h1 * 3266489909 >>> 0;
37
+ h1 ^= h1 >>> 16;
38
+ return h1;
39
+ }
40
+ function getTextHash(text) {
41
+ return murmurHash3(text);
42
+ }
43
+
44
+ // src/core/algorithms/hashAlgorithm.ts
45
+ var HashAlgorithm = class {
46
+ // Generate color from text using hash
47
+ generateColor(text, config) {
48
+ const hash = getTextHash(text);
49
+ const hue = config?.hue || [0, 360];
50
+ const saturation = config?.saturation || [70, 100];
51
+ const lightness = config?.lightness || [40, 60];
52
+ const hueRange = hue[1] - hue[0];
53
+ const calculatedHue = hue[0] + hash % hueRange;
54
+ const satRange = saturation[1] - saturation[0];
55
+ const calculatedSat = saturation[0] + (hash >> 8) % satRange;
56
+ const lightRange = lightness[1] - lightness[0];
57
+ const calculatedLight = lightness[0] + (hash >> 16) % lightRange;
58
+ return `hsl(${calculatedHue}, ${calculatedSat}%, ${calculatedLight}%)`;
59
+ }
60
+ // Get algorithm name
61
+ getName() {
62
+ return "hash";
63
+ }
64
+ };
65
+
66
+ // src/core/algorithms/featureAlgorithm.ts
67
+ var FeatureAlgorithm = class {
68
+ // Calculate text features
69
+ calculateFeatures(text) {
70
+ const length = text.length;
71
+ const vowels = "aeiouAEIOU";
72
+ const consonants = "bcdfghjklmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ";
73
+ let vowelCount = 0;
74
+ let consonantCount = 0;
75
+ const uniqueChars = /* @__PURE__ */ new Set();
76
+ let totalCharCode = 0;
77
+ for (const char of text) {
78
+ if (vowels.includes(char)) {
79
+ vowelCount++;
80
+ } else if (consonants.includes(char)) {
81
+ consonantCount++;
82
+ }
83
+ uniqueChars.add(char);
84
+ totalCharCode += char.charCodeAt(0);
85
+ }
86
+ return {
87
+ length,
88
+ vowelRatio: length > 0 ? vowelCount / length : 0,
89
+ consonantRatio: length > 0 ? consonantCount / length : 0,
90
+ uniqueCharRatio: length > 0 ? uniqueChars.size / length : 0,
91
+ avgCharCode: length > 0 ? totalCharCode / length : 0
92
+ };
93
+ }
94
+ // Generate color from text using features
95
+ generateColor(text, config) {
96
+ const features = this.calculateFeatures(text);
97
+ const hue = config?.hue || [0, 360];
98
+ const saturation = config?.saturation || [70, 100];
99
+ const lightness = config?.lightness || [40, 60];
100
+ const hueRange = hue[1] - hue[0];
101
+ const hueFactor = (features.length % 100 + features.uniqueCharRatio * 100 + features.avgCharCode % 100) / 3;
102
+ const calculatedHue = hue[0] + hueFactor / 100 * hueRange;
103
+ const satRange = saturation[1] - saturation[0];
104
+ const calculatedSat = saturation[0] + features.vowelRatio * satRange;
105
+ const lightRange = lightness[1] - lightness[0];
106
+ const calculatedLight = lightness[0] + features.consonantRatio * lightRange;
107
+ return `hsl(${calculatedHue}, ${calculatedSat}%, ${calculatedLight}%)`;
108
+ }
109
+ // Get algorithm name
110
+ getName() {
111
+ return "feature";
112
+ }
113
+ };
114
+
115
+ // src/core/algorithms/algorithmFactory.ts
116
+ var AlgorithmFactory = class {
117
+ // Create algorithm instance based on config
118
+ static createAlgorithm(config = DEFAULT_ALGORITHM) {
119
+ switch (config.type) {
120
+ case "feature":
121
+ return new FeatureAlgorithm();
122
+ case "hash":
123
+ default:
124
+ return new HashAlgorithm();
125
+ }
126
+ }
127
+ };
128
+
129
+ // src/core/color.ts
130
+ var ColorGenerator = class {
131
+ constructor(config = {}) {
132
+ __publicField(this, "config");
133
+ __publicField(this, "algorithm");
134
+ this.config = {
135
+ category: config.category || "default",
136
+ hue: config.hue || [0, 360],
137
+ saturation: config.saturation || [70, 100],
138
+ lightness: config.lightness || [40, 60],
139
+ algorithm: config.algorithm || DEFAULT_ALGORITHM
140
+ };
141
+ this.algorithm = AlgorithmFactory.createAlgorithm(this.config.algorithm);
142
+ }
143
+ // Get color for text
144
+ getColor(text) {
145
+ return this.algorithm.generateColor(text, this.config);
146
+ }
147
+ };
148
+
149
+ // src/vite-plugin.ts
150
+ function createFilter(include, exclude) {
151
+ return function(id) {
152
+ const included = include.some((pattern) => {
153
+ if (pattern === "**/*") return true;
154
+ const regex = new RegExp(pattern.replace(/\*/g, ".*"));
155
+ return regex.test(id);
156
+ });
157
+ const excluded = exclude && exclude.some((pattern) => {
158
+ const regex = new RegExp(pattern.replace(/\*/g, ".*"));
159
+ return regex.test(id);
160
+ });
161
+ return included && !excluded;
162
+ };
163
+ }
164
+ function viteAutoColorPlugin() {
165
+ const filter = createFilter(["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.vue"]);
166
+ return {
167
+ name: "v-auto-color",
168
+ // Analyze code during build and inject precomputed colors
169
+ transform(code, id) {
170
+ if (!filter(id)) return null;
171
+ if (!code.includes("useAutoColor")) return null;
172
+ const useAutoColorRegex = /useAutoColor\s*\(\s*(?:(['"])([^'"]+)\1|\{[^}]*\})\s*\)/g;
173
+ const getColorRegex = /\.getColor\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
174
+ let match;
175
+ const colorSets = /* @__PURE__ */ new Set();
176
+ while ((match = useAutoColorRegex.exec(code)) !== null) {
177
+ let category = "default";
178
+ if (match[2]) {
179
+ category = match[2];
180
+ } else {
181
+ const configMatch = match[0].match(/category\s*:\s*['"]([^'"]+)['"]/);
182
+ if (configMatch) {
183
+ category = configMatch[1];
184
+ }
185
+ }
186
+ colorSets.add(category);
187
+ }
188
+ const texts = /* @__PURE__ */ new Set();
189
+ while ((match = getColorRegex.exec(code)) !== null) {
190
+ texts.add(match[1]);
191
+ }
192
+ const colorUsage = {};
193
+ colorSets.forEach((category) => {
194
+ if (!colorUsage[category]) {
195
+ colorUsage[category] = {};
196
+ }
197
+ texts.forEach((text) => {
198
+ const generator = new ColorGenerator({ category });
199
+ const color = generator.getColor(text);
200
+ colorUsage[category][text] = color;
201
+ });
202
+ });
203
+ if (Object.keys(colorUsage).length > 0) {
204
+ const precomputedCode = `
205
+ // Precomputed colors for v-auto-color
206
+ import { __internal__setPrecomputedColors } from 'v-auto-color';
207
+ __internal__setPrecomputedColors(${JSON.stringify(colorUsage, null, 2)});
208
+ `;
209
+ code = precomputedCode + "\n" + code;
210
+ }
211
+ return code;
212
+ }
213
+ };
214
+ }
215
+ var vite_plugin_default = viteAutoColorPlugin;
216
+
217
+ export {
218
+ __publicField,
219
+ ColorGenerator,
220
+ viteAutoColorPlugin,
221
+ vite_plugin_default
222
+ };
@@ -0,0 +1,144 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+
5
+ // src/core/hash.ts
6
+ function murmurHash3(text) {
7
+ let h1 = 3735928559;
8
+ const c1 = 3432918353;
9
+ const c2 = 461845907;
10
+ const r1 = 15;
11
+ const r2 = 13;
12
+ const m = 5;
13
+ const n = 3864292196;
14
+ let i = 0;
15
+ const length = text.length;
16
+ let k1 = 0;
17
+ while (i < length) {
18
+ const char = text.charCodeAt(i++);
19
+ k1 = k1 << 8 | char;
20
+ }
21
+ k1 = k1 * c1 >>> 0;
22
+ k1 = (k1 << r1 | k1 >>> 32 - r1) >>> 0;
23
+ k1 = k1 * c2 >>> 0;
24
+ h1 ^= k1;
25
+ h1 = (h1 << r2 | h1 >>> 32 - r2) >>> 0;
26
+ h1 = h1 * m + n >>> 0;
27
+ h1 ^= length;
28
+ h1 ^= h1 >>> 16;
29
+ h1 = h1 * 2246822507 >>> 0;
30
+ h1 ^= h1 >>> 13;
31
+ h1 = h1 * 3266489909 >>> 0;
32
+ h1 ^= h1 >>> 16;
33
+ return h1;
34
+ }
35
+ function getTextHash(text) {
36
+ return murmurHash3(text);
37
+ }
38
+
39
+ // src/core/color.ts
40
+ var ColorGenerator = class {
41
+ constructor(config = {}) {
42
+ __publicField(this, "config");
43
+ this.config = {
44
+ category: config.category || "default",
45
+ hue: config.hue || [0, 360],
46
+ saturation: config.saturation || [70, 100],
47
+ lightness: config.lightness || [40, 60],
48
+ algorithm: config.algorithm || "hash",
49
+ similarityThreshold: config.similarityThreshold || 0.7
50
+ };
51
+ }
52
+ // Generate color from hash value
53
+ generateColor(hash) {
54
+ const { hue, saturation, lightness } = this.config;
55
+ const hueRange = hue[1] - hue[0];
56
+ const calculatedHue = hue[0] + hash % hueRange;
57
+ const satRange = saturation[1] - saturation[0];
58
+ const calculatedSat = saturation[0] + (hash >> 8) % satRange;
59
+ const lightRange = lightness[1] - lightness[0];
60
+ const calculatedLight = lightness[0] + (hash >> 16) % lightRange;
61
+ return `hsl(${calculatedHue}, ${calculatedSat}%, ${calculatedLight}%)`;
62
+ }
63
+ // Get color for text (wrapper method)
64
+ getColor(text, hashFn) {
65
+ const hash = hashFn(text);
66
+ return this.generateColor(hash);
67
+ }
68
+ };
69
+
70
+ // src/vite-plugin.ts
71
+ function createFilter(include, exclude) {
72
+ return function(id) {
73
+ const included = include.some((pattern) => {
74
+ if (pattern === "**/*") return true;
75
+ const regex = new RegExp(pattern.replace(/\*/g, ".*"));
76
+ return regex.test(id);
77
+ });
78
+ const excluded = exclude && exclude.some((pattern) => {
79
+ const regex = new RegExp(pattern.replace(/\*/g, ".*"));
80
+ return regex.test(id);
81
+ });
82
+ return included && !excluded;
83
+ };
84
+ }
85
+ function viteAutoColorPlugin() {
86
+ const filter = createFilter(["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.vue"]);
87
+ return {
88
+ name: "v-auto-color",
89
+ // Analyze code during build and inject precomputed colors
90
+ transform(code, id) {
91
+ if (!filter(id)) return null;
92
+ if (!code.includes("useAutoColor")) return null;
93
+ const useAutoColorRegex = /useAutoColor\s*\(\s*(?:(['"])([^'"]+)\1|\{[^}]*\})\s*\)/g;
94
+ const getColorRegex = /\.getColor\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
95
+ let match;
96
+ const colorSets = /* @__PURE__ */ new Set();
97
+ while ((match = useAutoColorRegex.exec(code)) !== null) {
98
+ let category = "default";
99
+ if (match[2]) {
100
+ category = match[2];
101
+ } else {
102
+ const configMatch = match[0].match(/category\s*:\s*['"]([^'"]+)['"]/);
103
+ if (configMatch) {
104
+ category = configMatch[1];
105
+ }
106
+ }
107
+ colorSets.add(category);
108
+ }
109
+ const texts = /* @__PURE__ */ new Set();
110
+ while ((match = getColorRegex.exec(code)) !== null) {
111
+ texts.add(match[1]);
112
+ }
113
+ const colorUsage = {};
114
+ colorSets.forEach((category) => {
115
+ if (!colorUsage[category]) {
116
+ colorUsage[category] = {};
117
+ }
118
+ texts.forEach((text) => {
119
+ const generator = new ColorGenerator({ category });
120
+ const color = generator.getColor(text, getTextHash);
121
+ colorUsage[category][text] = color;
122
+ });
123
+ });
124
+ if (Object.keys(colorUsage).length > 0) {
125
+ const precomputedCode = `
126
+ // Precomputed colors for v-auto-color
127
+ import { __internal__setPrecomputedColors } from 'v-auto-color';
128
+ __internal__setPrecomputedColors(${JSON.stringify(colorUsage, null, 2)});
129
+ `;
130
+ code = precomputedCode + "\n" + code;
131
+ }
132
+ return code;
133
+ }
134
+ };
135
+ }
136
+ var vite_plugin_default = viteAutoColorPlugin;
137
+
138
+ export {
139
+ __publicField,
140
+ getTextHash,
141
+ ColorGenerator,
142
+ viteAutoColorPlugin,
143
+ vite_plugin_default
144
+ };
package/dist/index.d.mts CHANGED
@@ -1,11 +1,14 @@
1
1
  export { default as viteAutoColorPlugin } from './vite-plugin.mjs';
2
2
  import 'vite';
3
3
 
4
+ type AlgorithmType = 'hash' | 'levenshtein' | 'cosine' | 'jaccard';
4
5
  interface ColorConfig {
5
6
  category?: string;
6
7
  hue?: [number, number];
7
8
  saturation?: [number, number];
8
9
  lightness?: [number, number];
10
+ algorithm?: AlgorithmType;
11
+ similarityThreshold?: number;
9
12
  }
10
13
 
11
14
  declare class ColorSet {
package/dist/index.d.ts CHANGED
@@ -1,11 +1,14 @@
1
1
  export { default as viteAutoColorPlugin } from './vite-plugin.js';
2
2
  import 'vite';
3
3
 
4
+ type AlgorithmType = 'hash' | 'levenshtein' | 'cosine' | 'jaccard';
4
5
  interface ColorConfig {
5
6
  category?: string;
6
7
  hue?: [number, number];
7
8
  saturation?: [number, number];
8
9
  lightness?: [number, number];
10
+ algorithm?: AlgorithmType;
11
+ similarityThreshold?: number;
9
12
  }
10
13
 
11
14
  declare class ColorSet {
package/dist/index.js CHANGED
@@ -71,7 +71,9 @@ var ColorGenerator = class {
71
71
  category: config.category || "default",
72
72
  hue: config.hue || [0, 360],
73
73
  saturation: config.saturation || [70, 100],
74
- lightness: config.lightness || [40, 60]
74
+ lightness: config.lightness || [40, 60],
75
+ algorithm: config.algorithm || "hash",
76
+ similarityThreshold: config.similarityThreshold || 0.7
75
77
  };
76
78
  }
77
79
  // Generate color from hash value
@@ -92,10 +94,98 @@ var ColorGenerator = class {
92
94
  }
93
95
  };
94
96
 
97
+ // src/core/similarity.ts
98
+ function levenshteinDistance(str1, str2) {
99
+ const matrix = [];
100
+ for (let i = 0; i <= str2.length; i++) {
101
+ matrix[i] = [i];
102
+ }
103
+ for (let j = 0; j <= str1.length; j++) {
104
+ matrix[0][j] = j;
105
+ }
106
+ for (let i = 1; i <= str2.length; i++) {
107
+ for (let j = 1; j <= str1.length; j++) {
108
+ if (str2.charAt(i - 1) === str1.charAt(j - 1)) {
109
+ matrix[i][j] = matrix[i - 1][j - 1];
110
+ } else {
111
+ matrix[i][j] = Math.min(
112
+ matrix[i - 1][j - 1] + 1,
113
+ // substitution
114
+ matrix[i][j - 1] + 1,
115
+ // insertion
116
+ matrix[i - 1][j] + 1
117
+ // deletion
118
+ );
119
+ }
120
+ }
121
+ }
122
+ const maxLength = Math.max(str1.length, str2.length);
123
+ return maxLength === 0 ? 1 : 1 - matrix[str2.length][str1.length] / maxLength;
124
+ }
125
+ function cosineSimilarity(str1, str2) {
126
+ const freq1 = /* @__PURE__ */ new Map();
127
+ const freq2 = /* @__PURE__ */ new Map();
128
+ const allChars = /* @__PURE__ */ new Set();
129
+ for (const char of str1) {
130
+ freq1.set(char, (freq1.get(char) || 0) + 1);
131
+ allChars.add(char);
132
+ }
133
+ for (const char of str2) {
134
+ freq2.set(char, (freq2.get(char) || 0) + 1);
135
+ allChars.add(char);
136
+ }
137
+ let dotProduct = 0;
138
+ let magnitude1 = 0;
139
+ let magnitude2 = 0;
140
+ for (const char of allChars) {
141
+ const f1 = freq1.get(char) || 0;
142
+ const f2 = freq2.get(char) || 0;
143
+ dotProduct += f1 * f2;
144
+ magnitude1 += f1 * f1;
145
+ magnitude2 += f2 * f2;
146
+ }
147
+ if (magnitude1 === 0 || magnitude2 === 0) {
148
+ return 0;
149
+ }
150
+ return dotProduct / (Math.sqrt(magnitude1) * Math.sqrt(magnitude2));
151
+ }
152
+ function jaccardSimilarity(str1, str2) {
153
+ const set1 = new Set(str1);
154
+ const set2 = new Set(str2);
155
+ const intersection = new Set([...set1].filter((x) => set2.has(x)));
156
+ const union = /* @__PURE__ */ new Set([...set1, ...set2]);
157
+ return union.size === 0 ? 0 : intersection.size / union.size;
158
+ }
159
+ function getSimilarityScore(str1, str2, algorithm) {
160
+ switch (algorithm) {
161
+ case "levenshtein":
162
+ return levenshteinDistance(str1, str2);
163
+ case "cosine":
164
+ return cosineSimilarity(str1, str2);
165
+ case "jaccard":
166
+ return jaccardSimilarity(str1, str2);
167
+ default:
168
+ return 0;
169
+ }
170
+ }
171
+
95
172
  // src/vite-plugin.ts
96
- var import_pluginutils = require("@rollup/pluginutils");
173
+ function createFilter(include, exclude) {
174
+ return function(id) {
175
+ const included = include.some((pattern) => {
176
+ if (pattern === "**/*") return true;
177
+ const regex = new RegExp(pattern.replace(/\*/g, ".*"));
178
+ return regex.test(id);
179
+ });
180
+ const excluded = exclude && exclude.some((pattern) => {
181
+ const regex = new RegExp(pattern.replace(/\*/g, ".*"));
182
+ return regex.test(id);
183
+ });
184
+ return included && !excluded;
185
+ };
186
+ }
97
187
  function viteAutoColorPlugin() {
98
- const filter = (0, import_pluginutils.createFilter)(["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.vue"]);
188
+ const filter = createFilter(["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.vue"]);
99
189
  return {
100
190
  name: "v-auto-color",
101
191
  // Analyze code during build and inject precomputed colors
@@ -166,6 +256,23 @@ var ColorSet = class {
166
256
  if (precomputedColors[this.category] && precomputedColors[this.category][text]) {
167
257
  return precomputedColors[this.category][text];
168
258
  }
259
+ if (this.config.algorithm && this.config.algorithm !== "hash" && precomputedColors[this.category]) {
260
+ const similarityThreshold = this.config.similarityThreshold || 0.7;
261
+ let mostSimilarText = text;
262
+ let highestSimilarity = 0;
263
+ for (const existingText in precomputedColors[this.category]) {
264
+ const similarity = getSimilarityScore(text, existingText, this.config.algorithm);
265
+ if (similarity > highestSimilarity && similarity >= similarityThreshold) {
266
+ highestSimilarity = similarity;
267
+ mostSimilarText = existingText;
268
+ }
269
+ }
270
+ if (mostSimilarText !== text && precomputedColors[this.category][mostSimilarText]) {
271
+ const color2 = precomputedColors[this.category][mostSimilarText];
272
+ precomputedColors[this.category][text] = color2;
273
+ return color2;
274
+ }
275
+ }
169
276
  const color = this.generator.getColor(text, getTextHash);
170
277
  if (!precomputedColors[this.category]) {
171
278
  precomputedColors[this.category] = {};
package/dist/index.mjs CHANGED
@@ -3,7 +3,82 @@ import {
3
3
  __publicField,
4
4
  getTextHash,
5
5
  viteAutoColorPlugin
6
- } from "./chunk-465SVM6G.mjs";
6
+ } from "./chunk-RECQIKGP.mjs";
7
+
8
+ // src/core/similarity.ts
9
+ function levenshteinDistance(str1, str2) {
10
+ const matrix = [];
11
+ for (let i = 0; i <= str2.length; i++) {
12
+ matrix[i] = [i];
13
+ }
14
+ for (let j = 0; j <= str1.length; j++) {
15
+ matrix[0][j] = j;
16
+ }
17
+ for (let i = 1; i <= str2.length; i++) {
18
+ for (let j = 1; j <= str1.length; j++) {
19
+ if (str2.charAt(i - 1) === str1.charAt(j - 1)) {
20
+ matrix[i][j] = matrix[i - 1][j - 1];
21
+ } else {
22
+ matrix[i][j] = Math.min(
23
+ matrix[i - 1][j - 1] + 1,
24
+ // substitution
25
+ matrix[i][j - 1] + 1,
26
+ // insertion
27
+ matrix[i - 1][j] + 1
28
+ // deletion
29
+ );
30
+ }
31
+ }
32
+ }
33
+ const maxLength = Math.max(str1.length, str2.length);
34
+ return maxLength === 0 ? 1 : 1 - matrix[str2.length][str1.length] / maxLength;
35
+ }
36
+ function cosineSimilarity(str1, str2) {
37
+ const freq1 = /* @__PURE__ */ new Map();
38
+ const freq2 = /* @__PURE__ */ new Map();
39
+ const allChars = /* @__PURE__ */ new Set();
40
+ for (const char of str1) {
41
+ freq1.set(char, (freq1.get(char) || 0) + 1);
42
+ allChars.add(char);
43
+ }
44
+ for (const char of str2) {
45
+ freq2.set(char, (freq2.get(char) || 0) + 1);
46
+ allChars.add(char);
47
+ }
48
+ let dotProduct = 0;
49
+ let magnitude1 = 0;
50
+ let magnitude2 = 0;
51
+ for (const char of allChars) {
52
+ const f1 = freq1.get(char) || 0;
53
+ const f2 = freq2.get(char) || 0;
54
+ dotProduct += f1 * f2;
55
+ magnitude1 += f1 * f1;
56
+ magnitude2 += f2 * f2;
57
+ }
58
+ if (magnitude1 === 0 || magnitude2 === 0) {
59
+ return 0;
60
+ }
61
+ return dotProduct / (Math.sqrt(magnitude1) * Math.sqrt(magnitude2));
62
+ }
63
+ function jaccardSimilarity(str1, str2) {
64
+ const set1 = new Set(str1);
65
+ const set2 = new Set(str2);
66
+ const intersection = new Set([...set1].filter((x) => set2.has(x)));
67
+ const union = /* @__PURE__ */ new Set([...set1, ...set2]);
68
+ return union.size === 0 ? 0 : intersection.size / union.size;
69
+ }
70
+ function getSimilarityScore(str1, str2, algorithm) {
71
+ switch (algorithm) {
72
+ case "levenshtein":
73
+ return levenshteinDistance(str1, str2);
74
+ case "cosine":
75
+ return cosineSimilarity(str1, str2);
76
+ case "jaccard":
77
+ return jaccardSimilarity(str1, str2);
78
+ default:
79
+ return 0;
80
+ }
81
+ }
7
82
 
8
83
  // src/index.ts
9
84
  var precomputedColors = {};
@@ -25,6 +100,23 @@ var ColorSet = class {
25
100
  if (precomputedColors[this.category] && precomputedColors[this.category][text]) {
26
101
  return precomputedColors[this.category][text];
27
102
  }
103
+ if (this.config.algorithm && this.config.algorithm !== "hash" && precomputedColors[this.category]) {
104
+ const similarityThreshold = this.config.similarityThreshold || 0.7;
105
+ let mostSimilarText = text;
106
+ let highestSimilarity = 0;
107
+ for (const existingText in precomputedColors[this.category]) {
108
+ const similarity = getSimilarityScore(text, existingText, this.config.algorithm);
109
+ if (similarity > highestSimilarity && similarity >= similarityThreshold) {
110
+ highestSimilarity = similarity;
111
+ mostSimilarText = existingText;
112
+ }
113
+ }
114
+ if (mostSimilarText !== text && precomputedColors[this.category][mostSimilarText]) {
115
+ const color2 = precomputedColors[this.category][mostSimilarText];
116
+ precomputedColors[this.category][text] = color2;
117
+ return color2;
118
+ }
119
+ }
28
120
  const color = this.generator.getColor(text, getTextHash);
29
121
  if (!precomputedColors[this.category]) {
30
122
  precomputedColors[this.category] = {};
@@ -26,7 +26,6 @@ __export(vite_plugin_exports, {
26
26
  viteAutoColorPlugin: () => viteAutoColorPlugin
27
27
  });
28
28
  module.exports = __toCommonJS(vite_plugin_exports);
29
- var import_pluginutils = require("@rollup/pluginutils");
30
29
 
31
30
  // src/core/hash.ts
32
31
  function murmurHash3(text) {
@@ -70,7 +69,9 @@ var ColorGenerator = class {
70
69
  category: config.category || "default",
71
70
  hue: config.hue || [0, 360],
72
71
  saturation: config.saturation || [70, 100],
73
- lightness: config.lightness || [40, 60]
72
+ lightness: config.lightness || [40, 60],
73
+ algorithm: config.algorithm || "hash",
74
+ similarityThreshold: config.similarityThreshold || 0.7
74
75
  };
75
76
  }
76
77
  // Generate color from hash value
@@ -92,8 +93,22 @@ var ColorGenerator = class {
92
93
  };
93
94
 
94
95
  // src/vite-plugin.ts
96
+ function createFilter(include, exclude) {
97
+ return function(id) {
98
+ const included = include.some((pattern) => {
99
+ if (pattern === "**/*") return true;
100
+ const regex = new RegExp(pattern.replace(/\*/g, ".*"));
101
+ return regex.test(id);
102
+ });
103
+ const excluded = exclude && exclude.some((pattern) => {
104
+ const regex = new RegExp(pattern.replace(/\*/g, ".*"));
105
+ return regex.test(id);
106
+ });
107
+ return included && !excluded;
108
+ };
109
+ }
95
110
  function viteAutoColorPlugin() {
96
- const filter = (0, import_pluginutils.createFilter)(["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.vue"]);
111
+ const filter = createFilter(["**/*.ts", "**/*.tsx", "**/*.js", "**/*.jsx", "**/*.vue"]);
97
112
  return {
98
113
  name: "v-auto-color",
99
114
  // Analyze code during build and inject precomputed colors
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  viteAutoColorPlugin,
3
3
  vite_plugin_default
4
- } from "./chunk-465SVM6G.mjs";
4
+ } from "./chunk-RECQIKGP.mjs";
5
5
  export {
6
6
  vite_plugin_default as default,
7
7
  viteAutoColorPlugin
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "v-auto-color",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "Vite plugin for automatic color generation based on text similarity",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -32,7 +32,6 @@
32
32
  "vite": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
33
33
  },
34
34
  "dependencies": {
35
- "@rollup/pluginutils": "^5.0.2"
36
35
  },
37
36
  "devDependencies": {
38
37
  "@types/node": "^18.19.3",
package/src/core/color.ts CHANGED
@@ -1,9 +1,13 @@
1
1
  // Color generation based on hash values
2
+ export type AlgorithmType = 'hash' | 'levenshtein' | 'cosine' | 'jaccard';
3
+
2
4
  export interface ColorConfig {
3
5
  category?: string;
4
6
  hue?: [number, number];
5
7
  saturation?: [number, number];
6
8
  lightness?: [number, number];
9
+ algorithm?: AlgorithmType;
10
+ similarityThreshold?: number; // 相似度阈值,用于相似度算法
7
11
  }
8
12
 
9
13
  export class ColorGenerator {
@@ -14,7 +18,9 @@ export class ColorGenerator {
14
18
  category: config.category || 'default',
15
19
  hue: config.hue || [0, 360],
16
20
  saturation: config.saturation || [70, 100],
17
- lightness: config.lightness || [40, 60]
21
+ lightness: config.lightness || [40, 60],
22
+ algorithm: config.algorithm || 'hash',
23
+ similarityThreshold: config.similarityThreshold || 0.7
18
24
  };
19
25
  }
20
26
 
@@ -0,0 +1,95 @@
1
+ // String similarity algorithms
2
+
3
+ // Levenshtein distance algorithm (edit distance)
4
+ export function levenshteinDistance(str1: string, str2: string): number {
5
+ const matrix: number[][] = [];
6
+
7
+ // Initialize matrix
8
+ for (let i = 0; i <= str2.length; i++) {
9
+ matrix[i] = [i];
10
+ }
11
+ for (let j = 0; j <= str1.length; j++) {
12
+ matrix[0][j] = j;
13
+ }
14
+
15
+ // Calculate Levenshtein distance
16
+ for (let i = 1; i <= str2.length; i++) {
17
+ for (let j = 1; j <= str1.length; j++) {
18
+ if (str2.charAt(i - 1) === str1.charAt(j - 1)) {
19
+ matrix[i][j] = matrix[i - 1][j - 1];
20
+ } else {
21
+ matrix[i][j] = Math.min(
22
+ matrix[i - 1][j - 1] + 1, // substitution
23
+ matrix[i][j - 1] + 1, // insertion
24
+ matrix[i - 1][j] + 1 // deletion
25
+ );
26
+ }
27
+ }
28
+ }
29
+
30
+ // Calculate similarity score (0-1)
31
+ const maxLength = Math.max(str1.length, str2.length);
32
+ return maxLength === 0 ? 1 : 1 - matrix[str2.length][str1.length] / maxLength;
33
+ }
34
+
35
+ // Cosine similarity algorithm
36
+ export function cosineSimilarity(str1: string, str2: string): number {
37
+ // Create character frequency maps
38
+ const freq1 = new Map<string, number>();
39
+ const freq2 = new Map<string, number>();
40
+ const allChars = new Set<string>();
41
+
42
+ // Count frequencies for str1
43
+ for (const char of str1) {
44
+ freq1.set(char, (freq1.get(char) || 0) + 1);
45
+ allChars.add(char);
46
+ }
47
+
48
+ // Count frequencies for str2
49
+ for (const char of str2) {
50
+ freq2.set(char, (freq2.get(char) || 0) + 1);
51
+ allChars.add(char);
52
+ }
53
+
54
+ // Calculate dot product
55
+ let dotProduct = 0;
56
+ let magnitude1 = 0;
57
+ let magnitude2 = 0;
58
+
59
+ for (const char of allChars) {
60
+ const f1 = freq1.get(char) || 0;
61
+ const f2 = freq2.get(char) || 0;
62
+ dotProduct += f1 * f2;
63
+ magnitude1 += f1 * f1;
64
+ magnitude2 += f2 * f2;
65
+ }
66
+
67
+ // Calculate cosine similarity
68
+ if (magnitude1 === 0 || magnitude2 === 0) {
69
+ return 0;
70
+ }
71
+ return dotProduct / (Math.sqrt(magnitude1) * Math.sqrt(magnitude2));
72
+ }
73
+
74
+ // Jaccard similarity algorithm
75
+ export function jaccardSimilarity(str1: string, str2: string): number {
76
+ const set1 = new Set(str1);
77
+ const set2 = new Set(str2);
78
+ const intersection = new Set([...set1].filter(x => set2.has(x)));
79
+ const union = new Set([...set1, ...set2]);
80
+ return union.size === 0 ? 0 : intersection.size / union.size;
81
+ }
82
+
83
+ // Get similarity score based on algorithm
84
+ export function getSimilarityScore(str1: string, str2: string, algorithm: 'levenshtein' | 'cosine' | 'jaccard'): number {
85
+ switch (algorithm) {
86
+ case 'levenshtein':
87
+ return levenshteinDistance(str1, str2);
88
+ case 'cosine':
89
+ return cosineSimilarity(str1, str2);
90
+ case 'jaccard':
91
+ return jaccardSimilarity(str1, str2);
92
+ default:
93
+ return 0;
94
+ }
95
+ }
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { getTextHash } from './core/hash';
2
2
  import { ColorConfig, ColorGenerator } from './core/color';
3
+ import { getSimilarityScore } from './core/similarity';
3
4
  import { viteAutoColorPlugin } from './vite-plugin';
4
5
 
5
6
  // Precomputed colors cache (filled by Vite plugin at build time)
@@ -29,7 +30,31 @@ export class ColorSet {
29
30
  return precomputedColors[this.category][text];
30
31
  }
31
32
 
32
- // Generate color at runtime if not precomputed
33
+ // Check for similar strings in the same category (if using similarity algorithm)
34
+ if (this.config.algorithm && this.config.algorithm !== 'hash' && precomputedColors[this.category]) {
35
+ const similarityThreshold = this.config.similarityThreshold || 0.7;
36
+ let mostSimilarText = text;
37
+ let highestSimilarity = 0;
38
+
39
+ // Find most similar text in the category
40
+ for (const existingText in precomputedColors[this.category]) {
41
+ const similarity = getSimilarityScore(text, existingText, this.config.algorithm as 'levenshtein' | 'cosine' | 'jaccard');
42
+ if (similarity > highestSimilarity && similarity >= similarityThreshold) {
43
+ highestSimilarity = similarity;
44
+ mostSimilarText = existingText;
45
+ }
46
+ }
47
+
48
+ // Use color from most similar text if found
49
+ if (mostSimilarText !== text && precomputedColors[this.category][mostSimilarText]) {
50
+ const color = precomputedColors[this.category][mostSimilarText];
51
+ // Cache the color for current text
52
+ precomputedColors[this.category][text] = color;
53
+ return color;
54
+ }
55
+ }
56
+
57
+ // Generate color at runtime if not precomputed and no similar text found
33
58
  const color = this.generator.getColor(text, getTextHash);
34
59
 
35
60
  // Cache the generated color for future use
@@ -1,8 +1,27 @@
1
1
  import { Plugin } from 'vite';
2
- import { createFilter } from '@rollup/pluginutils';
3
2
  import { getTextHash } from './core/hash';
4
3
  import { ColorGenerator } from './core/color';
5
4
 
5
+ // Simple file filter implementation (replaces @rollup/pluginutils createFilter)
6
+ function createFilter(include: string[], exclude?: string[]) {
7
+ return function(id: string) {
8
+ // Check if id matches any include pattern
9
+ const included = include.some(pattern => {
10
+ if (pattern === '**/*') return true;
11
+ const regex = new RegExp(pattern.replace(/\*/g, '.*'));
12
+ return regex.test(id);
13
+ });
14
+
15
+ // Check if id matches any exclude pattern
16
+ const excluded = exclude && exclude.some(pattern => {
17
+ const regex = new RegExp(pattern.replace(/\*/g, '.*'));
18
+ return regex.test(id);
19
+ });
20
+
21
+ return included && !excluded;
22
+ };
23
+ }
24
+
6
25
  // Vite plugin for precomputing colors at build time
7
26
  export function viteAutoColorPlugin(): Plugin {
8
27
  const filter = createFilter(['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx', '**/*.vue']);