colbrush 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/dist/KR-BEBXLJC6.svg +27 -0
- package/dist/US-H2QUNREB.svg +23 -0
- package/dist/cli.cjs +398 -0
- package/dist/cli.d.cts +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +375 -0
- package/dist/client.cjs +393 -0
- package/dist/client.d.cts +23 -0
- package/dist/client.d.ts +23 -0
- package/dist/client.js +361 -0
- package/dist/default-EPJEL445.svg +4 -0
- package/dist/deuteranopia-C23MR6FZ.svg +4 -0
- package/dist/logo-RY5YYL6A.svg +10 -0
- package/dist/protanopia-HOT6PUHN.svg +5 -0
- package/dist/styles.css +2 -0
- package/dist/tritanopia-MTE6DBJL.svg +5 -0
- package/package.json +80 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<svg width="42" height="30" viewBox="0 0 42 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<g clip-path="url(#clip0_85_784)" filter="url(#filter0_d_85_784)">
|
|
3
|
+
<path d="M3 3H39V27H3V3Z" fill="white"/>
|
|
4
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.49609 10.27L11.8243 5.27771L12.7001 5.86161L9.37194 10.8539L8.49609 10.27ZM9.74417 11.1021L13.0724 6.10976L13.9482 6.69366L10.62 11.686L9.74417 11.1021ZM10.9922 11.9341L14.3204 6.94181L15.1963 7.52571L11.8681 12.518L10.9922 11.9341ZM26.8012 22.4734L30.1294 17.4811L31.0053 18.065L27.6771 23.0573L26.8012 22.4734ZM28.0493 23.3055L31.3775 18.3131L32.2533 18.897L28.9251 23.8893L28.0493 23.3055ZM33.5014 19.7291L30.1732 24.7214L29.2974 24.1375L32.6256 19.1452L33.5014 19.7291Z" fill="black"/>
|
|
5
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M31.9884 22.8526L27.8281 20.0791L28.3147 19.3492L32.475 22.1227L31.9884 22.8526Z" fill="white"/>
|
|
6
|
+
<path d="M25.9915 18.3277C27.8296 15.5705 27.0846 11.8453 24.3274 10.0072C21.5702 8.16905 17.845 8.9141 16.0069 11.6713C14.1688 14.4284 14.9138 18.1537 17.671 19.9918C20.4282 21.8299 24.1534 21.0848 25.9915 18.3277Z" fill="#CA163A"/>
|
|
7
|
+
<path d="M16.0069 11.6713C15.0883 13.0491 15.4611 14.9129 16.839 15.8315C18.2168 16.7501 20.0806 16.3773 20.9992 14.9995C21.9178 13.6216 23.7816 13.2488 25.1595 14.1674C26.5373 15.086 26.9101 16.9498 25.9915 18.3277C24.1544 21.0834 20.4268 21.8289 17.671 19.9918C14.9153 18.1546 14.1697 14.427 16.0069 11.6713Z" fill="#0E4896"/>
|
|
8
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M30.1732 5.27734L33.5014 10.2697L32.6255 10.8536L29.2973 5.86124L30.1732 5.27734ZM28.9251 6.10942L32.2533 11.1017L31.3775 11.6856L28.0493 6.69332L28.9251 6.10942ZM27.677 6.94146L31.0052 11.9337L30.1294 12.5176L26.8012 7.52536L27.677 6.94146ZM11.868 17.4807L15.1962 22.4731L14.3204 23.057L10.9922 18.0646L11.868 17.4807ZM10.6199 18.3128L13.9481 23.3051L13.0723 23.889L9.74409 18.8967L10.6199 18.3128ZM9.37194 19.1449L12.7001 24.1372L11.8243 24.7211L8.49609 19.7288L9.37194 19.1449Z" fill="black"/>
|
|
9
|
+
<path d="M11.2241 21.5159L12.4722 20.6839L11.2241 21.5159ZM28.0714 10.2844L29.5292 9.31252L28.0714 10.2844ZM30.7773 8.48048L32.0254 7.64844L30.7773 8.48048Z" fill="black"/>
|
|
10
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M32.2688 8.01308L31.0207 8.84512L30.5341 8.11524L31.7822 7.2832L32.2688 8.01308ZM29.7726 9.67715L28.3148 10.649L27.8283 9.91912L29.286 8.94728L29.7726 9.67715ZM12.7156 21.0486L11.4675 21.8806L10.981 21.1507L12.229 20.3187L12.7156 21.0486Z" fill="white"/>
|
|
11
|
+
</g>
|
|
12
|
+
<defs>
|
|
13
|
+
<filter id="filter0_d_85_784" x="0.658202" y="0.658202" width="40.6836" height="28.6836" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
14
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
15
|
+
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
|
16
|
+
<feOffset/>
|
|
17
|
+
<feGaussianBlur stdDeviation="1.1709"/>
|
|
18
|
+
<feComposite in2="hardAlpha" operator="out"/>
|
|
19
|
+
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
|
20
|
+
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_85_784"/>
|
|
21
|
+
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_85_784" result="shape"/>
|
|
22
|
+
</filter>
|
|
23
|
+
<clipPath id="clip0_85_784">
|
|
24
|
+
<rect width="36" height="24" fill="white" transform="translate(3 3)"/>
|
|
25
|
+
</clipPath>
|
|
26
|
+
</defs>
|
|
27
|
+
</svg>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<svg width="50" height="38" viewBox="0 0 50 38" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<g clip-path="url(#clip0_85_779)" filter="url(#filter0_d_85_779)">
|
|
3
|
+
<path d="M7 7H42.7847V30.8565H7V7Z" fill="white"/>
|
|
4
|
+
<path d="M7 7H42.7847V8.83458H7V7ZM7 10.6692H42.7847V12.5037H7V10.6692ZM7 14.3383H42.7847V16.1729H7V14.3383ZM7 18.0075H42.7847V19.842H7V18.0075ZM7 21.6836H42.7847V23.5182H7V21.6836ZM7 25.3527H42.7847V27.1873H7V25.3527ZM7 29.0219H42.7847V30.8565H7V29.0219Z" fill="#D80027"/>
|
|
5
|
+
<path d="M7 7H24.8923V19.842H7V7Z" fill="#2E52B2"/>
|
|
6
|
+
<path d="M10.3344 16.6892L10.0553 15.7964L9.74841 16.6892H8.82764L9.57402 17.2264L9.295 18.1192L10.0553 17.5682L10.7947 18.1192L10.5087 17.2264L11.2691 16.6892H10.3344ZM14.2616 16.6892L13.9756 15.7964L13.6826 16.6892H12.7619L13.5082 17.2264L13.2292 18.1192L13.9756 17.5682L14.729 18.1192L14.4499 17.2264L15.1963 16.6892H14.2616ZM18.2028 16.6892L17.9029 15.7964L17.6238 16.6892H16.6821L17.4494 17.2264L17.1565 18.1192L17.9029 17.5682L18.6702 18.1192L18.3772 17.2264L19.1236 16.6892H18.2028ZM22.1231 16.6892L21.8441 15.7964L21.5511 16.6892H20.6233L21.3767 17.2264L21.0977 18.1192L21.8441 17.5682L22.5974 18.1192L22.2975 17.2264L23.0648 16.6892H22.1231ZM13.9756 12.2528L13.6826 13.1457H12.7619L13.5082 13.6967L13.2292 14.5757L13.9756 14.0316L14.729 14.5757L14.4499 13.6967L15.1963 13.1457H14.2616L13.9756 12.2528ZM10.0553 12.2528L9.74841 13.1457H8.82764L9.57402 13.6967L9.295 14.5757L10.0553 14.0316L10.7947 14.5757L10.5087 13.6967L11.2691 13.1457H10.3344L10.0553 12.2528ZM17.9029 12.2528L17.6238 13.1457H16.6821L17.4494 13.6967L17.1565 14.5757L17.9029 14.0316L18.6702 14.5757L18.3772 13.6967L19.1236 13.1457H18.2028L17.9029 12.2528ZM21.8441 12.2528L21.5511 13.1457H20.6233L21.3767 13.6967L21.0977 14.5757L21.8441 14.0316L22.5974 14.5757L22.2975 13.6967L23.0648 13.1457H22.1231L21.8441 12.2528ZM10.0553 8.72314L9.74841 9.60207H8.82764L9.57402 10.1531L9.295 11.039L10.0553 10.488L10.7947 11.039L10.5087 10.1531L11.2691 9.60207H10.3344L10.0553 8.72314ZM13.9756 8.72314L13.6826 9.60207H12.7619L13.5082 10.1531L13.2292 11.039L13.9756 10.488L14.729 11.039L14.4499 10.1531L15.1963 9.60207H14.2616L13.9756 8.72314ZM17.9029 8.72314L17.6238 9.60207H16.6821L17.4494 10.1531L17.1565 11.039L17.9029 10.488L18.6702 11.039L18.3772 10.1531L19.1236 9.60207H18.2028L17.9029 8.72314ZM21.8441 8.72314L21.5511 9.60207H20.6233L21.3767 10.1531L21.0977 11.039L21.8441 10.488L22.5974 11.039L22.2975 10.1531L23.0648 9.60207H22.1231L21.8441 8.72314Z" fill="white"/>
|
|
7
|
+
</g>
|
|
8
|
+
<defs>
|
|
9
|
+
<filter id="filter0_d_85_779" x="0.761634" y="0.761634" width="48.2614" height="36.3332" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
10
|
+
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
|
11
|
+
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
|
12
|
+
<feOffset/>
|
|
13
|
+
<feGaussianBlur stdDeviation="3.11918"/>
|
|
14
|
+
<feComposite in2="hardAlpha" operator="out"/>
|
|
15
|
+
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/>
|
|
16
|
+
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_85_779"/>
|
|
17
|
+
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_85_779" result="shape"/>
|
|
18
|
+
</filter>
|
|
19
|
+
<clipPath id="clip0_85_779">
|
|
20
|
+
<rect width="35.7847" height="23.8565" fill="white" transform="translate(7 7)"/>
|
|
21
|
+
</clipPath>
|
|
22
|
+
</defs>
|
|
23
|
+
</svg>
|
package/dist/cli.cjs
ADDED
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
// src/node/parseFlags.ts
|
|
27
|
+
function parseFlags(argv = process.argv.slice(2)) {
|
|
28
|
+
const out = { _: [] };
|
|
29
|
+
for (const s of argv) {
|
|
30
|
+
const m = /^--([^=]+)=(.*)$/.exec(s);
|
|
31
|
+
if (m) out[m[1]] = m[2];
|
|
32
|
+
else if (s.startsWith("--")) out[s.slice(2)] = true;
|
|
33
|
+
else out._.push(s);
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// src/constants/regex.ts
|
|
39
|
+
var variableRegex = /(--color-[\w-]+):\s*(#[0-9a-fA-F]{3,8})/g;
|
|
40
|
+
var variationRegex = /^--(.+?)-(50|100|200|300|400|500|600|700|800|900)$/i;
|
|
41
|
+
|
|
42
|
+
// src/node/applyThemes.ts
|
|
43
|
+
var import_node_fs = __toESM(require("fs"), 1);
|
|
44
|
+
var import_postcss = __toESM(require("postcss"), 1);
|
|
45
|
+
var import_postcss_safe_parser = __toESM(require("postcss-safe-parser"), 1);
|
|
46
|
+
|
|
47
|
+
// src/utils/core/colorScale.ts
|
|
48
|
+
var import_colorjs = __toESM(require("colorjs.io"), 1);
|
|
49
|
+
|
|
50
|
+
// src/constants/variation.ts
|
|
51
|
+
var DEFAULT_KEYS = [
|
|
52
|
+
"50",
|
|
53
|
+
"100",
|
|
54
|
+
"200",
|
|
55
|
+
"300",
|
|
56
|
+
"400",
|
|
57
|
+
"500",
|
|
58
|
+
"600",
|
|
59
|
+
"700",
|
|
60
|
+
"800",
|
|
61
|
+
"900"
|
|
62
|
+
];
|
|
63
|
+
var DEFAULT_SCALE = {
|
|
64
|
+
"50": { dL: 0.36, cMul: 0.95 },
|
|
65
|
+
"100": { dL: 0.28, cMul: 0.96 },
|
|
66
|
+
"200": { dL: 0.18, cMul: 0.98 },
|
|
67
|
+
"300": { dL: 0.08, cMul: 0.99 },
|
|
68
|
+
"400": { dL: 0.02, cMul: 1 },
|
|
69
|
+
"500": { dL: 0, cMul: 1 },
|
|
70
|
+
"600": { dL: -0.05, cMul: 0.98 },
|
|
71
|
+
"700": { dL: -0.15, cMul: 0.94 },
|
|
72
|
+
"800": { dL: -0.22, cMul: 0.9 },
|
|
73
|
+
"900": { dL: -0.3, cMul: 0.88 }
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// src/utils/core/colorScale.ts
|
|
77
|
+
var CLAMP01 = (x) => Math.max(0, Math.min(1, x));
|
|
78
|
+
function hexToOKLCH(hex) {
|
|
79
|
+
const c = new import_colorjs.default(hex);
|
|
80
|
+
const o = c.to("oklch");
|
|
81
|
+
return { l: o.l, c: o.c, h: o.h ?? 0 };
|
|
82
|
+
}
|
|
83
|
+
function oklchToHex(l, c, h) {
|
|
84
|
+
const color = new import_colorjs.default("oklch", [l, c, h]);
|
|
85
|
+
const srgb = color.to("srgb");
|
|
86
|
+
const r = CLAMP01(srgb.r), g = CLAMP01(srgb.g), b = CLAMP01(srgb.b);
|
|
87
|
+
return new import_colorjs.default("srgb", [r, g, b]).toString({ format: "hex" });
|
|
88
|
+
}
|
|
89
|
+
function buildScaleFromBaseHex(baseHex, opts) {
|
|
90
|
+
const keys = opts?.keys ?? DEFAULT_KEYS;
|
|
91
|
+
const cMin = opts?.cMin ?? 0.02;
|
|
92
|
+
const cMax = opts?.cMax ?? 0.4;
|
|
93
|
+
const lockHue = true;
|
|
94
|
+
const base = hexToOKLCH(baseHex);
|
|
95
|
+
const out = {};
|
|
96
|
+
for (const k of keys) {
|
|
97
|
+
const pat = DEFAULT_SCALE[k] ?? DEFAULT_SCALE["500"];
|
|
98
|
+
const L = CLAMP01(base.l + pat.dL);
|
|
99
|
+
const C = Math.max(cMin, Math.min(cMax, base.c * pat.cMul));
|
|
100
|
+
const H = lockHue ? base.h : base.h;
|
|
101
|
+
out[k] = oklchToHex(L, C, H);
|
|
102
|
+
}
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// src/node/applyThemes.ts
|
|
107
|
+
var CSS_PATH = "src/index.css";
|
|
108
|
+
function toThemeColor(hex, _vision) {
|
|
109
|
+
return hex;
|
|
110
|
+
}
|
|
111
|
+
function loadRoot(cssPath = CSS_PATH) {
|
|
112
|
+
const css = import_node_fs.default.readFileSync(cssPath, "utf8");
|
|
113
|
+
return (0, import_postcss.default)().process(css, { parser: import_postcss_safe_parser.default }).root;
|
|
114
|
+
}
|
|
115
|
+
function getExistingKeysForToken(root, token) {
|
|
116
|
+
const keys = /* @__PURE__ */ new Set();
|
|
117
|
+
root.walkDecls((d) => {
|
|
118
|
+
if (!d.prop.startsWith("--")) return;
|
|
119
|
+
const m = d.prop.match(variationRegex);
|
|
120
|
+
if (!m) return;
|
|
121
|
+
const [, t, k] = m;
|
|
122
|
+
if (t === token) keys.add(k);
|
|
123
|
+
});
|
|
124
|
+
return Array.from(keys);
|
|
125
|
+
}
|
|
126
|
+
function upsertBlock(root, selector, kv) {
|
|
127
|
+
let rule;
|
|
128
|
+
root.walkRules((r) => {
|
|
129
|
+
if (r.selector === selector) rule = r;
|
|
130
|
+
});
|
|
131
|
+
if (!rule) {
|
|
132
|
+
rule = new import_postcss.Rule({ selector });
|
|
133
|
+
root.append(rule);
|
|
134
|
+
}
|
|
135
|
+
const remain = new Map(Object.entries(kv));
|
|
136
|
+
rule.walkDecls((d) => {
|
|
137
|
+
if (remain.has(d.prop)) {
|
|
138
|
+
d.value = remain.get(d.prop);
|
|
139
|
+
remain.delete(d.prop);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
for (const [prop, value] of remain) rule.append({ prop, value });
|
|
143
|
+
}
|
|
144
|
+
function applyThemes(input, cssPath = CSS_PATH) {
|
|
145
|
+
const root = loadRoot(cssPath);
|
|
146
|
+
const selector = `[data-theme='${input.vision}']`;
|
|
147
|
+
const varsForTheme = {};
|
|
148
|
+
for (const [varName, val] of Object.entries(input.variables)) {
|
|
149
|
+
const rich = typeof val === "string" ? inferRich(varName, val) : val;
|
|
150
|
+
const m = varName.match(variationRegex);
|
|
151
|
+
const isPattern = !!m;
|
|
152
|
+
if (isPattern) {
|
|
153
|
+
const [, token] = m;
|
|
154
|
+
const keys = rich.keys && rich.keys.length ? rich.keys : getExistingKeysForToken(root, token).length ? getExistingKeysForToken(root, token) : Array.from(DEFAULT_KEYS);
|
|
155
|
+
if (rich.scale !== false) {
|
|
156
|
+
const scaleMap = buildScaleFromBaseHex(rich.base, {
|
|
157
|
+
keys
|
|
158
|
+
});
|
|
159
|
+
for (const k of keys) {
|
|
160
|
+
const hex = scaleMap[k];
|
|
161
|
+
varsForTheme[`--${token}-${k}`] = toThemeColor(
|
|
162
|
+
hex,
|
|
163
|
+
input.vision
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
} else {
|
|
167
|
+
varsForTheme[varName] = toThemeColor(rich.base, input.vision);
|
|
168
|
+
}
|
|
169
|
+
} else {
|
|
170
|
+
varsForTheme[varName] = toThemeColor(rich.base, input.vision);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
upsertBlock(root, selector, varsForTheme);
|
|
174
|
+
import_node_fs.default.writeFileSync(cssPath, root.toString(), "utf8");
|
|
175
|
+
console.log(`\u2705 [${input.vision}] theme updated in ${cssPath}`);
|
|
176
|
+
}
|
|
177
|
+
function inferRich(varName, baseHex) {
|
|
178
|
+
return variationRegex.test(varName) ? { base: baseHex, scale: true } : { base: baseHex, scale: false };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// src/node/colorTransform.ts
|
|
182
|
+
var import_chroma_js = __toESM(require("chroma-js"), 1);
|
|
183
|
+
var import_color_blind = __toESM(require("color-blind"), 1);
|
|
184
|
+
var THRESHOLD = 10;
|
|
185
|
+
var CANDIDATE_COUNT = 10;
|
|
186
|
+
var HUE_STEP = 180;
|
|
187
|
+
function generateCandidates(hex) {
|
|
188
|
+
const [baseHue] = (0, import_chroma_js.default)(hex).hcl();
|
|
189
|
+
const candidates = [];
|
|
190
|
+
candidates[0] = hex;
|
|
191
|
+
for (let i = 1; i <= CANDIDATE_COUNT / 2; i++) {
|
|
192
|
+
candidates[i * 2 - 1] = (0, import_chroma_js.default)(hex).set("hcl.h", (baseHue + HUE_STEP / CANDIDATE_COUNT * i) % 360).hex();
|
|
193
|
+
candidates[i * 2] = (0, import_chroma_js.default)(hex).set("hcl.h", (baseHue - HUE_STEP / CANDIDATE_COUNT * i + 360) % 360).hex();
|
|
194
|
+
}
|
|
195
|
+
return candidates;
|
|
196
|
+
}
|
|
197
|
+
function findOptimalColorCombination(colorKeys, baseColorsArray, candidateList, vision) {
|
|
198
|
+
const n = baseColorsArray.length;
|
|
199
|
+
let bestColors = null;
|
|
200
|
+
let minDeltaSum = Infinity;
|
|
201
|
+
let blind;
|
|
202
|
+
switch (vision) {
|
|
203
|
+
// 적색맹
|
|
204
|
+
case "protanopia":
|
|
205
|
+
blind = import_color_blind.default.protanopia;
|
|
206
|
+
break;
|
|
207
|
+
// 녹색맹
|
|
208
|
+
case "deuteranopia":
|
|
209
|
+
blind = import_color_blind.default.deuteranopia;
|
|
210
|
+
break;
|
|
211
|
+
// 청색맹
|
|
212
|
+
case "tritanopia":
|
|
213
|
+
blind = import_color_blind.default.tritanopia;
|
|
214
|
+
break;
|
|
215
|
+
default:
|
|
216
|
+
throw new Error("Invalid color blindness option");
|
|
217
|
+
}
|
|
218
|
+
function dfs(index, currentColors, totalDelta) {
|
|
219
|
+
if (index === n) {
|
|
220
|
+
if (isValidColors(currentColors, blind)) {
|
|
221
|
+
if (totalDelta < minDeltaSum) {
|
|
222
|
+
minDeltaSum = totalDelta;
|
|
223
|
+
bestColors = [...currentColors];
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
for (const candidate of candidateList[index]) {
|
|
229
|
+
const delta = import_chroma_js.default.deltaE(baseColorsArray[index], candidate);
|
|
230
|
+
if (totalDelta + delta > minDeltaSum) continue;
|
|
231
|
+
currentColors.push(candidate);
|
|
232
|
+
dfs(index + 1, currentColors, totalDelta + delta);
|
|
233
|
+
currentColors.pop();
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
dfs(0, [], 0);
|
|
237
|
+
const finalColors = bestColors ?? [...baseColorsArray];
|
|
238
|
+
return colorKeys.reduce((acc, key, idx) => {
|
|
239
|
+
acc[key] = finalColors[idx];
|
|
240
|
+
return acc;
|
|
241
|
+
}, {});
|
|
242
|
+
}
|
|
243
|
+
async function requestColorTransformation(variables) {
|
|
244
|
+
const scaleGroups = {};
|
|
245
|
+
const filteredVariables = {};
|
|
246
|
+
for (const key in variables) {
|
|
247
|
+
if (variables[key].scale) {
|
|
248
|
+
const prefixMatch = key.match(/^(--[\w-]+)-\d+$/);
|
|
249
|
+
if (!prefixMatch) {
|
|
250
|
+
filteredVariables[key] = variables[key];
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
const prefix = prefixMatch[1];
|
|
254
|
+
if (!scaleGroups[prefix]) scaleGroups[prefix] = [];
|
|
255
|
+
scaleGroups[prefix].push(key);
|
|
256
|
+
} else {
|
|
257
|
+
filteredVariables[key] = variables[key];
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
for (const prefix in scaleGroups) {
|
|
261
|
+
const keys = scaleGroups[prefix];
|
|
262
|
+
const middleKey = getMiddleScaleKey(keys);
|
|
263
|
+
if (middleKey) {
|
|
264
|
+
filteredVariables[middleKey] = variables[middleKey];
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
const colorKeys = Object.keys(filteredVariables);
|
|
268
|
+
const baseColorsArray = Object.values(filteredVariables).map((v) => v.base);
|
|
269
|
+
const candidateList = baseColorsArray.map((color) => {
|
|
270
|
+
return generateCandidates(color);
|
|
271
|
+
});
|
|
272
|
+
const colors_d = findOptimalColorCombination(colorKeys, baseColorsArray, candidateList, "deuteranopia");
|
|
273
|
+
const colors_p = findOptimalColorCombination(colorKeys, baseColorsArray, candidateList, "protanopia");
|
|
274
|
+
const colors_t = findOptimalColorCombination(colorKeys, baseColorsArray, candidateList, "tritanopia");
|
|
275
|
+
const visions = ["deuteranopia", "protanopia", "tritanopia"];
|
|
276
|
+
const colorsMap = {
|
|
277
|
+
deuteranopia: colors_d,
|
|
278
|
+
protanopia: colors_p,
|
|
279
|
+
tritanopia: colors_t
|
|
280
|
+
};
|
|
281
|
+
const themes = visions.map((vision) => ({
|
|
282
|
+
vision,
|
|
283
|
+
variables: Object.entries(colorsMap[vision]).reduce((acc, [varName, baseHex]) => {
|
|
284
|
+
acc[varName] = inferRich(varName, baseHex);
|
|
285
|
+
return acc;
|
|
286
|
+
}, {})
|
|
287
|
+
}));
|
|
288
|
+
return { themes };
|
|
289
|
+
}
|
|
290
|
+
function getMiddleScaleKey(keys) {
|
|
291
|
+
const scaleNumbers = keys.map((k) => {
|
|
292
|
+
const m = k.match(/\d+$/);
|
|
293
|
+
return m ? parseInt(m[0], 10) : null;
|
|
294
|
+
}).filter((n) => n !== null);
|
|
295
|
+
if (scaleNumbers.length === 0) return null;
|
|
296
|
+
scaleNumbers.sort((a, b) => a - b);
|
|
297
|
+
const midIndex = Math.floor(scaleNumbers.length / 2);
|
|
298
|
+
const midNumber = scaleNumbers[midIndex];
|
|
299
|
+
const middleKey = keys.find((k) => k.endsWith(midNumber.toString())) || null;
|
|
300
|
+
return middleKey;
|
|
301
|
+
}
|
|
302
|
+
function isValidColors(colors, blind) {
|
|
303
|
+
for (let i = 0; i < colors.length; i++) {
|
|
304
|
+
for (let j = i + 1; j < colors.length; j++) {
|
|
305
|
+
if (import_chroma_js.default.deltaE(blind(colors[i]), blind(colors[j])) < THRESHOLD) return false;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// src/node/runThemeApply.ts
|
|
312
|
+
var import_node_fs2 = __toESM(require("fs"), 1);
|
|
313
|
+
|
|
314
|
+
// src/node/removeExistingThemeBlocks.ts
|
|
315
|
+
function removeExistingThemeBlocks(content) {
|
|
316
|
+
const visions = ["protanopia", "deuteranopia", "tritanopia"];
|
|
317
|
+
let cleaned = content;
|
|
318
|
+
for (const vision of visions) {
|
|
319
|
+
const pattern = new RegExp(
|
|
320
|
+
`\\/\\*\\s*${vision} theme start\\s*\\*\\/[^]*?\\/\\*\\s*${vision} theme end\\s*\\*\\/`,
|
|
321
|
+
"gm"
|
|
322
|
+
);
|
|
323
|
+
cleaned = cleaned.replace(pattern, "");
|
|
324
|
+
}
|
|
325
|
+
return cleaned.trim();
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// src/node/runThemeApply.ts
|
|
329
|
+
function isBlackOrWhite(hexColor) {
|
|
330
|
+
const hex = hexColor.toLowerCase().replace("#", "");
|
|
331
|
+
const fullHex = hex.length === 3 ? hex.split("").map((char) => char + char).join("") : hex;
|
|
332
|
+
const r = parseInt(fullHex.substr(0, 2), 16);
|
|
333
|
+
const g = parseInt(fullHex.substr(2, 2), 16);
|
|
334
|
+
const b = parseInt(fullHex.substr(4, 2), 16);
|
|
335
|
+
const isWhite = r >= 250 && g >= 250 && b >= 250;
|
|
336
|
+
const isBlack = r <= 10 && g <= 10 && b <= 10;
|
|
337
|
+
return isWhite || isBlack;
|
|
338
|
+
}
|
|
339
|
+
function calculateScale(varName, hexColor) {
|
|
340
|
+
if (isBlackOrWhite(hexColor)) {
|
|
341
|
+
return false;
|
|
342
|
+
}
|
|
343
|
+
return /\d+$/.test(varName);
|
|
344
|
+
}
|
|
345
|
+
async function runThemeApply(cssPath) {
|
|
346
|
+
if (!import_node_fs2.default.existsSync(cssPath)) {
|
|
347
|
+
throw new Error(`\u274C CSS \uD30C\uC77C\uC774 \uC874\uC7AC\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4: ${cssPath}`);
|
|
348
|
+
}
|
|
349
|
+
let content = import_node_fs2.default.readFileSync(cssPath, "utf8");
|
|
350
|
+
content = removeExistingThemeBlocks(content);
|
|
351
|
+
const variables = {};
|
|
352
|
+
let match;
|
|
353
|
+
while ((match = variableRegex.exec(content)) !== null) {
|
|
354
|
+
const [, key, value] = match;
|
|
355
|
+
const cleanKey = key.trim();
|
|
356
|
+
const cleanValue = value.trim();
|
|
357
|
+
const scale = calculateScale(cleanKey, cleanValue);
|
|
358
|
+
const rich = {
|
|
359
|
+
base: cleanValue,
|
|
360
|
+
scale
|
|
361
|
+
};
|
|
362
|
+
variables[cleanKey] = rich;
|
|
363
|
+
}
|
|
364
|
+
const visions = ["deuteranopia", "protanopia", "tritanopia"];
|
|
365
|
+
try {
|
|
366
|
+
const algorithmResult = await requestColorTransformation(variables);
|
|
367
|
+
for (const themeData of algorithmResult.themes) {
|
|
368
|
+
await applyThemes(themeData, cssPath);
|
|
369
|
+
}
|
|
370
|
+
} catch (error) {
|
|
371
|
+
console.log("\u{1F680} ~ runThemeApply ~ error:", error);
|
|
372
|
+
for (const vision of visions) {
|
|
373
|
+
await applyThemes({ vision, variables }, cssPath);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
console.log(`\u2705 ${cssPath}\uC5D0 \uC0C9\uB9F9 \uD14C\uB9C8\uAC00 \uC801\uC6A9\uB418\uC5C8\uC2B5\uB2C8\uB2E4`);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// src/cli.ts
|
|
380
|
+
async function main() {
|
|
381
|
+
const flags = parseFlags();
|
|
382
|
+
const cmd = flags._[0] ?? "generate";
|
|
383
|
+
const cssPath = typeof flags.css === "string" ? flags.css : "src/index.css";
|
|
384
|
+
if (cmd === "generate") {
|
|
385
|
+
await runThemeApply(cssPath);
|
|
386
|
+
process.exit(0);
|
|
387
|
+
}
|
|
388
|
+
console.log(`\uC0AC\uC6A9 \uBC29\uBC95:
|
|
389
|
+
- cb-theme generate [--css=src/index.css]
|
|
390
|
+
CSS \uD30C\uC77C\uC5D0\uC11C \uC0C9\uC0C1 \uBCC0\uC218\uB4E4\uC744 \uCD94\uCD9C\uD558\uACE0, \uC0C9\uB9F9 \uD14C\uB9C8\uB97C \uC790\uB3D9 \uC0DD\uC131\uD558\uC5EC \uAC19\uC740 CSS \uD30C\uC77C\uC5D0 \uC801\uC6A9\uD569\uB2C8\uB2E4.
|
|
391
|
+
(\uAE30\uBCF8 \uACBD\uB85C\uB294 src/index.css)
|
|
392
|
+
`);
|
|
393
|
+
process.exit(1);
|
|
394
|
+
}
|
|
395
|
+
main().catch((e) => {
|
|
396
|
+
console.error("\u274C CLI failed:", e);
|
|
397
|
+
process.exit(1);
|
|
398
|
+
});
|
package/dist/cli.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|