@weasel-js/theme 1.9.3 → 1.9.4

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.
@@ -247,8 +247,8 @@ declare const THEMES: {
247
247
  readonly '--wzl-slider-thumb-size': "8px";
248
248
  readonly '--wzl-slider-track-slop': "6px";
249
249
  readonly '--wzl-slider-thumb-slop': "6px";
250
- readonly '--wzl-slider-track-mix': "18%";
251
- readonly '--wzl-slider-thumb-mix': "70%";
250
+ readonly '--wzl-slider-track-mix': "40%";
251
+ readonly '--wzl-slider-thumb-mix': "100%";
252
252
  readonly '--wzl-handle-size': "9px";
253
253
  readonly '--wzl-handle-size-sm': "7px";
254
254
  readonly '--wzl-handle-size-lg': "10px";
@@ -425,8 +425,8 @@ declare const THEMES: {
425
425
  readonly '--wzl-slider-thumb-size': "8px";
426
426
  readonly '--wzl-slider-track-slop': "6px";
427
427
  readonly '--wzl-slider-thumb-slop': "6px";
428
- readonly '--wzl-slider-track-mix': "18%";
429
- readonly '--wzl-slider-thumb-mix': "70%";
428
+ readonly '--wzl-slider-track-mix': "40%";
429
+ readonly '--wzl-slider-thumb-mix': "100%";
430
430
  readonly '--wzl-handle-size': "9px";
431
431
  readonly '--wzl-handle-size-sm': "7px";
432
432
  readonly '--wzl-handle-size-lg': "10px";
@@ -603,8 +603,8 @@ declare const THEMES: {
603
603
  readonly '--wzl-slider-thumb-size': "8px";
604
604
  readonly '--wzl-slider-track-slop': "6px";
605
605
  readonly '--wzl-slider-thumb-slop': "6px";
606
- readonly '--wzl-slider-track-mix': "18%";
607
- readonly '--wzl-slider-thumb-mix': "70%";
606
+ readonly '--wzl-slider-track-mix': "40%";
607
+ readonly '--wzl-slider-thumb-mix': "100%";
608
608
  readonly '--wzl-handle-size': "9px";
609
609
  readonly '--wzl-handle-size-sm': "7px";
610
610
  readonly '--wzl-handle-size-lg': "10px";
@@ -1221,7 +1221,8 @@ declare function themeAxes(theme: Theme): AxisDefs;
1221
1221
  /**
1222
1222
  * Merge the extends chain at a selection, resolve every alias, and key the
1223
1223
  * result by CSS custom-property name. A missing or unknown axis value takes the
1224
- * axis default.
1224
+ * axis default, and a theme in the chain reads a value it never declared at its
1225
+ * own default for that axis.
1225
1226
  *
1226
1227
  * Pure — no DOM. An unresolvable reference throws rather than falling back.
1227
1228
  */
package/dist/engine.d.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  import { OklchDeg, oklchDegToHex } from '@weasel-js/paint';
2
- import { T as ThemeDefinition, L as Lookup, F as FlatTokens, R as RawToken, S as Selection, B as BakedTheme, A as AxisDefs, a as Theme } from './definition-DACR1H4A.js';
3
- export { C as CategoricalRampDef, b as ContrastRule, c as LightnessRampDef, d as LiteralRule, N as NumberParam, O as OffsetRule, P as PinObject, e as PinValue, f as RampDef, g as RefRule, h as ScaleDef, i as SemanticRule, j as StepRule, k as bake, l as bakeChain, m as mergeChain } from './definition-DACR1H4A.js';
2
+ import { T as ThemeDefinition, L as Lookup, F as FlatTokens, R as RawToken, S as Selection, B as BakedTheme, A as AxisDefs, a as Theme } from './definition-GS7_Kz9D.js';
3
+ export { C as CategoricalRampDef, b as ContrastRule, c as LightnessRampDef, d as LiteralRule, N as NumberParam, O as OffsetRule, P as PinObject, e as PinValue, f as RampDef, g as RefRule, h as ScaleDef, i as SemanticRule, j as StepRule, k as bake, l as bakeChain, m as mergeChain } from './definition-GS7_Kz9D.js';
4
4
 
5
5
  /** A color in OKLCH: lightness 0–1, chroma, hue in degrees. paint's own type,
6
6
  * under the name this engine has always called it. */
7
7
  type Lch = OklchDeg;
8
- /** `#rrggbb` to 0–255 channels. */
9
- declare function hexToRgb(hex: string): [number, number, number];
8
+ /** A hex color to 0–255 channels, its alpha dropped. Throws on anything else. */
9
+ declare const hexToRgb: (hex: string) => [number, number, number];
10
10
  /** OKLCH (lightness 0–1, chroma, hue in degrees) to `#rrggbb`. Chroma past the gamut is clipped at constant lightness. */
11
11
  declare const toHex: typeof oklchDegToHex;
12
12
  /** `#rrggbb` to OKLCH, hue in 0–360. */
package/dist/engine.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { overrideKey, AXES_EXT, ALPHA_EXT } from './chunk-Z675UQ26.js';
2
- import { mergeAxes, toLch, toHex, generate, DEFAULT_CONSTRAINTS, fullSelection, pick, enumerateSelections, resolveTokens, pickAll, themeAxes, selectionKey, isByAxis, contrast } from './chunk-NUILKH2Z.js';
3
- export { CHROMA_WEIGHT, DEFAULT_CONSTRAINTS, anchorFromHex, chromaCap, contrast, deltaE, floorDegrees, generate, hexToRgb, hueGap, hueName, luminance, toHex, toHexPreview, toLab, toLch, vividAt } from './chunk-NUILKH2Z.js';
2
+ import { mergeAxes, toLch, toHex, generate, DEFAULT_CONSTRAINTS, fullSelection, pick, enumerateSelections, resolveTokens, mixAlpha, pickAll, themeAxes, tokensAt, selectionKey, isByAxis, contrast, tokenAt } from './chunk-7LCRXZDY.js';
3
+ export { CHROMA_WEIGHT, DEFAULT_CONSTRAINTS, anchorFromHex, chromaCap, contrast, deltaE, floorDegrees, generate, hexToRgb, hueGap, hueName, luminance, toHex, toHexPreview, toLab, toLch, vividAt } from './chunk-7LCRXZDY.js';
4
+ import { isPlainObject } from '@weasel-js/registry';
4
5
 
5
6
  // src/engine/steps.ts
6
7
  var stepList = (x) => Array.isArray(x) ? [...new Set(x.filter((s) => typeof s === "string"))] : [];
@@ -217,6 +218,20 @@ function axisDependencies(definition, lookup) {
217
218
  return result;
218
219
  }
219
220
 
221
+ // src/engine/axisIssues.ts
222
+ function axisIssues(axes) {
223
+ const issues = [];
224
+ for (const [name, def] of Object.entries(axes)) {
225
+ const values = Object.keys(def.values ?? {});
226
+ if (values.length === 0) {
227
+ issues.push({ kind: "invalid", path: `axes.${name}`, message: `axis "${name}" has no values` });
228
+ } else if (!values.includes(def.default)) {
229
+ issues.push({ kind: "invalid", path: `axes.${name}`, message: `axis "${name}" defaults to "${def.default}", which is not one of its values (${values.join(", ")})` });
230
+ }
231
+ }
232
+ return issues;
233
+ }
234
+
220
235
  // src/engine/ramps.ts
221
236
  var MAX_CHROMA = 0.4;
222
237
  var smoothstep = (t) => t * t * (3 - 2 * t);
@@ -290,12 +305,10 @@ function scale(steps, p) {
290
305
  });
291
306
  return out;
292
307
  }
293
-
294
- // src/engine/semantics.ts
295
308
  var TOKEN_REF2 = /^\{([^}.]+)\}$/;
