colx 1.0.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 +111 -0
- package/dist/analyzer/color-parser.d.ts +18 -0
- package/dist/analyzer/color-parser.d.ts.map +1 -0
- package/dist/analyzer/color-parser.js +68 -0
- package/dist/analyzer/color-parser.js.map +1 -0
- package/dist/analyzer/consolidator.d.ts +13 -0
- package/dist/analyzer/consolidator.d.ts.map +1 -0
- package/dist/analyzer/consolidator.js +72 -0
- package/dist/analyzer/consolidator.js.map +1 -0
- package/dist/analyzer/similarity.d.ts +10 -0
- package/dist/analyzer/similarity.d.ts.map +1 -0
- package/dist/analyzer/similarity.js +72 -0
- package/dist/analyzer/similarity.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +104 -0
- package/dist/index.js.map +1 -0
- package/dist/scanner/color-extractor.d.ts +10 -0
- package/dist/scanner/color-extractor.d.ts.map +1 -0
- package/dist/scanner/color-extractor.js +98 -0
- package/dist/scanner/color-extractor.js.map +1 -0
- package/dist/scanner/file-walker.d.ts +2 -0
- package/dist/scanner/file-walker.d.ts.map +1 -0
- package/dist/scanner/file-walker.js +45 -0
- package/dist/scanner/file-walker.js.map +1 -0
- package/dist/server/api.d.ts +33 -0
- package/dist/server/api.d.ts.map +1 -0
- package/dist/server/api.js +72 -0
- package/dist/server/api.js.map +1 -0
- package/dist/server/server.d.ts +4 -0
- package/dist/server/server.d.ts.map +1 -0
- package/dist/server/server.js +41 -0
- package/dist/server/server.js.map +1 -0
- package/package.json +51 -0
- package/src/ui/app.jsx +277 -0
- package/src/ui/index.html +341 -0
package/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Tailwind Color Visualizer
|
|
2
|
+
|
|
3
|
+
A CLI tool that scans TypeScript/JSX files for Tailwind arbitrary color values, visualizes them in a coolors.co-style web UI, and provides suggestions for CSS variable consolidation and color merging opportunities.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔍 **Scan** `.tsx` and `.jsx` files for Tailwind arbitrary color values
|
|
8
|
+
- 🎨 **Visualize** colors in a beautiful web interface (similar to coolors.co)
|
|
9
|
+
- 💡 **Suggest** CSS variable consolidation for duplicate colors
|
|
10
|
+
- 🔬 **Detect** similar colors and suggest merges using Delta E color difference
|
|
11
|
+
- 📊 **Statistics** about color usage across your codebase
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g colx
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or use with npx (no installation required):
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx colx [directory]
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
### Basic Usage
|
|
28
|
+
|
|
29
|
+
Scan the current directory:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
colx
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Or specify a directory:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
colx ./src
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Options
|
|
42
|
+
|
|
43
|
+
- `--port <number>` - Server port (default: 6969)
|
|
44
|
+
- `--no-open` - Don't open browser automatically
|
|
45
|
+
- `--threshold <number>` - Color similarity threshold for merge suggestions (default: 5)
|
|
46
|
+
|
|
47
|
+
### Examples
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Scan a specific directory
|
|
51
|
+
colx ./my-project/src
|
|
52
|
+
|
|
53
|
+
# Use a custom port
|
|
54
|
+
colx --port 8080
|
|
55
|
+
|
|
56
|
+
# Don't open browser automatically
|
|
57
|
+
colx --no-open
|
|
58
|
+
|
|
59
|
+
# Adjust similarity threshold (lower = more strict)
|
|
60
|
+
colx --threshold 3
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Supported Color Formats
|
|
64
|
+
|
|
65
|
+
The tool detects Tailwind arbitrary values in the following formats:
|
|
66
|
+
|
|
67
|
+
- **Hex**: `bg-[#ff5733]`, `text-[#ABC]`
|
|
68
|
+
- **RGB**: `bg-[rgb(255,87,51)]`
|
|
69
|
+
- **RGBA**: `bg-[rgba(255,87,51,0.5)]`
|
|
70
|
+
- **HSL**: `bg-[hsl(9,100%,50%)]`
|
|
71
|
+
- **HSLA**: `bg-[hsla(9,100%,50%,0.5)]`
|
|
72
|
+
|
|
73
|
+
## What It Does
|
|
74
|
+
|
|
75
|
+
1. **Scans** your codebase for `.tsx` and `.jsx` files
|
|
76
|
+
2. **Extracts** all Tailwind arbitrary color values
|
|
77
|
+
3. **Normalizes** colors to hex format for comparison
|
|
78
|
+
4. **Analyzes** color similarities using Delta E (CIEDE2000)
|
|
79
|
+
5. **Suggests** CSS variable consolidation for duplicate colors
|
|
80
|
+
6. **Suggests** color merges for visually similar colors
|
|
81
|
+
7. **Displays** everything in a beautiful web interface
|
|
82
|
+
|
|
83
|
+
## Output
|
|
84
|
+
|
|
85
|
+
The tool launches a web server and opens your browser to display:
|
|
86
|
+
|
|
87
|
+
- **Color Palette**: Grid view of all unique colors found
|
|
88
|
+
- **Color Details**: Click any color to see where it's used
|
|
89
|
+
- **Filter by Utility Class**: Filter colors by bg, text, border, etc.
|
|
90
|
+
- **Merge Suggestions**: Groups of similar colors that could be merged
|
|
91
|
+
|
|
92
|
+
## Development
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# Install dependencies
|
|
96
|
+
npm install
|
|
97
|
+
|
|
98
|
+
# Build TypeScript
|
|
99
|
+
npm run build
|
|
100
|
+
|
|
101
|
+
# Run in development mode (with Bun)
|
|
102
|
+
bun run src/index.ts
|
|
103
|
+
|
|
104
|
+
# Or with Node.js
|
|
105
|
+
npm run dev
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Requirements
|
|
109
|
+
|
|
110
|
+
- Node.js >= 16 (or Bun)
|
|
111
|
+
- TypeScript projects with `.tsx` or `.jsx` files
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface ParsedColor {
|
|
2
|
+
hex: string;
|
|
3
|
+
originalValue: string;
|
|
4
|
+
format: 'hex' | 'rgb' | 'rgba' | 'hsl' | 'hsla';
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Normalize a color value to hex format
|
|
8
|
+
* Handles hex, rgb, rgba, hsl, and hsla formats
|
|
9
|
+
*/
|
|
10
|
+
export declare function parseColor(value: string, format: ParsedColor['format']): ParsedColor | null;
|
|
11
|
+
/**
|
|
12
|
+
* Parse multiple color values and return unique parsed colors
|
|
13
|
+
*/
|
|
14
|
+
export declare function parseColors(occurrences: Array<{
|
|
15
|
+
originalValue: string;
|
|
16
|
+
format: ParsedColor['format'];
|
|
17
|
+
}>): Map<string, ParsedColor>;
|
|
18
|
+
//# sourceMappingURL=color-parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color-parser.d.ts","sourceRoot":"","sources":["../../src/analyzer/color-parser.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;CACjD;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,GAAG,WAAW,GAAG,IAAI,CA6C3F;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,WAAW,EAAE,KAAK,CAAC;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAA;CAAE,CAAC,GAC3E,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAc1B"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parseColor = parseColor;
|
|
7
|
+
exports.parseColors = parseColors;
|
|
8
|
+
const chroma_js_1 = __importDefault(require("chroma-js"));
|
|
9
|
+
/**
|
|
10
|
+
* Normalize a color value to hex format
|
|
11
|
+
* Handles hex, rgb, rgba, hsl, and hsla formats
|
|
12
|
+
*/
|
|
13
|
+
function parseColor(value, format) {
|
|
14
|
+
try {
|
|
15
|
+
let chromaColor;
|
|
16
|
+
switch (format) {
|
|
17
|
+
case 'hex':
|
|
18
|
+
// Handle 3-digit hex (#ABC -> #AABBCC)
|
|
19
|
+
if (value.length === 4) {
|
|
20
|
+
value = `#${value[1]}${value[1]}${value[2]}${value[2]}${value[3]}${value[3]}`;
|
|
21
|
+
}
|
|
22
|
+
chromaColor = (0, chroma_js_1.default)(value);
|
|
23
|
+
break;
|
|
24
|
+
case 'rgb':
|
|
25
|
+
chromaColor = (0, chroma_js_1.default)(`rgb(${value})`);
|
|
26
|
+
break;
|
|
27
|
+
case 'rgba':
|
|
28
|
+
chromaColor = (0, chroma_js_1.default)(`rgba(${value})`);
|
|
29
|
+
break;
|
|
30
|
+
case 'hsl':
|
|
31
|
+
chromaColor = (0, chroma_js_1.default)(`hsl(${value})`);
|
|
32
|
+
break;
|
|
33
|
+
case 'hsla':
|
|
34
|
+
chromaColor = (0, chroma_js_1.default)(`hsla(${value})`);
|
|
35
|
+
break;
|
|
36
|
+
default:
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
// Normalize to hex (uppercase, no alpha)
|
|
40
|
+
const hex = chromaColor.hex().toUpperCase();
|
|
41
|
+
return {
|
|
42
|
+
hex,
|
|
43
|
+
originalValue: value,
|
|
44
|
+
format
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
// Invalid color format
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Parse multiple color values and return unique parsed colors
|
|
54
|
+
*/
|
|
55
|
+
function parseColors(occurrences) {
|
|
56
|
+
const colorMap = new Map();
|
|
57
|
+
for (const occurrence of occurrences) {
|
|
58
|
+
const parsed = parseColor(occurrence.originalValue, occurrence.format);
|
|
59
|
+
if (parsed) {
|
|
60
|
+
// Use hex as the key to deduplicate
|
|
61
|
+
if (!colorMap.has(parsed.hex)) {
|
|
62
|
+
colorMap.set(parsed.hex, parsed);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return colorMap;
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=color-parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color-parser.js","sourceRoot":"","sources":["../../src/analyzer/color-parser.ts"],"names":[],"mappings":";;;;;AAYA,gCA6CC;AAKD,kCAgBC;AA9ED,0DAA+B;AAQ/B;;;GAGG;AACH,SAAgB,UAAU,CAAC,KAAa,EAAE,MAA6B;IACrE,IAAI,CAAC;QACH,IAAI,WAAyB,CAAC;QAE9B,QAAQ,MAAM,EAAE,CAAC;YACf,KAAK,KAAK;gBACR,uCAAuC;gBACvC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBACvB,KAAK,GAAG,IAAI,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;gBAChF,CAAC;gBACD,WAAW,GAAG,IAAA,mBAAM,EAAC,KAAK,CAAC,CAAC;gBAC5B,MAAM;YAER,KAAK,KAAK;gBACR,WAAW,GAAG,IAAA,mBAAM,EAAC,OAAO,KAAK,GAAG,CAAC,CAAC;gBACtC,MAAM;YAER,KAAK,MAAM;gBACT,WAAW,GAAG,IAAA,mBAAM,EAAC,QAAQ,KAAK,GAAG,CAAC,CAAC;gBACvC,MAAM;YAER,KAAK,KAAK;gBACR,WAAW,GAAG,IAAA,mBAAM,EAAC,OAAO,KAAK,GAAG,CAAC,CAAC;gBACtC,MAAM;YAER,KAAK,MAAM;gBACT,WAAW,GAAG,IAAA,mBAAM,EAAC,QAAQ,KAAK,GAAG,CAAC,CAAC;gBACvC,MAAM;YAER;gBACE,OAAO,IAAI,CAAC;QAChB,CAAC;QAED,yCAAyC;QACzC,MAAM,GAAG,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,CAAC;QAE5C,OAAO;YACL,GAAG;YACH,aAAa,EAAE,KAAK;YACpB,MAAM;SACP,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,uBAAuB;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAgB,WAAW,CACzB,WAA4E;IAE5E,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAuB,CAAC;IAEhD,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,UAAU,CAAC,UAAU,CAAC,aAAa,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;QACvE,IAAI,MAAM,EAAE,CAAC;YACX,oCAAoC;YACpC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC9B,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YACnC,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ColorOccurrence } from '../scanner/color-extractor';
|
|
2
|
+
export interface CSSVariableSuggestion {
|
|
3
|
+
variable: string;
|
|
4
|
+
value: string;
|
|
5
|
+
occurrences: number;
|
|
6
|
+
files: string[];
|
|
7
|
+
hex: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Consolidate duplicate colors into CSS variable suggestions
|
|
11
|
+
*/
|
|
12
|
+
export declare function consolidateToCSSVariables(occurrences: ColorOccurrence[]): CSSVariableSuggestion[];
|
|
13
|
+
//# sourceMappingURL=consolidator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consolidator.d.ts","sourceRoot":"","sources":["../../src/analyzer/consolidator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAG7D,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;CACb;AA4BD;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,eAAe,EAAE,GAC7B,qBAAqB,EAAE,CAmDzB"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.consolidateToCSSVariables = consolidateToCSSVariables;
|
|
4
|
+
const color_parser_1 = require("./color-parser");
|
|
5
|
+
/**
|
|
6
|
+
* Generate CSS variable name from color value or usage
|
|
7
|
+
*/
|
|
8
|
+
function generateVariableName(hex, index) {
|
|
9
|
+
// Try to generate semantic names based on color properties
|
|
10
|
+
// For now, use generic names like --color-primary, --color-accent, etc.
|
|
11
|
+
const names = [
|
|
12
|
+
'primary',
|
|
13
|
+
'secondary',
|
|
14
|
+
'accent',
|
|
15
|
+
'background',
|
|
16
|
+
'foreground',
|
|
17
|
+
'muted',
|
|
18
|
+
'border',
|
|
19
|
+
'ring',
|
|
20
|
+
'shadow'
|
|
21
|
+
];
|
|
22
|
+
if (index < names.length) {
|
|
23
|
+
return `--color-${names[index]}`;
|
|
24
|
+
}
|
|
25
|
+
// Fallback to indexed names
|
|
26
|
+
return `--color-${index + 1}`;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Consolidate duplicate colors into CSS variable suggestions
|
|
30
|
+
*/
|
|
31
|
+
function consolidateToCSSVariables(occurrences) {
|
|
32
|
+
// Group occurrences by hex value
|
|
33
|
+
const hexGroups = new Map();
|
|
34
|
+
for (const occurrence of occurrences) {
|
|
35
|
+
// Parse the color to get its hex value
|
|
36
|
+
const parsedMap = (0, color_parser_1.parseColors)([{
|
|
37
|
+
originalValue: occurrence.originalValue,
|
|
38
|
+
format: occurrence.format
|
|
39
|
+
}]);
|
|
40
|
+
// Get the parsed color (should only be one)
|
|
41
|
+
const parsed = Array.from(parsedMap.values())[0];
|
|
42
|
+
if (parsed) {
|
|
43
|
+
const hex = parsed.hex;
|
|
44
|
+
if (!hexGroups.has(hex)) {
|
|
45
|
+
hexGroups.set(hex, {
|
|
46
|
+
occurrences: [],
|
|
47
|
+
parsed
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
hexGroups.get(hex).occurrences.push(occurrence);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Generate suggestions for colors that appear multiple times
|
|
54
|
+
const suggestions = [];
|
|
55
|
+
let variableIndex = 0;
|
|
56
|
+
for (const [hex, group] of hexGroups.entries()) {
|
|
57
|
+
if (group.occurrences.length > 1) {
|
|
58
|
+
const uniqueFiles = [...new Set(group.occurrences.map(o => o.file))];
|
|
59
|
+
suggestions.push({
|
|
60
|
+
variable: generateVariableName(hex, variableIndex),
|
|
61
|
+
value: group.parsed.originalValue,
|
|
62
|
+
occurrences: group.occurrences.length,
|
|
63
|
+
files: uniqueFiles,
|
|
64
|
+
hex
|
|
65
|
+
});
|
|
66
|
+
variableIndex++;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Sort by occurrences (most used first)
|
|
70
|
+
return suggestions.sort((a, b) => b.occurrences - a.occurrences);
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=consolidator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consolidator.js","sourceRoot":"","sources":["../../src/analyzer/consolidator.ts"],"names":[],"mappings":";;AAwCA,8DAqDC;AA5FD,iDAA0D;AAU1D;;GAEG;AACH,SAAS,oBAAoB,CAAC,GAAW,EAAE,KAAa;IACtD,2DAA2D;IAC3D,wEAAwE;IACxE,MAAM,KAAK,GAAG;QACZ,SAAS;QACT,WAAW;QACX,QAAQ;QACR,YAAY;QACZ,YAAY;QACZ,OAAO;QACP,QAAQ;QACR,MAAM;QACN,QAAQ;KACT,CAAC;IAEF,IAAI,KAAK,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QACzB,OAAO,WAAW,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;IACnC,CAAC;IAED,4BAA4B;IAC5B,OAAO,WAAW,KAAK,GAAG,CAAC,EAAE,CAAC;AAChC,CAAC;AAED;;GAEG;AACH,SAAgB,yBAAyB,CACvC,WAA8B;IAE9B,iCAAiC;IACjC,MAAM,SAAS,GAAG,IAAI,GAAG,EAGrB,CAAC;IAEL,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;QACrC,uCAAuC;QACvC,MAAM,SAAS,GAAG,IAAA,0BAAW,EAAC,CAAC;gBAC7B,aAAa,EAAE,UAAU,CAAC,aAAa;gBACvC,MAAM,EAAE,UAAU,CAAC,MAAM;aAC1B,CAAC,CAAC,CAAC;QAEJ,4CAA4C;QAC5C,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAEjD,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC;YACvB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBACxB,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE;oBACjB,WAAW,EAAE,EAAE;oBACf,MAAM;iBACP,CAAC,CAAC;YACL,CAAC;YACD,SAAS,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAED,6DAA6D;IAC7D,MAAM,WAAW,GAA4B,EAAE,CAAC;IAChD,IAAI,aAAa,GAAG,CAAC,CAAC;IAEtB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,SAAS,CAAC,OAAO,EAAE,EAAE,CAAC;QAC/C,IAAI,KAAK,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACjC,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAErE,WAAW,CAAC,IAAI,CAAC;gBACf,QAAQ,EAAE,oBAAoB,CAAC,GAAG,EAAE,aAAa,CAAC;gBAClD,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,aAAa;gBACjC,WAAW,EAAE,KAAK,CAAC,WAAW,CAAC,MAAM;gBACrC,KAAK,EAAE,WAAW;gBAClB,GAAG;aACJ,CAAC,CAAC;YAEH,aAAa,EAAE,CAAC;QAClB,CAAC;IACH,CAAC;IAED,wCAAwC;IACxC,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC;AACnE,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface SimilarColorGroup {
|
|
2
|
+
colors: string[];
|
|
3
|
+
suggestedColor: string;
|
|
4
|
+
averageSimilarity: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Find similar color groups using Delta E threshold
|
|
8
|
+
*/
|
|
9
|
+
export declare function findSimilarColors(colors: string[], threshold?: number): SimilarColorGroup[];
|
|
10
|
+
//# sourceMappingURL=similarity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"similarity.d.ts","sourceRoot":"","sources":["../../src/analyzer/similarity.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AA2BD;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EAAE,EAChB,SAAS,GAAE,MAAU,GACpB,iBAAiB,EAAE,CA4CrB"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.findSimilarColors = findSimilarColors;
|
|
7
|
+
const chroma_js_1 = __importDefault(require("chroma-js"));
|
|
8
|
+
/**
|
|
9
|
+
* Calculate Delta E (CIEDE2000) color difference
|
|
10
|
+
* Lower values mean colors are more similar
|
|
11
|
+
*/
|
|
12
|
+
function calculateDeltaE(color1, color2) {
|
|
13
|
+
try {
|
|
14
|
+
const c1 = (0, chroma_js_1.default)(color1);
|
|
15
|
+
const c2 = (0, chroma_js_1.default)(color2);
|
|
16
|
+
// Get LAB values for Delta E calculation
|
|
17
|
+
const lab1 = c1.lab();
|
|
18
|
+
const lab2 = c2.lab();
|
|
19
|
+
// Simple Delta E calculation (Euclidean distance in LAB space)
|
|
20
|
+
// For more accurate CIEDE2000, we'd need a specialized library
|
|
21
|
+
const deltaL = lab1[0] - lab2[0];
|
|
22
|
+
const deltaA = lab1[1] - lab2[1];
|
|
23
|
+
const deltaB = lab1[2] - lab2[2];
|
|
24
|
+
return Math.sqrt(deltaL * deltaL + deltaA * deltaA + deltaB * deltaB);
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
return Infinity; // Colors are very different if we can't compare
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Find similar color groups using Delta E threshold
|
|
32
|
+
*/
|
|
33
|
+
function findSimilarColors(colors, threshold = 5) {
|
|
34
|
+
if (colors.length === 0)
|
|
35
|
+
return [];
|
|
36
|
+
const groups = [];
|
|
37
|
+
const processed = new Set();
|
|
38
|
+
for (let i = 0; i < colors.length; i++) {
|
|
39
|
+
const color1 = colors[i];
|
|
40
|
+
if (processed.has(color1))
|
|
41
|
+
continue;
|
|
42
|
+
const similarColors = [color1];
|
|
43
|
+
const similarities = [];
|
|
44
|
+
// Find all colors similar to color1
|
|
45
|
+
for (let j = i + 1; j < colors.length; j++) {
|
|
46
|
+
const color2 = colors[j];
|
|
47
|
+
if (processed.has(color2))
|
|
48
|
+
continue;
|
|
49
|
+
const deltaE = calculateDeltaE(color1, color2);
|
|
50
|
+
if (deltaE <= threshold) {
|
|
51
|
+
similarColors.push(color2);
|
|
52
|
+
similarities.push(deltaE);
|
|
53
|
+
processed.add(color2);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (similarColors.length > 1) {
|
|
57
|
+
// Calculate average similarity
|
|
58
|
+
const avgSimilarity = similarities.length > 0
|
|
59
|
+
? similarities.reduce((a, b) => a + b, 0) / similarities.length
|
|
60
|
+
: 0;
|
|
61
|
+
// Suggest the first color as the merge target (could be improved with better logic)
|
|
62
|
+
groups.push({
|
|
63
|
+
colors: similarColors,
|
|
64
|
+
suggestedColor: color1,
|
|
65
|
+
averageSimilarity: avgSimilarity
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
processed.add(color1);
|
|
69
|
+
}
|
|
70
|
+
return groups;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=similarity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"similarity.js","sourceRoot":"","sources":["../../src/analyzer/similarity.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const path_1 = require("path");
|
|
6
|
+
const fs_1 = require("fs");
|
|
7
|
+
const file_walker_1 = require("./scanner/file-walker");
|
|
8
|
+
const color_extractor_1 = require("./scanner/color-extractor");
|
|
9
|
+
const color_parser_1 = require("./analyzer/color-parser");
|
|
10
|
+
const similarity_1 = require("./analyzer/similarity");
|
|
11
|
+
const consolidator_1 = require("./analyzer/consolidator");
|
|
12
|
+
const server_1 = require("./server/server");
|
|
13
|
+
const api_1 = require("./server/api");
|
|
14
|
+
function findUiDirectory() {
|
|
15
|
+
// Try multiple possible paths
|
|
16
|
+
const possiblePaths = [
|
|
17
|
+
// Development (running from src/)
|
|
18
|
+
(0, path_1.resolve)(__dirname, 'ui'),
|
|
19
|
+
// Production (running from dist/)
|
|
20
|
+
(0, path_1.resolve)(__dirname, '../src/ui'),
|
|
21
|
+
// Installed package
|
|
22
|
+
(0, path_1.resolve)(__dirname, '../../src/ui'),
|
|
23
|
+
];
|
|
24
|
+
for (const path of possiblePaths) {
|
|
25
|
+
if ((0, fs_1.existsSync)(path) && (0, fs_1.existsSync)((0, path_1.join)(path, 'index.html'))) {
|
|
26
|
+
return path;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// Fallback to first path
|
|
30
|
+
return possiblePaths[0];
|
|
31
|
+
}
|
|
32
|
+
const program = new commander_1.Command();
|
|
33
|
+
program
|
|
34
|
+
.name('tailwind-color-visualizer')
|
|
35
|
+
.description('Scan and visualize Tailwind arbitrary color values')
|
|
36
|
+
.version('1.0.0')
|
|
37
|
+
.argument('[directory]', 'Directory to scan (default: current directory)', process.cwd())
|
|
38
|
+
.option('-p, --port <number>', 'Server port', '6969')
|
|
39
|
+
.option('--no-open', 'Do not open browser automatically')
|
|
40
|
+
.option('-t, --threshold <number>', 'Color similarity threshold (Delta E)', '5')
|
|
41
|
+
.action(async (directory, options) => {
|
|
42
|
+
const targetDir = (0, path_1.resolve)(directory);
|
|
43
|
+
const port = parseInt(options.port, 10);
|
|
44
|
+
const threshold = parseFloat(options.threshold);
|
|
45
|
+
const shouldOpen = options.open !== false;
|
|
46
|
+
console.log('🔍 Scanning for Tailwind arbitrary color values...\n');
|
|
47
|
+
console.log(`📁 Directory: ${targetDir}`);
|
|
48
|
+
console.log(`🎨 Similarity threshold: ${threshold}`);
|
|
49
|
+
console.log('');
|
|
50
|
+
try {
|
|
51
|
+
// Phase 1: Find and scan files
|
|
52
|
+
console.log('📂 Finding .tsx and .jsx files...');
|
|
53
|
+
const files = await (0, file_walker_1.findTsxJsxFiles)(targetDir);
|
|
54
|
+
console.log(` Found ${files.length} file${files.length !== 1 ? 's' : ''}`);
|
|
55
|
+
if (files.length === 0) {
|
|
56
|
+
console.log('\n⚠️ No .tsx or .jsx files found in the specified directory.');
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
// Phase 2: Extract colors
|
|
60
|
+
console.log('\n🎨 Extracting color values...');
|
|
61
|
+
const occurrences = await (0, color_extractor_1.extractColorsFromFiles)(files);
|
|
62
|
+
console.log(` Found ${occurrences.length} color occurrence${occurrences.length !== 1 ? 's' : ''}`);
|
|
63
|
+
if (occurrences.length === 0) {
|
|
64
|
+
console.log('\n⚠️ No Tailwind arbitrary color values found.');
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
// Phase 3: Parse and normalize colors
|
|
68
|
+
console.log('\n🔬 Parsing and normalizing colors...');
|
|
69
|
+
const parsedColors = (0, color_parser_1.parseColors)(occurrences.map(occ => ({
|
|
70
|
+
originalValue: occ.originalValue,
|
|
71
|
+
format: occ.format
|
|
72
|
+
})));
|
|
73
|
+
console.log(` Found ${parsedColors.size} unique color${parsedColors.size !== 1 ? 's' : ''}`);
|
|
74
|
+
// Phase 4: Analyze similarities
|
|
75
|
+
console.log('\n🔍 Analyzing color similarities...');
|
|
76
|
+
const uniqueHexColors = Array.from(parsedColors.keys());
|
|
77
|
+
const similarGroups = (0, similarity_1.findSimilarColors)(uniqueHexColors, threshold);
|
|
78
|
+
console.log(` Found ${similarGroups.length} group${similarGroups.length !== 1 ? 's' : ''} of similar colors`);
|
|
79
|
+
// Phase 5: Generate CSS variable suggestions
|
|
80
|
+
console.log('\n💡 Generating CSS variable suggestions...');
|
|
81
|
+
const cssVariables = (0, consolidator_1.consolidateToCSSVariables)(occurrences);
|
|
82
|
+
console.log(` Generated ${cssVariables.length} CSS variable suggestion${cssVariables.length !== 1 ? 's' : ''}`);
|
|
83
|
+
// Phase 6: Setup API data
|
|
84
|
+
(0, api_1.setColorData)(occurrences, parsedColors, similarGroups, cssVariables);
|
|
85
|
+
// Phase 7: Start server
|
|
86
|
+
console.log('\n🚀 Starting web server...');
|
|
87
|
+
const uiDir = findUiDirectory();
|
|
88
|
+
await (0, server_1.startServer)(port, uiDir, shouldOpen);
|
|
89
|
+
// Keep the process alive
|
|
90
|
+
process.on('SIGINT', () => {
|
|
91
|
+
console.log('\n\n👋 Shutting down...');
|
|
92
|
+
process.exit(0);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
console.error('\n❌ Error:', error instanceof Error ? error.message : String(error));
|
|
97
|
+
if (error instanceof Error && error.stack) {
|
|
98
|
+
console.error(error.stack);
|
|
99
|
+
}
|
|
100
|
+
process.exit(1);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
program.parse();
|
|
104
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface ColorOccurrence {
|
|
2
|
+
file: string;
|
|
3
|
+
line: number;
|
|
4
|
+
className: string;
|
|
5
|
+
originalValue: string;
|
|
6
|
+
format: 'hex' | 'rgb' | 'rgba' | 'hsl' | 'hsla';
|
|
7
|
+
}
|
|
8
|
+
export declare function extractColorsFromFile(filePath: string): Promise<ColorOccurrence[]>;
|
|
9
|
+
export declare function extractColorsFromFiles(filePaths: string[]): Promise<ColorOccurrence[]>;
|
|
10
|
+
//# sourceMappingURL=color-extractor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color-extractor.d.ts","sourceRoot":"","sources":["../../src/scanner/color-extractor.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;CACjD;AAyCD,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAwExF;AAED,wBAAsB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAS5F"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractColorsFromFile = extractColorsFromFile;
|
|
4
|
+
exports.extractColorsFromFiles = extractColorsFromFiles;
|
|
5
|
+
const promises_1 = require("fs/promises");
|
|
6
|
+
// Tailwind utility prefixes that support color arbitrary values
|
|
7
|
+
const COLOR_PREFIXES = [
|
|
8
|
+
'bg', 'text', 'border', 'ring', 'outline', 'divide', 'from', 'via', 'to',
|
|
9
|
+
'decoration', 'accent', 'caret', 'fill', 'stroke', 'shadow'
|
|
10
|
+
];
|
|
11
|
+
// Build regex pattern for all color prefixes
|
|
12
|
+
const prefixPattern = COLOR_PREFIXES.join('|');
|
|
13
|
+
// Hex color pattern: bg-[#ff5733], text-[#ABC]
|
|
14
|
+
const hexPattern = new RegExp(`(?:${prefixPattern})-\\[#([0-9a-fA-F]{3,8})\\]`, 'g');
|
|
15
|
+
// RGB pattern: bg-[rgb(255,87,51)]
|
|
16
|
+
const rgbPattern = new RegExp(`(?:${prefixPattern})-\\[rgb\\(([^)]+)\\)\\]`, 'g');
|
|
17
|
+
// RGBA pattern: bg-[rgba(255,87,51,0.5)]
|
|
18
|
+
const rgbaPattern = new RegExp(`(?:${prefixPattern})-\\[rgba\\(([^)]+)\\)\\]`, 'g');
|
|
19
|
+
// HSL pattern: bg-[hsl(9,100%,50%)]
|
|
20
|
+
const hslPattern = new RegExp(`(?:${prefixPattern})-\\[hsl\\(([^)]+)\\)\\]`, 'g');
|
|
21
|
+
// HSLA pattern: bg-[hsla(9,100%,50%,0.5)]
|
|
22
|
+
const hslaPattern = new RegExp(`(?:${prefixPattern})-\\[hsla\\(([^)]+)\\)\\]`, 'g');
|
|
23
|
+
async function extractColorsFromFile(filePath) {
|
|
24
|
+
const occurrences = [];
|
|
25
|
+
try {
|
|
26
|
+
const content = await (0, promises_1.readFile)(filePath, 'utf-8');
|
|
27
|
+
const lines = content.split('\n');
|
|
28
|
+
// Extract hex colors
|
|
29
|
+
let match;
|
|
30
|
+
while ((match = hexPattern.exec(content)) !== null) {
|
|
31
|
+
const lineNumber = content.substring(0, match.index).split('\n').length;
|
|
32
|
+
occurrences.push({
|
|
33
|
+
file: filePath,
|
|
34
|
+
line: lineNumber,
|
|
35
|
+
className: match[0],
|
|
36
|
+
originalValue: `#${match[1]}`,
|
|
37
|
+
format: 'hex'
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
// Extract RGB colors
|
|
41
|
+
while ((match = rgbPattern.exec(content)) !== null) {
|
|
42
|
+
const lineNumber = content.substring(0, match.index).split('\n').length;
|
|
43
|
+
occurrences.push({
|
|
44
|
+
file: filePath,
|
|
45
|
+
line: lineNumber,
|
|
46
|
+
className: match[0],
|
|
47
|
+
originalValue: `rgb(${match[1]})`,
|
|
48
|
+
format: 'rgb'
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
// Extract RGBA colors
|
|
52
|
+
while ((match = rgbaPattern.exec(content)) !== null) {
|
|
53
|
+
const lineNumber = content.substring(0, match.index).split('\n').length;
|
|
54
|
+
occurrences.push({
|
|
55
|
+
file: filePath,
|
|
56
|
+
line: lineNumber,
|
|
57
|
+
className: match[0],
|
|
58
|
+
originalValue: `rgba(${match[1]})`,
|
|
59
|
+
format: 'rgba'
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
// Extract HSL colors
|
|
63
|
+
while ((match = hslPattern.exec(content)) !== null) {
|
|
64
|
+
const lineNumber = content.substring(0, match.index).split('\n').length;
|
|
65
|
+
occurrences.push({
|
|
66
|
+
file: filePath,
|
|
67
|
+
line: lineNumber,
|
|
68
|
+
className: match[0],
|
|
69
|
+
originalValue: `hsl(${match[1]})`,
|
|
70
|
+
format: 'hsl'
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
// Extract HSLA colors
|
|
74
|
+
while ((match = hslaPattern.exec(content)) !== null) {
|
|
75
|
+
const lineNumber = content.substring(0, match.index).split('\n').length;
|
|
76
|
+
occurrences.push({
|
|
77
|
+
file: filePath,
|
|
78
|
+
line: lineNumber,
|
|
79
|
+
className: match[0],
|
|
80
|
+
originalValue: `hsla(${match[1]})`,
|
|
81
|
+
format: 'hsla'
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
console.warn(`Warning: Could not read file ${filePath}: ${error}`);
|
|
87
|
+
}
|
|
88
|
+
return occurrences;
|
|
89
|
+
}
|
|
90
|
+
async function extractColorsFromFiles(filePaths) {
|
|
91
|
+
const allOccurrences = [];
|
|
92
|
+
for (const filePath of filePaths) {
|
|
93
|
+
const occurrences = await extractColorsFromFile(filePath);
|
|
94
|
+
allOccurrences.push(...occurrences);
|
|
95
|
+
}
|
|
96
|
+
return allOccurrences;
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=color-extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"color-extractor.js","sourceRoot":"","sources":["../../src/scanner/color-extractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-walker.d.ts","sourceRoot":"","sources":["../../src/scanner/file-walker.ts"],"names":[],"mappings":"AAKA,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAwCxE"}
|