@weasel-js/theme 1.5.2 → 1.6.1
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 +39 -1
- package/dist/{chunk-XFCWYN2L.js → chunk-4EIPTWJA.js} +914 -60
- package/dist/chunk-4EIPTWJA.js.map +1 -0
- package/dist/chunk-C3S4A65R.js +557 -0
- package/dist/chunk-C3S4A65R.js.map +1 -0
- package/dist/chunk-EINHFDWI.js +12 -0
- package/dist/chunk-EINHFDWI.js.map +1 -0
- package/dist/colorMode-CoklZomJ.d.ts +12 -0
- package/dist/{resolveTheme-CF8i5IHy.d.ts → definition-DLdTdFWs.d.ts} +584 -51
- package/dist/engine.d.ts +76 -8
- package/dist/engine.js +183 -427
- package/dist/engine.js.map +1 -1
- package/dist/faces.css +19 -0
- package/dist/fonts.css +4 -18
- package/dist/index.d.ts +49 -5
- package/dist/index.js +172 -20
- package/dist/index.js.map +1 -1
- package/dist/react.d.ts +50 -6
- package/dist/react.js +68 -6
- package/dist/react.js.map +1 -1
- package/dist/tokens.css +103 -1
- package/fonts/README.md +5 -6
- package/package.json +4 -3
- package/dist/chunk-LL2ERJPZ.js +0 -175
- package/dist/chunk-LL2ERJPZ.js.map +0 -1
- package/dist/chunk-PJMTWM34.js +0 -6
- package/dist/chunk-PJMTWM34.js.map +0 -1
- package/dist/chunk-XFCWYN2L.js.map +0 -1
- package/dist/theme-DAZcQi_L.d.ts +0 -191
package/dist/engine.js
CHANGED
|
@@ -1,387 +1,6 @@
|
|
|
1
|
-
import { ALPHA_EXT } from './chunk-
|
|
2
|
-
import { mergeAxes, fullSelection, pick, enumerateSelections, resolveTokens, pickAll, themeAxes, isByAxis,
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
function hexToRgb(hex) {
|
|
6
|
-
const n = Number.parseInt(hex.slice(1), 16);
|
|
7
|
-
return [n >> 16 & 255, n >> 8 & 255, n & 255];
|
|
8
|
-
}
|
|
9
|
-
var toHex = oklchDegToHex;
|
|
10
|
-
var toLch = hexToOklchDeg;
|
|
11
|
-
function chromaCap(L, H) {
|
|
12
|
-
return toLch(toHex(L, 0.5, H)).C;
|
|
13
|
-
}
|
|
14
|
-
function luminance(hex) {
|
|
15
|
-
const [r, g, b] = hexToRgb(hex).map((v) => v / 255);
|
|
16
|
-
const f = (u) => u <= 0.04045 ? u / 12.92 : ((u + 0.055) / 1.055) ** 2.4;
|
|
17
|
-
return 0.2126 * f(r) + 0.7152 * f(g) + 0.0722 * f(b);
|
|
18
|
-
}
|
|
19
|
-
function contrast(a, b) {
|
|
20
|
-
const [hi, lo] = [luminance(a), luminance(b)].sort((p, q) => q - p);
|
|
21
|
-
return (hi + 0.05) / (lo + 0.05);
|
|
22
|
-
}
|
|
23
|
-
function hueGap(a, b) {
|
|
24
|
-
const d = Math.abs(a - b) % 360;
|
|
25
|
-
return Math.min(d, 360 - d);
|
|
26
|
-
}
|
|
27
|
-
function toLab(hex) {
|
|
28
|
-
const [r, g, b] = hexToRgb(hex);
|
|
29
|
-
return srgbU8ToOklab(r, g, b);
|
|
30
|
-
}
|
|
31
|
-
var CHROMA_WEIGHT = 3;
|
|
32
|
-
function deltaE(a, b) {
|
|
33
|
-
const [l1, a1, b1] = toLab(a);
|
|
34
|
-
const [l2, a2, b2] = toLab(b);
|
|
35
|
-
return Math.hypot(l1 - l2, CHROMA_WEIGHT * (a1 - a2), CHROMA_WEIGHT * (b1 - b2));
|
|
36
|
-
}
|
|
37
|
-
function vividAt(L, H) {
|
|
38
|
-
return toHex(L, chromaCap(L, H), H);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// src/engine/color/generate.ts
|
|
42
|
-
function peakLightness(H) {
|
|
43
|
-
let bestL = 0.5;
|
|
44
|
-
let bestC = -1;
|
|
45
|
-
for (let L = 0.32; L <= 0.96; L += 0.01) {
|
|
46
|
-
const c = chromaCap(L, H);
|
|
47
|
-
if (c > bestC) {
|
|
48
|
-
bestC = c;
|
|
49
|
-
bestL = L;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return bestL;
|
|
53
|
-
}
|
|
54
|
-
var PEAK = [];
|
|
55
|
-
for (let H = 0; H < 360; H += 1) PEAK.push(peakLightness(H));
|
|
56
|
-
var peakAt = (H) => PEAK[(Math.round(H) % 360 + 360) % 360];
|
|
57
|
-
function place(H, c) {
|
|
58
|
-
const L = peakAt(H) + (c.lightnessTarget - peakAt(H)) * c.lightnessPull;
|
|
59
|
-
return toHex(L, chromaCap(L, H) * c.chromaFraction, H);
|
|
60
|
-
}
|
|
61
|
-
var HUE_NAMES = [
|
|
62
|
-
[15, "rose"],
|
|
63
|
-
[45, "red"],
|
|
64
|
-
[62, "orange"],
|
|
65
|
-
[85, "amber"],
|
|
66
|
-
[100, "gold"],
|
|
67
|
-
[122, "yellow"],
|
|
68
|
-
[138, "lime"],
|
|
69
|
-
[165, "green"],
|
|
70
|
-
[195, "teal"],
|
|
71
|
-
[218, "cyan"],
|
|
72
|
-
[245, "sky"],
|
|
73
|
-
[275, "blue"],
|
|
74
|
-
[310, "violet"],
|
|
75
|
-
[345, "fuchsia"]
|
|
76
|
-
];
|
|
77
|
-
function hueName(H) {
|
|
78
|
-
for (const [ceil, name] of HUE_NAMES) if (H < ceil) return name;
|
|
79
|
-
return "rose";
|
|
80
|
-
}
|
|
81
|
-
function uniqueNames(hues) {
|
|
82
|
-
const seen = /* @__PURE__ */ new Map();
|
|
83
|
-
return hues.map((h) => {
|
|
84
|
-
const base = hueName(h);
|
|
85
|
-
const n = (seen.get(base) ?? 0) + 1;
|
|
86
|
-
seen.set(base, n);
|
|
87
|
-
return n === 1 ? base : `${base}-${n}`;
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
function equalizeChroma(hexes, k, anchored) {
|
|
91
|
-
if (k <= 0) return hexes;
|
|
92
|
-
const lch = hexes.map(toLch);
|
|
93
|
-
const target = lch.reduce((sum, x) => sum + x.C, 0) / lch.length;
|
|
94
|
-
return lch.map((x, i) => {
|
|
95
|
-
const wanted = x.C + (target - x.C) * k;
|
|
96
|
-
if (anchored[i]) return toHex(x.L, Math.min(wanted, chromaCap(x.L, x.H)), x.H);
|
|
97
|
-
if (chromaCap(x.L, x.H) >= wanted) return toHex(x.L, wanted, x.H);
|
|
98
|
-
let bestL = x.L;
|
|
99
|
-
let bestDistance = Infinity;
|
|
100
|
-
for (let L = 0.3; L <= 0.96; L += 5e-3) {
|
|
101
|
-
if (chromaCap(L, x.H) >= wanted) {
|
|
102
|
-
const d = Math.abs(L - x.L);
|
|
103
|
-
if (d < bestDistance) {
|
|
104
|
-
bestDistance = d;
|
|
105
|
-
bestL = L;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
if (bestDistance === Infinity) {
|
|
110
|
-
const pk = peakAt(x.H);
|
|
111
|
-
return toHex(pk, chromaCap(pk, x.H), x.H);
|
|
112
|
-
}
|
|
113
|
-
return toHex(bestL, wanted, x.H);
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
function farthestFirst(hues) {
|
|
117
|
-
const order = [0];
|
|
118
|
-
while (order.length < hues.length) {
|
|
119
|
-
let best = -1;
|
|
120
|
-
let bestGap = -1;
|
|
121
|
-
for (let i = 0; i < hues.length; i += 1) {
|
|
122
|
-
if (order.includes(i)) continue;
|
|
123
|
-
const gap = Math.min(...order.map((j) => hueGap(hues[i], hues[j])));
|
|
124
|
-
if (gap > bestGap) {
|
|
125
|
-
bestGap = gap;
|
|
126
|
-
best = i;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
order.push(best);
|
|
130
|
-
}
|
|
131
|
-
return order;
|
|
132
|
-
}
|
|
133
|
-
function floorDegrees(c) {
|
|
134
|
-
return c.hueFloor <= 0 ? 0 : c.hueFloor * 360 / Math.max(1, c.count);
|
|
135
|
-
}
|
|
136
|
-
function searchHues(c) {
|
|
137
|
-
const minGapDeg = floorDegrees(c);
|
|
138
|
-
const anchorHues = c.anchors.map((a) => a.hue);
|
|
139
|
-
const anchorHexes = c.anchors.map(toHexPreview);
|
|
140
|
-
const freeCount = c.count - c.anchors.length;
|
|
141
|
-
if (freeCount < 0) return null;
|
|
142
|
-
if (freeCount === 0) return [];
|
|
143
|
-
const weighted = (hex) => {
|
|
144
|
-
const [L, a, b] = toLab(hex);
|
|
145
|
-
return [L, CHROMA_WEIGHT * a, CHROMA_WEIGHT * b];
|
|
146
|
-
};
|
|
147
|
-
const between = (p, q) => Math.hypot(p[0] - q[0], p[1] - q[1], p[2] - q[2]);
|
|
148
|
-
const placed = /* @__PURE__ */ new Map();
|
|
149
|
-
const at = (H) => {
|
|
150
|
-
const key = (Math.round(H) % 360 + 360) % 360;
|
|
151
|
-
let v = placed.get(key);
|
|
152
|
-
if (!v) {
|
|
153
|
-
const hex = place(key, c);
|
|
154
|
-
const lch = toLch(hex);
|
|
155
|
-
v = {
|
|
156
|
-
hex,
|
|
157
|
-
hue: lch.H,
|
|
158
|
-
contrast: contrast(hex, c.surface),
|
|
159
|
-
chroma: lch.C,
|
|
160
|
-
surfaceDistance: deltaE(hex, c.surface),
|
|
161
|
-
lab: weighted(hex)
|
|
162
|
-
};
|
|
163
|
-
placed.set(key, v);
|
|
164
|
-
}
|
|
165
|
-
return v;
|
|
166
|
-
};
|
|
167
|
-
const anchorMeta = anchorHexes.map((hex) => {
|
|
168
|
-
const lch = toLch(hex);
|
|
169
|
-
return {
|
|
170
|
-
hex,
|
|
171
|
-
hue: lch.H,
|
|
172
|
-
contrast: contrast(hex, c.surface),
|
|
173
|
-
chroma: lch.C,
|
|
174
|
-
surfaceDistance: deltaE(hex, c.surface),
|
|
175
|
-
lab: weighted(hex)
|
|
176
|
-
};
|
|
177
|
-
});
|
|
178
|
-
const metaFor = (free) => [...anchorMeta, ...free.map(at)];
|
|
179
|
-
const penalty = (free) => {
|
|
180
|
-
const m = metaFor(free);
|
|
181
|
-
let p = 0;
|
|
182
|
-
if (minGapDeg > 0) {
|
|
183
|
-
for (let i = 0; i < m.length; i += 1) {
|
|
184
|
-
for (let j = i + 1; j < m.length; j += 1) {
|
|
185
|
-
const short = minGapDeg - hueGap(m[i].hue, m[j].hue);
|
|
186
|
-
if (short > 0) p += short;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
if (c.minContrast > 0) {
|
|
191
|
-
for (const x of m) {
|
|
192
|
-
const short = c.minContrast - x.contrast;
|
|
193
|
-
if (short > 0) p += short * 40;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
if (c.minSurfaceDistance > 0) {
|
|
197
|
-
for (const x of m) {
|
|
198
|
-
const short = c.minSurfaceDistance - x.surfaceDistance;
|
|
199
|
-
if (short > 0) p += short * 400;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
if (c.minDistance > 0) {
|
|
203
|
-
for (let i = 0; i < m.length; i += 1) {
|
|
204
|
-
for (let j = i + 1; j < m.length; j += 1) {
|
|
205
|
-
const short = c.minDistance - between(m[i].lab, m[j].lab);
|
|
206
|
-
if (short > 0) p += short * 400;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
return p;
|
|
211
|
-
};
|
|
212
|
-
const score = (free) => {
|
|
213
|
-
const m = metaFor(free);
|
|
214
|
-
return m.reduce((sum, x) => sum + x.chroma, 0) / m.length;
|
|
215
|
-
};
|
|
216
|
-
const seeds = [];
|
|
217
|
-
const seedStep = Math.max(6, Math.round(freeCount * 1.6));
|
|
218
|
-
for (let start = 0; start < 360; start += seedStep) {
|
|
219
|
-
seeds.push(Array.from({ length: freeCount }, (_, i) => Math.round((start + i * 360 / freeCount) % 360)));
|
|
220
|
-
}
|
|
221
|
-
if (anchorHues.length > 0) {
|
|
222
|
-
const base = anchorHues[0];
|
|
223
|
-
const span = 360 - 2 * Math.max(minGapDeg, 1);
|
|
224
|
-
for (let jitter = -8; jitter <= 8; jitter += 2) {
|
|
225
|
-
for (const divisor of [Math.max(1, freeCount - 1), freeCount]) {
|
|
226
|
-
seeds.push(
|
|
227
|
-
Array.from(
|
|
228
|
-
{ length: freeCount },
|
|
229
|
-
(_, i) => Math.round(((base + minGapDeg + jitter + i * span / divisor) % 360 + 360) % 360)
|
|
230
|
-
)
|
|
231
|
-
);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
const STEPS = [-16, -8, -4, -2, -1, 1, 2, 4, 8, 16];
|
|
236
|
-
const penaltyAt = (m, i) => {
|
|
237
|
-
let p = 0;
|
|
238
|
-
const x = m[i];
|
|
239
|
-
if (c.minContrast > 0) {
|
|
240
|
-
const short = c.minContrast - x.contrast;
|
|
241
|
-
if (short > 0) p += short * 40;
|
|
242
|
-
}
|
|
243
|
-
if (c.minSurfaceDistance > 0) {
|
|
244
|
-
const short = c.minSurfaceDistance - x.surfaceDistance;
|
|
245
|
-
if (short > 0) p += short * 400;
|
|
246
|
-
}
|
|
247
|
-
for (let j = 0; j < m.length; j += 1) {
|
|
248
|
-
if (j === i) continue;
|
|
249
|
-
if (minGapDeg > 0) {
|
|
250
|
-
const short = minGapDeg - hueGap(x.hue, m[j].hue);
|
|
251
|
-
if (short > 0) p += short;
|
|
252
|
-
}
|
|
253
|
-
if (c.minDistance > 0) {
|
|
254
|
-
const short = c.minDistance - between(x.lab, m[j].lab);
|
|
255
|
-
if (short > 0) p += short * 400;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
return p;
|
|
259
|
-
};
|
|
260
|
-
const descend = (hues, useChroma, keepLegal) => {
|
|
261
|
-
const current = [...hues];
|
|
262
|
-
let meta = metaFor(current);
|
|
263
|
-
const offset = anchorMeta.length;
|
|
264
|
-
for (let iteration = 0; iteration < 120; iteration += 1) {
|
|
265
|
-
let improved = false;
|
|
266
|
-
for (let i = 0; i < freeCount; i += 1) {
|
|
267
|
-
const slot = offset + i;
|
|
268
|
-
const beforeP = penaltyAt(meta, slot);
|
|
269
|
-
const beforeC = meta[slot].chroma;
|
|
270
|
-
for (const delta of STEPS) {
|
|
271
|
-
const next = ((current[i] + delta) % 360 + 360) % 360;
|
|
272
|
-
if (next === current[i]) continue;
|
|
273
|
-
const candidate = at(next);
|
|
274
|
-
const trialMeta = meta.slice();
|
|
275
|
-
trialMeta[slot] = candidate;
|
|
276
|
-
const afterP = penaltyAt(trialMeta, slot);
|
|
277
|
-
const better = useChroma ? (keepLegal ? afterP <= beforeP : true) && candidate.chroma > beforeC : afterP < beforeP;
|
|
278
|
-
if (!better) continue;
|
|
279
|
-
if (keepLegal && afterP > beforeP) continue;
|
|
280
|
-
current[i] = next;
|
|
281
|
-
meta = trialMeta;
|
|
282
|
-
improved = true;
|
|
283
|
-
break;
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
if (!improved) break;
|
|
287
|
-
}
|
|
288
|
-
return current;
|
|
289
|
-
};
|
|
290
|
-
let best = null;
|
|
291
|
-
let bestScore = -1;
|
|
292
|
-
for (const seed of seeds) {
|
|
293
|
-
const repaired = penalty(seed) > 0 ? descend(seed, false, false) : seed;
|
|
294
|
-
if (penalty(repaired) > 0) continue;
|
|
295
|
-
const tuned = descend(repaired, true, true);
|
|
296
|
-
if (penalty(tuned) > 0) continue;
|
|
297
|
-
const s = score(tuned);
|
|
298
|
-
if (s > bestScore) {
|
|
299
|
-
bestScore = s;
|
|
300
|
-
best = tuned;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
return best;
|
|
304
|
-
}
|
|
305
|
-
function pairwiseMin(hexes) {
|
|
306
|
-
if (hexes.length < 2) return 1;
|
|
307
|
-
let min = Infinity;
|
|
308
|
-
for (let i = 0; i < hexes.length; i += 1) {
|
|
309
|
-
for (let j = i + 1; j < hexes.length; j += 1) min = Math.min(min, deltaE(hexes[i], hexes[j]));
|
|
310
|
-
}
|
|
311
|
-
return min;
|
|
312
|
-
}
|
|
313
|
-
function summarize(swatches, surface) {
|
|
314
|
-
const cs = swatches.map((s) => s.lch.C);
|
|
315
|
-
const ls = swatches.map((s) => s.lch.L);
|
|
316
|
-
let gap = 360;
|
|
317
|
-
for (let i = 0; i < swatches.length; i += 1) {
|
|
318
|
-
for (let j = i + 1; j < swatches.length; j += 1) {
|
|
319
|
-
gap = Math.min(gap, hueGap(swatches[i].lch.H, swatches[j].lch.H));
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
return {
|
|
323
|
-
meanChroma: cs.reduce((a, b) => a + b, 0) / (cs.length || 1),
|
|
324
|
-
chromaSpread: Math.max(...cs) - Math.min(...cs),
|
|
325
|
-
lightnessSpread: Math.max(...ls) - Math.min(...ls),
|
|
326
|
-
minHueGap: swatches.length > 1 ? gap : 360,
|
|
327
|
-
hueFloorShare: swatches.length > 1 ? gap * swatches.length / 360 : 1,
|
|
328
|
-
minContrast: Math.min(...swatches.map((s) => contrast(s.hex, surface))),
|
|
329
|
-
minSurfaceDistance: Math.min(...swatches.map((s) => deltaE(s.hex, surface))),
|
|
330
|
-
minDistance: pairwiseMin(swatches.map((s) => s.hex))
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
function generate(c) {
|
|
334
|
-
const found = searchHues(c);
|
|
335
|
-
const feasible = found !== null;
|
|
336
|
-
const free = found ?? Array.from(
|
|
337
|
-
{ length: Math.max(0, c.count - c.anchors.length) },
|
|
338
|
-
(_, i) => Math.round(i * 360 / Math.max(1, c.count - c.anchors.length))
|
|
339
|
-
);
|
|
340
|
-
const hues = [...c.anchors.map((a) => a.hue), ...free];
|
|
341
|
-
const anchoredFlags = [...c.anchors.map(() => true), ...free.map(() => false)];
|
|
342
|
-
const raw = [
|
|
343
|
-
...c.anchors.map(toHexPreview),
|
|
344
|
-
...free.map((h) => place(h, c))
|
|
345
|
-
];
|
|
346
|
-
const hexes = equalizeChroma(raw, c.equalize, anchoredFlags);
|
|
347
|
-
const order = c.order === "farthest" ? farthestFirst(hues) : [...hues.keys()].sort((a, b) => hues[a] - hues[b]);
|
|
348
|
-
const names = uniqueNames(order.map((i) => toLch(hexes[i]).H));
|
|
349
|
-
const swatches = order.map((i, position) => ({
|
|
350
|
-
name: c.anchors[i]?.name ?? names[position],
|
|
351
|
-
hex: hexes[i],
|
|
352
|
-
lch: toLch(hexes[i]),
|
|
353
|
-
anchored: anchoredFlags[i],
|
|
354
|
-
contrast: contrast(hexes[i], c.surface)
|
|
355
|
-
}));
|
|
356
|
-
return { swatches, stats: summarize(swatches, c.surface), feasible };
|
|
357
|
-
}
|
|
358
|
-
var DEFAULT_CONSTRAINTS = {
|
|
359
|
-
count: 10,
|
|
360
|
-
hueFloor: 0.9,
|
|
361
|
-
minContrast: 4,
|
|
362
|
-
surface: "#181a1e",
|
|
363
|
-
lightnessTarget: 0.72,
|
|
364
|
-
lightnessPull: 0.4,
|
|
365
|
-
chromaFraction: 0.95,
|
|
366
|
-
equalize: 0,
|
|
367
|
-
minSurfaceDistance: 0.25,
|
|
368
|
-
minDistance: 0.22,
|
|
369
|
-
anchors: [],
|
|
370
|
-
order: "farthest"
|
|
371
|
-
};
|
|
372
|
-
function toHexPreview(a) {
|
|
373
|
-
const cap = chromaCap(a.lightness, a.hue);
|
|
374
|
-
return toHex(a.lightness, a.chroma === void 0 ? cap : Math.min(a.chroma, cap), a.hue);
|
|
375
|
-
}
|
|
376
|
-
function anchorFromHex(hex, name) {
|
|
377
|
-
const { L, C, H } = toLch(hex);
|
|
378
|
-
return {
|
|
379
|
-
name: name ?? hueName(H),
|
|
380
|
-
hue: Math.round(H),
|
|
381
|
-
lightness: Number(L.toFixed(3)),
|
|
382
|
-
chroma: Number(C.toFixed(4))
|
|
383
|
-
};
|
|
384
|
-
}
|
|
1
|
+
import { overrideKey, AXES_EXT, ALPHA_EXT } from './chunk-EINHFDWI.js';
|
|
2
|
+
import { mergeAxes, toLch, toHex, generate, DEFAULT_CONSTRAINTS, fullSelection, pick, enumerateSelections, resolveTokens, pickAll, themeAxes, selectionKey, isByAxis, contrast } from './chunk-C3S4A65R.js';
|
|
3
|
+
export { CHROMA_WEIGHT, DEFAULT_CONSTRAINTS, anchorFromHex, chromaCap, contrast, deltaE, floorDegrees, generate, hexToRgb, hueGap, hueName, luminance, toHex, toHexPreview, toLab, toLch, vividAt } from './chunk-C3S4A65R.js';
|
|
385
4
|
|
|
386
5
|
// src/engine/steps.ts
|
|
387
6
|
var stepList = (x) => Array.isArray(x) ? [...new Set(x.filter((s) => typeof s === "string"))] : [];
|
|
@@ -422,6 +41,17 @@ function ownSteps(def) {
|
|
|
422
41
|
}
|
|
423
42
|
return names;
|
|
424
43
|
}
|
|
44
|
+
function fillFromParent(child, parent, axes) {
|
|
45
|
+
if (!isByAxis(child) || parent === void 0) return child;
|
|
46
|
+
const values = Object.keys(axes[child.by]?.values ?? {});
|
|
47
|
+
const out = { ...child };
|
|
48
|
+
for (const v of values) {
|
|
49
|
+
if (v in out) continue;
|
|
50
|
+
out[v] = isByAxis(parent) && parent.by === child.by ? parent[v] : parent;
|
|
51
|
+
if (out[v] === void 0) delete out[v];
|
|
52
|
+
}
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
425
55
|
function mergeChain(def, lookup, seen = /* @__PURE__ */ new Set()) {
|
|
426
56
|
if (seen.has(def.name)) throw new Error(`extends cycle at theme "${def.name}"`);
|
|
427
57
|
if (!def.extends) return def;
|
|
@@ -432,8 +62,13 @@ function mergeChain(def, lookup, seen = /* @__PURE__ */ new Set()) {
|
|
|
432
62
|
for (const layer of LAYERS) out[layer] = { ...parent[layer] ?? {}, ...def[layer] ?? {} };
|
|
433
63
|
const shadowed = ownSteps(def);
|
|
434
64
|
const inherited = Object.entries(parent.pins ?? {}).filter(([name]) => !shadowed.has(name));
|
|
435
|
-
|
|
436
|
-
|
|
65
|
+
const axes = mergeAxes(parent.axes ?? {}, def.axes ?? {});
|
|
66
|
+
const parentPins = parent.pins ?? {};
|
|
67
|
+
const own = Object.entries(def.pins ?? {}).map(([name, v]) => [name, fillFromParent(v, parentPins[name], axes)]);
|
|
68
|
+
out.pins = { ...Object.fromEntries(inherited), ...Object.fromEntries(own) };
|
|
69
|
+
out.axes = axes;
|
|
70
|
+
out.tones = def.tones ?? parent.tones;
|
|
71
|
+
if (out.tones === void 0) delete out.tones;
|
|
437
72
|
return out;
|
|
438
73
|
}
|
|
439
74
|
|
|
@@ -596,14 +231,23 @@ function lightnessRamp(p) {
|
|
|
596
231
|
const at = (i) => n === 1 ? 0 : i / (n - 1);
|
|
597
232
|
const lightBias = p.lightBias ?? 0;
|
|
598
233
|
const max = envelopeMax(lightBias, p.darkBias);
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
const
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
234
|
+
const anchors = p.steps.flatMap((s, i) => {
|
|
235
|
+
const hex = p.anchor?.[s];
|
|
236
|
+
if (hex === void 0) return [];
|
|
237
|
+
const { C, H } = toLch(hex);
|
|
238
|
+
return [{ i, hue: H, peak: C }];
|
|
239
|
+
});
|
|
240
|
+
const hueAndPeak = (i) => {
|
|
241
|
+
if (anchors.length === 0) return p;
|
|
242
|
+
const next = anchors.findIndex((a2) => a2.i >= i);
|
|
243
|
+
if (next === 0) return anchors[0];
|
|
244
|
+
if (next === -1) return anchors[anchors.length - 1];
|
|
245
|
+
const a = anchors[next - 1];
|
|
246
|
+
const b = anchors[next];
|
|
247
|
+
const f = (i - a.i) / (b.i - a.i);
|
|
248
|
+
const arc = ((b.hue - a.hue) % 360 + 540) % 360 - 180;
|
|
249
|
+
return { hue: ((a.hue + arc * f) % 360 + 360) % 360, peak: a.peak + (b.peak - a.peak) * f };
|
|
250
|
+
};
|
|
607
251
|
const out = {};
|
|
608
252
|
p.steps.forEach((step, i) => {
|
|
609
253
|
const fixed = p.anchor?.[step];
|
|
@@ -613,6 +257,7 @@ function lightnessRamp(p) {
|
|
|
613
257
|
}
|
|
614
258
|
const t = at(i);
|
|
615
259
|
const L = p.lightness[0] + (p.lightness[1] - p.lightness[0]) * (t + (smoothstep(t) - t) * p.curve);
|
|
260
|
+
const { hue, peak } = hueAndPeak(i);
|
|
616
261
|
out[step] = toHex(L, Math.min(peak * envelope(t, lightBias, p.darkBias) / max, MAX_CHROMA), hue);
|
|
617
262
|
});
|
|
618
263
|
return out;
|
|
@@ -1236,6 +881,7 @@ function derive(definition, selection = {}, lookup) {
|
|
|
1236
881
|
}
|
|
1237
882
|
for (const [name, v] of Object.entries(def.pins ?? {})) {
|
|
1238
883
|
const prior = has(tokens, name) ? tokens[name] : void 0;
|
|
884
|
+
if (prior && !pick(v, sel).ok) continue;
|
|
1239
885
|
const token = toToken(v, `pins.${name}`, prior);
|
|
1240
886
|
if (!token) {
|
|
1241
887
|
if (!prior) failed.add(name);
|
|
@@ -1328,7 +974,15 @@ function bakeOver(definition, merged, above, lookup) {
|
|
|
1328
974
|
const value = build(0, {});
|
|
1329
975
|
if (value !== void 0) tokens[name] = value;
|
|
1330
976
|
}
|
|
1331
|
-
|
|
977
|
+
const ramps = Object.fromEntries(Object.entries(definition.ramps ?? {}).map(([name, entry]) => [name, declaredSteps(entry)]));
|
|
978
|
+
return {
|
|
979
|
+
name: definition.name,
|
|
980
|
+
extends: definition.extends ?? null,
|
|
981
|
+
axes,
|
|
982
|
+
tokens,
|
|
983
|
+
...Object.keys(ramps).length > 0 ? { ramps } : {},
|
|
984
|
+
...definition.tones !== void 0 ? { tones: definition.tones } : {}
|
|
985
|
+
};
|
|
1332
986
|
}
|
|
1333
987
|
|
|
1334
988
|
// src/engine/emit/css.ts
|
|
@@ -1520,6 +1174,9 @@ function emitCss(themes) {
|
|
|
1520
1174
|
var REF3 = /^\{([^}.]+)\}$/;
|
|
1521
1175
|
function toDTCG(theme) {
|
|
1522
1176
|
const axes = themeAxes(theme);
|
|
1177
|
+
const defaults = fullSelection(axes);
|
|
1178
|
+
const nonMode = Object.keys(axes).filter((a) => a !== "mode");
|
|
1179
|
+
const declaredModes = Object.keys(axes.mode?.values ?? {});
|
|
1523
1180
|
const typeOf = (name) => {
|
|
1524
1181
|
for (let t = theme; t; t = t.extends) {
|
|
1525
1182
|
if (!Object.hasOwn(t.tokens, name)) continue;
|
|
@@ -1542,35 +1199,52 @@ function toDTCG(theme) {
|
|
|
1542
1199
|
...t.alpha !== void 0 ? { $extensions: { [ALPHA_EXT]: t.alpha } } : {}
|
|
1543
1200
|
};
|
|
1544
1201
|
};
|
|
1545
|
-
const
|
|
1546
|
-
if (!isByAxis(v)) return
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
if (inner !== void 0) out[mode] = inner;
|
|
1553
|
-
}
|
|
1554
|
-
return out;
|
|
1202
|
+
const scan2 = (v, used, modeKeys) => {
|
|
1203
|
+
if (!isByAxis(v)) return;
|
|
1204
|
+
used.add(v.by);
|
|
1205
|
+
for (const [k, x] of Object.entries(v)) {
|
|
1206
|
+
if (k === "by") continue;
|
|
1207
|
+
if (v.by === "mode") modeKeys.add(k);
|
|
1208
|
+
scan2(x, used, modeKeys);
|
|
1555
1209
|
}
|
|
1556
|
-
const fallback = axes[v.by]?.default;
|
|
1557
|
-
const chosen = fallback !== void 0 ? v[fallback] : void 0;
|
|
1558
|
-
return chosen === void 0 ? void 0 : flatten(chosen);
|
|
1559
|
-
};
|
|
1560
|
-
const branches = (v) => {
|
|
1561
|
-
if (!isByAxis(v)) return [];
|
|
1562
|
-
return Object.entries(v).flatMap(([mode, x]) => mode === "by" ? [] : [[mode, x]]);
|
|
1563
1210
|
};
|
|
1564
|
-
const
|
|
1565
|
-
for (const mode of
|
|
1566
|
-
const
|
|
1211
|
+
const base = { primitives: {}, modes: {} };
|
|
1212
|
+
for (const mode of declaredModes) base.modes[mode] = {};
|
|
1213
|
+
const overrides = {};
|
|
1214
|
+
const varies = {};
|
|
1567
1215
|
for (const [name, raw] of Object.entries(theme.tokens)) {
|
|
1568
|
-
const
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1216
|
+
const used = /* @__PURE__ */ new Set();
|
|
1217
|
+
const modeKeys = /* @__PURE__ */ new Set();
|
|
1218
|
+
scan2(raw, used, modeKeys);
|
|
1219
|
+
const varying = nonMode.filter((a) => used.has(a));
|
|
1220
|
+
if (varying.length > 0) varies[name] = varying;
|
|
1221
|
+
const modes = used.has("mode") ? [.../* @__PURE__ */ new Set([...declaredModes, ...modeKeys])] : [];
|
|
1222
|
+
for (const combo of enumerateSelections(Object.fromEntries(varying.map((a) => [a, axes[a]])))) {
|
|
1223
|
+
const key = overrideKey(axes, combo);
|
|
1224
|
+
const layer = key === "" ? base : overrides[key] ??= { primitives: {}, modes: {} };
|
|
1225
|
+
const sel = { ...defaults, ...combo };
|
|
1226
|
+
if (modes.length === 0) {
|
|
1227
|
+
const p = pick(raw, sel);
|
|
1228
|
+
if (p.ok) put(layer.primitives, name, p.value);
|
|
1229
|
+
continue;
|
|
1230
|
+
}
|
|
1231
|
+
const picked = modes.map((mode) => [mode, pick(raw, { ...sel, mode })]);
|
|
1232
|
+
const first = picked[0][1];
|
|
1233
|
+
if (first.ok && picked.every(([, p]) => p.ok && p.value === first.value)) {
|
|
1234
|
+
put(layer.primitives, name, first.value);
|
|
1235
|
+
continue;
|
|
1236
|
+
}
|
|
1237
|
+
for (const [mode, p] of picked) if (p.ok) put(layer.modes[mode] ??= {}, name, p.value);
|
|
1238
|
+
}
|
|
1572
1239
|
}
|
|
1573
|
-
|
|
1240
|
+
const ext = { axes, varies, overrides };
|
|
1241
|
+
return {
|
|
1242
|
+
name: theme.name,
|
|
1243
|
+
...axes.mode ? { defaultMode: axes.mode.default } : {},
|
|
1244
|
+
primitives: base.primitives,
|
|
1245
|
+
modes: base.modes,
|
|
1246
|
+
...Object.keys(axes).length > 0 ? { $extensions: { [AXES_EXT]: ext } } : {}
|
|
1247
|
+
};
|
|
1574
1248
|
}
|
|
1575
1249
|
|
|
1576
1250
|
// src/hooks.ts
|
|
@@ -1581,11 +1255,23 @@ var TOKEN_HOOKS = [
|
|
|
1581
1255
|
value: "4px",
|
|
1582
1256
|
description: "Gap between a Disclosure\u2019s twisty and its label."
|
|
1583
1257
|
},
|
|
1258
|
+
{
|
|
1259
|
+
name: "disclosure-fill",
|
|
1260
|
+
type: "color",
|
|
1261
|
+
value: "var(--wzl-accent-fg)",
|
|
1262
|
+
description: "Fill of a Disclosure\u2019s fold mark."
|
|
1263
|
+
},
|
|
1584
1264
|
{
|
|
1585
1265
|
name: "disclosure-target",
|
|
1586
1266
|
type: "dimension",
|
|
1587
1267
|
value: "20px",
|
|
1588
|
-
description: "Hit-target square of a Disclosure twisty, and of a
|
|
1268
|
+
description: "Hit-target square of a Disclosure twisty, and the twisty column of a LayerList row."
|
|
1269
|
+
},
|
|
1270
|
+
{
|
|
1271
|
+
name: "input-surface",
|
|
1272
|
+
type: "color",
|
|
1273
|
+
value: "var(--wzl-surface-sunken)",
|
|
1274
|
+
description: "Surface a text Input\u2019s frame sits on. Set it on any container whose own background is the default \u2014 a sunken rail or panel \u2014 where an unset field is the same color as what is behind it."
|
|
1589
1275
|
},
|
|
1590
1276
|
{
|
|
1591
1277
|
name: "number-field-width",
|
|
@@ -1593,6 +1279,36 @@ var TOKEN_HOOKS = [
|
|
|
1593
1279
|
value: "9ch",
|
|
1594
1280
|
description: "Width of a NumberField\u2019s input."
|
|
1595
1281
|
},
|
|
1282
|
+
{
|
|
1283
|
+
name: "params-label-align",
|
|
1284
|
+
type: "string",
|
|
1285
|
+
value: "start",
|
|
1286
|
+
description: "Text alignment of row labels in PropertyPanel, Prefs and labkit\u2019s ControlPanel."
|
|
1287
|
+
},
|
|
1288
|
+
{
|
|
1289
|
+
name: "params-label-case",
|
|
1290
|
+
type: "string",
|
|
1291
|
+
value: "uppercase",
|
|
1292
|
+
description: "text-transform of every label and title in PropertyPanel, Prefs and labkit\u2019s ControlPanel."
|
|
1293
|
+
},
|
|
1294
|
+
{
|
|
1295
|
+
name: "params-label-tracking",
|
|
1296
|
+
type: "dimension",
|
|
1297
|
+
value: "var(--wzl-tracking-wide)",
|
|
1298
|
+
description: "Letter spacing of every label and title in PropertyPanel, Prefs and labkit\u2019s ControlPanel. Unset, rows take --wzl-tracking-wide and titles --wzl-tracking-wider."
|
|
1299
|
+
},
|
|
1300
|
+
{
|
|
1301
|
+
name: "params-label-width",
|
|
1302
|
+
type: "dimension",
|
|
1303
|
+
value: "auto",
|
|
1304
|
+
description: "Inline size of row labels in PropertyPanel, Prefs and labkit\u2019s ControlPanel."
|
|
1305
|
+
},
|
|
1306
|
+
{
|
|
1307
|
+
name: "prefs-rail-width",
|
|
1308
|
+
type: "dimension",
|
|
1309
|
+
value: "216px",
|
|
1310
|
+
description: "Width of the Prefs navigation rail."
|
|
1311
|
+
},
|
|
1596
1312
|
{
|
|
1597
1313
|
name: "prefs-column-width",
|
|
1598
1314
|
type: "dimension",
|
|
@@ -1620,8 +1336,20 @@ var TOKEN_HOOKS = [
|
|
|
1620
1336
|
{
|
|
1621
1337
|
name: "property-readout-w",
|
|
1622
1338
|
type: "dimension",
|
|
1623
|
-
value: "
|
|
1624
|
-
description: "
|
|
1339
|
+
value: "calc(4.5ch + 2px)",
|
|
1340
|
+
description: "Width of a Properties row\u2019s value readout \u2014 four digits, so the controls beside a column of readouts end on one line. A range whose widest value needs more widens its own readout."
|
|
1341
|
+
},
|
|
1342
|
+
{
|
|
1343
|
+
name: "select-border",
|
|
1344
|
+
type: "border",
|
|
1345
|
+
value: "1px solid var(--wzl-border)",
|
|
1346
|
+
description: "Border of a Select\u2019s trigger. Unset, a boxed trigger draws --wzl-border and a bare one a transparent border of the same width."
|
|
1347
|
+
},
|
|
1348
|
+
{
|
|
1349
|
+
name: "select-fg",
|
|
1350
|
+
type: "color",
|
|
1351
|
+
value: "var(--wzl-fg)",
|
|
1352
|
+
description: "Text color of a Select\u2019s trigger."
|
|
1625
1353
|
},
|
|
1626
1354
|
{
|
|
1627
1355
|
name: "swatch-size",
|
|
@@ -1640,6 +1368,12 @@ var TOKEN_HOOKS = [
|
|
|
1640
1368
|
type: "dimension",
|
|
1641
1369
|
value: "32px",
|
|
1642
1370
|
description: "Width reserved for the Timeline\u2019s value axis."
|
|
1371
|
+
},
|
|
1372
|
+
{
|
|
1373
|
+
name: "tree-indent",
|
|
1374
|
+
type: "dimension",
|
|
1375
|
+
value: "calc(var(--wzl-control-h-xs) + var(--wzl-space-2))",
|
|
1376
|
+
description: "Indent of each Tree level. Unset, it is one twisty and one gap, so a child\u2019s twisty sits under its parent\u2019s label."
|
|
1643
1377
|
}
|
|
1644
1378
|
];
|
|
1645
1379
|
|
|
@@ -1725,9 +1459,9 @@ ${body}
|
|
|
1725
1459
|
return [
|
|
1726
1460
|
"// GENERATED by packages/theme/scripts/build-tokens.ts \u2014 do not edit.",
|
|
1727
1461
|
"",
|
|
1728
|
-
"import type { AxisDefs } from '../axes';",
|
|
1729
|
-
"import type { ThemeDefinition } from '../definition';",
|
|
1730
|
-
"import type { BakedTheme } from '../engine/bake';",
|
|
1462
|
+
"import type { AxisDefs } from '../axes.ts';",
|
|
1463
|
+
"import type { ThemeDefinition } from '../definition.ts';",
|
|
1464
|
+
"import type { BakedTheme } from '../engine/bake.ts';",
|
|
1731
1465
|
"",
|
|
1732
1466
|
"/** Every design token the built-in themes declare, spelled as the CSS",
|
|
1733
1467
|
" * custom property it becomes. */",
|
|
@@ -1784,6 +1518,28 @@ function generateTokens(definitions) {
|
|
|
1784
1518
|
};
|
|
1785
1519
|
}
|
|
1786
1520
|
|
|
1787
|
-
|
|
1521
|
+
// src/store.ts
|
|
1522
|
+
var serializeDefinition = (definition) => `${JSON.stringify(definition, null, 2)}
|
|
1523
|
+
`;
|
|
1524
|
+
function httpThemeApi(base = document.baseURI, fetchImpl = (input, init) => fetch(input, init)) {
|
|
1525
|
+
const url = (path) => new URL(`__theme/${path}`, base).toString();
|
|
1526
|
+
const read = async (response) => {
|
|
1527
|
+
if (!response.ok && response.status !== 409 && response.status !== 400) throw new Error(`${response.status} ${response.statusText}`);
|
|
1528
|
+
return await response.json();
|
|
1529
|
+
};
|
|
1530
|
+
return {
|
|
1531
|
+
list: async () => (await read(await fetchImpl(url("list"), void 0))).themes,
|
|
1532
|
+
get: async (name) => read(await fetchImpl(url(name), void 0)),
|
|
1533
|
+
put: async (name, definition, baseHash) => read(
|
|
1534
|
+
await fetchImpl(url(name), {
|
|
1535
|
+
method: "PUT",
|
|
1536
|
+
headers: { "Content-Type": "application/json" },
|
|
1537
|
+
body: JSON.stringify({ definition, hash: baseHash })
|
|
1538
|
+
})
|
|
1539
|
+
)
|
|
1540
|
+
};
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
export { axisDependencies, bake, bakeChain, categoricalRamp, cssValue, declaredSteps, derive, emitCss, emitManifest, emitThemes, generateTokens, httpThemeApi, lightnessRamp, mergeChain, scale, serializeDefinition, toDTCG };
|
|
1788
1544
|
//# sourceMappingURL=engine.js.map
|
|
1789
1545
|
//# sourceMappingURL=engine.js.map
|