296
309
  var HEX = /^#[0-9a-f]{6}$/i;
297
310
  var MAX_DEPTH = 32;
298
- var isRecord = (x) => typeof x === "object" && x !== null && !Array.isArray(x);
311
+ var isRecord = isPlainObject;
299
312
  var refTarget = (value) => typeof value === "string" ? TOKEN_REF2.exec(value.trim())?.[1] : void 0;
300
313
  function deriveSemantics(rules, ctx) {
301
314
  const done = /* @__PURE__ */ new Map();
@@ -577,12 +590,12 @@ var Reader = class {
577
590
  }
578
591
  record(x, path) {
579
592
  if (x === void 0) return {};
580
- if (typeof x === "object" && x !== null && !Array.isArray(x)) return x;
593
+ if (isPlainObject(x)) return x;
581
594
  this.fail(path, "expected an object");
582
595
  return {};
583
596
  }
584
597
  };
585
- var isRecord2 = (x) => typeof x === "object" && x !== null && !Array.isArray(x);
598
+ var isRecord2 = isPlainObject;
586
599
  var stepNames = (x) => Array.isArray(x) && x.every((s) => typeof s === "string") ? x : void 0;
587
600
  function rampColors(name, r, steps, ctx) {
588
601
  const path = `ramps.${name}`;
@@ -726,7 +739,7 @@ function inferTypes(tokens, provenance, failed, issues) {
726
739
  issues.push({ kind: "untyped-pin", token: name });
727
740
  }
728
741
  }
