@whittlelabs/sifter 0.25.0 → 0.26.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.
Files changed (3) hide show
  1. package/bin.js +63 -1
  2. package/bin.js.map +2 -2
  3. package/package.json +1 -1
package/bin.js CHANGED
@@ -19502,10 +19502,45 @@ var require_output_gates = __commonJS({
19502
19502
  "../../packages/shuttle/dist/executors/output-gates.js"(exports2) {
19503
19503
  "use strict";
19504
19504
  Object.defineProperty(exports2, "__esModule", { value: true });
19505
+ exports2.isSanctionedPaint = isSanctionedPaint;
19506
+ exports2.unsanctionedPaints = unsanctionedPaints;
19505
19507
  exports2.resolvePath = resolvePath;
19506
19508
  exports2.evaluateOutputGates = evaluateOutputGates;
19507
19509
  exports2.renderRepairPrompt = renderRepairPrompt;
19508
19510
  var INPUT_REF = "input:";
19511
+ var COLOR_BEARING = ["fill", "stroke", "color", "stop-color", "flood-color", "lighting-color"];
19512
+ var INERT = /* @__PURE__ */ new Set(["none", "inherit", "initial", "unset", "transparent", "currentcolor"]);
19513
+ function isSanctionedPaint(raw) {
19514
+ const value = raw.trim().toLowerCase();
19515
+ if (value === "")
19516
+ return true;
19517
+ if (INERT.has(value))
19518
+ return true;
19519
+ if (/^url\(\s*['"]?#[^)'"]+['"]?\s*\)$/.test(value))
19520
+ return true;
19521
+ const varMatch = /^var\(\s*(--[a-z0-9-]+)\s*(?:,([\s\S]*))?\)$/.exec(value);
19522
+ if (!varMatch)
19523
+ return false;
19524
+ const [, name, fallback] = varMatch;
19525
+ if (!name.startsWith("--color-"))
19526
+ return false;
19527
+ return fallback === void 0 || isSanctionedPaint(fallback);
19528
+ }
19529
+ function unsanctionedPaints(source) {
19530
+ const found = [];
19531
+ const attrs = COLOR_BEARING.join("|");
19532
+ const attribute = new RegExp(`\\b(?:${attrs})\\s*=\\s*(['"])([\\s\\S]*?)\\1`, "gi");
19533
+ const declaration = new RegExp(`\\b(?:${attrs})\\s*:\\s*([^;"'}\\n]+)`, "gi");
19534
+ for (const [, , value] of source.matchAll(attribute)) {
19535
+ if (!isSanctionedPaint(value))
19536
+ found.push(value.trim());
19537
+ }
19538
+ for (const [, value] of source.matchAll(declaration)) {
19539
+ if (!isSanctionedPaint(value))
19540
+ found.push(value.trim());
19541
+ }
19542
+ return found;
19543
+ }
19509
19544
  function resolvePath(root, path) {
19510
19545
  let ctx = root;
19511
19546
  for (const seg of path.split(".")) {
@@ -19593,6 +19628,33 @@ var require_output_gates = __commonJS({
19593
19628
  return [
19594
19629
  `'${path}' must be a non-empty array${cond}; it was ${Array.isArray(value) ? "empty" : "absent"}.`
19595
19630
  ];
19631
+ },
19632
+ /**
19633
+ * `bounded_palette` — every colour in the sources at `path` must be named
19634
+ * by role rather than written as a value. Params:
19635
+ * `{ path: <output path>, roles?: <role names for the repair turn> }`.
19636
+ *
19637
+ * A drawing the model authors is markup the house did not write, and a
19638
+ * literal in it is a colour outside the consuming palette: unproved
19639
+ * against the colours beside it, unchecked for contrast on the ground it
19640
+ * lands on, and fixed to one theme. The consumer strips such a paint at
19641
+ * render, so an artifact that carries one silently loses it. Catching it
19642
+ * HERE is what makes that a repair the model can make while it still has
19643
+ * the drawing in hand, instead of a defect nobody sees until the picture
19644
+ * comes out plain.
19645
+ */
19646
+ bounded_palette(params, output) {
19647
+ const path = String(params.path);
19648
+ const value = resolvePath(output, path);
19649
+ const sources = (Array.isArray(value) ? value : [value]).filter((v) => typeof v === "string" && v.length > 0);
19650
+ const offenders = [...new Set(sources.flatMap(unsanctionedPaints))];
19651
+ if (offenders.length === 0)
19652
+ return [];
19653
+ const roles = Array.isArray(params.roles) ? params.roles.map(String) : [];
19654
+ const vocabulary = roles.length > 0 ? ` Use one of: ${roles.join(", ")}.` : "";
19655
+ return [
19656
+ `'${path}' states ${offenders.length} colour value${offenders.length === 1 ? "" : "s"} directly (${offenders.slice(0, 8).join(", ")}${offenders.length > 8 ? ", \u2026" : ""}). Every colour must be named by role \u2014 var(--color-\u2026), currentColor, or none \u2014 never as a hex, rgb(), hsl() or colour name, because a value cannot adapt to the reader's theme and was never checked against the colours beside it.${vocabulary} Redraw with roles and return the whole output again.`
19657
+ ];
19596
19658
  }
19597
19659
  };
19598
19660
  function evaluateOutputGates(gates, output, inputs) {
@@ -22371,7 +22433,7 @@ var import_path = require("path");
22371
22433
  var import_promises = require("fs/promises");
22372
22434
  var import_yaml = __toESM(require_dist());
22373
22435
  var import_shuttle = __toESM(require_dist5());
22374
- var buildVersion = true ? "0.25.0" : pkg.version;
22436
+ var buildVersion = true ? "0.26.0" : pkg.version;
22375
22437
  var sifterBrand = {
22376
22438
  product: {
22377
22439
  id: "sifter",