729
- var isPinObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v) && "value" in v;
742
+ var isPinObject = (v) => isPlainObject(v) && "value" in v;
730
743
  var NAMED_LAYERS = ["ramps", "scales", "semantics", "components", "pins"];
731
744
  var SAFE_NAME = /^[A-Za-z0-9_-]+$/;
732
745
  var isSafeName = (name) => SAFE_NAME.test(name);
@@ -746,6 +759,7 @@ function withoutUnsafeNames(def, issues) {
746
759
  function derive(definition, selection = {}, lookup) {
747
760
  const issues = [];
748
761
  const def = withoutUnsafeNames(mergeChain(definition, lookup), issues);
762
+ issues.push(...axisIssues(def.axes ?? {}));
749
763
  const sel = fullSelection(def.axes ?? {}, selection);
750
764
  const seeds = {};
751
765
  for (const [k, v] of Object.entries(def.seeds ?? {})) {
@@ -937,23 +951,14 @@ function bakeOver(definition, merged, above, lookup) {
937
951
  const axes = merged.axes ?? {};
938
952
  const own = table(definition, axes, lookup);
939
953
  const at = (sel, name) => own.get(selectionKey(axes, sel))?.[name];
940
- const inherited = (sel, name) => {
941
- const full = fullSelection(axes, sel);
942
- for (let i = above.length - 1; i >= 0; i--) {
943
- const tokens2 = above[i].tokens;
944
- if (!Object.hasOwn(tokens2, name)) continue;
945
- const picked = pick(tokens2[name], full);
946
- if (picked.ok) return picked.value;
947
- }
948
- return void 0;
949
- };
954
+ const inherited = (sel, name) => tokenAt(above, name, fullSelection(axes, sel));
950
955
  const produced = new Set([...own.values()].flatMap((t) => Object.keys(t)));
951
956
  const ordered = definitionOrder(merged).filter((n) => produced.has(n));
952
957
  const names = [...ordered, ...[...produced].filter((n) => !ordered.includes(n))];
953
958
  const selections = enumerateSelections(axes);
954
959
  const tokens = {};
955
960
  for (const name of names) {
956
- if (selections.every((sel) => same(at(sel, name), inherited(sel, name)))) continue;
961
+ if (selections.every((sel) => at(sel, name) === void 0 || same(at(sel, name), inherited(sel, name)))) continue;
957
962
  const varying = Object.keys(axes).filter(
958
963
  (axis) => selections.some((sel) => Object.keys(axes[axis].values).some((v) => !same(at(sel, name), at({ ...sel, [axis]: v }, name))))
959
964
  );
@@ -998,7 +1003,7 @@ function cssValue(name, token) {
998
1003
  const target = refTarget2(token);
999
1004
  if (target === null) return resolveTokens({ [name]: token })[name];
1000
1005
  if (token.alpha === void 0) return `var(--wzl-${target})`;
1001
- return `color-mix(in srgb, var(--wzl-${target}) ${Math.round(token.alpha * 100)}%, transparent)`;
1006
+ return mixAlpha(`var(--wzl-${target})`, token.alpha);
1002
1007
  }
1003
1008
  function axisSets(axes) {
1004
1009
  const names = Object.keys(axes);
@@ -1030,21 +1035,16 @@ function combineAxes(own, other) {
1030
1035
  var Resolved = class {
1031
1036
  axes;
1032
1037
  names;
1038
+ /** Root first. */
1033
1039
  chain;
1034
1040
  memo = /* @__PURE__ */ new Map();
1035
1041
  constructor(axes, chain) {
1036
1042
  this.axes = axes;
1037
1043
  this.chain = chain;
1038
- this.names = [...new Set([...chain].reverse().flatMap((t) => Object.keys(t.tokens)))];
1044
+ this.names = [...new Set(chain.flatMap((t) => Object.keys(t.tokens)))];
1039
1045
  }
1040
1046
  token(name, sel) {
1041
- const full = fullSelection(this.axes, sel);
1042
- for (const t of this.chain) {
1043
- if (!Object.hasOwn(t.tokens, name)) continue;
1044
- const picked = pick(t.tokens[name], full);
1045
- if (picked.ok) return picked.value;
1046
- }
1047
- return void 0;
1047
+ return tokenAt(this.chain, name, fullSelection(this.axes, sel));
1048
1048
  }
1049
1049
  /** What a browser computes: every `var()` substituted, down to literals. */
1050
1050
  expanded(name, sel, stack = []) {
@@ -1064,7 +1064,7 @@ function chainOf(baked, byName) {
1064
1064
  if (!baked.extends) return [baked];
1065
1065
  const parent = byName.get(baked.extends);
1066
1066
  if (!parent) throw new Error(`Theme "${baked.name}" extends "${baked.extends}", which is not being emitted`);
1067
- return [baked, ...chainOf(parent, byName)];
1067
+ return [...chainOf(parent, byName), baked];
1068
1068
  }
1069
1069
  var union = (axes, ...sets) => {
1070
1070
  const all = new Set(sets.flatMap((s) => s ?? []));
@@ -1380,6 +1380,48 @@ var TOKEN_HOOKS = [
1380
1380
  type: "dimension",
1381
1381
  value: "calc(var(--wzl-control-h-xs) + var(--wzl-space-2))",
1382
1382
  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."
1383
+ },
1384
+ {
1385
+ name: "tree-font-size",
1386
+ type: "dimension",
1387
+ value: "var(--wzl-font-size-sm)",
1388
+ description: "Size of a Tree row\u2019s text."
1389
+ },
1390
+ {
1391
+ name: "tree-fold-size",
1392
+ type: "dimension",
1393
+ value: "16px",
1394
+ description: "Size of the leading glyph a Tree folds a branch by, where its branches fold by their glyphs."
1395
+ },
1396
+ {
1397
+ name: "tree-guide",
1398
+ type: "color",
1399
+ value: "var(--wzl-fg-subtle)",
1400
+ description: "Color of the bracket a Tree draws down a branch."
1401
+ },
1402
+ {
1403
+ name: "dialog-max-width",
1404
+ type: "dimension",
1405
+ value: "900px",
1406
+ description: "Widest a Dialog grows, modal or inline, before its contents wrap or scroll."
1407
+ },
1408
+ {
1409
+ name: "prefs-across-gap",
1410
+ type: "dimension",
1411
+ value: "28px",
1412
+ description: "Gap between the two columns of rows a PrefsForm pane sets side by side."
1413
+ },
1414
+ {
1415
+ name: "prefs-control-min",
1416
+ type: "dimension",
1417
+ value: "80px",
1418
+ description: "Narrowest a PrefsForm row\u2019s control gets in a two-across pane before the row wraps it under its label."
1419
+ },
1420
+ {
1421
+ name: "prop-panel-actions-basis",
1422
+ type: "dimension",
1423
+ value: "auto",
1424
+ description: "Width a PropertyPanel\u2019s title row gives its actions. Set, it holds that width whatever the actions are."
1383
1425
  }
1384
1426
  ];
1385
1427
 
@@ -1434,9 +1476,6 @@ function chainOf2(baked, byName) {
1434
1476
  if (baked.extends && !parent) throw new Error(`Theme "${baked.name}" extends "${baked.extends}", which is not in themes/`);
1435
1477
  return [...parent ? chainOf2(parent, byName) : [], baked];
1436
1478
  }
1437
- function tokensAt(chain, sel) {
1438
- return Object.assign({}, ...chain.map((t) => pickAll(t.tokens, sel)));
1439
- }
1440
1479
  function emitThemes(themes) {
1441
1480
  const byName = new Map(themes.map((t) => [t.baked.name, t.baked]));
1442
1481
  const names = /* @__PURE__ */ new Set();
@@ -1506,11 +1545,12 @@ function generateTokens(definitions) {
1506
1545
  const roots = definitions.filter((d) => !d.extends);
1507
1546
  if (roots.length !== 1) throw new Error(`themes/ needs exactly one theme that extends nothing; found ${roots.length}`);
1508
1547
  const ordered = [roots[0], ...definitions.filter((d) => d.extends)];
1509
- const problems = ordered.flatMap(
1510
- (def) => enumerateSelections(mergeChain(def, lookup).axes ?? {}).flatMap(
1548
+ const problems = ordered.flatMap((def) => {
1549
+ const selections = enumerateSelections(mergeChain(def, lookup).axes ?? {});
1550
+ return (selections.length > 0 ? selections : [{}]).flatMap(
1511
1551
  (sel) => derive(def, sel, lookup).issues.map((issue) => `${def.name} ${JSON.stringify(sel)}: ${JSON.stringify(issue)}`)
1512
- )
1513
- );
1552
+ );
1553
+ });
1514
1554
  if (problems.length > 0) return { ok: false, problems };
1515
1555
  const themes = ordered.map((definition) => ({
1516
1556
  definition,