@domandigital/craft 0.18.2 → 0.19.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.
@@ -9,7 +9,7 @@ import {
9
9
  parseFile,
10
10
  sentencesOf,
11
11
  specificity
12
- } from "./chunk-4QWEAL4G.js";
12
+ } from "./chunk-CZ7KT452.js";
13
13
  import {
14
14
  SNAPSHOT_VERSION
15
15
  } from "./chunk-QYBAVU3P.js";
@@ -237,7 +237,7 @@ function fontClass(family) {
237
237
  }
238
238
 
239
239
  // src/fingerprint/index.ts
240
- var FINGERPRINT_VERSION = 2;
240
+ var FINGERPRINT_VERSION = 3;
241
241
  var CHROMATIC = 0.03;
242
242
  var MIN_ACCENT_AREA = 2e4;
243
243
  var oklch = (value) => {
@@ -273,6 +273,24 @@ function median(values) {
273
273
  const mid = Math.floor(sorted.length / 2);
274
274
  return sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
275
275
  }
276
+ var FULL_WIDTH_IMAGE = 0.8;
277
+ function heroOf(s) {
278
+ const h1 = s.headings.find((h) => h.level === 1);
279
+ const inside = (top, x) => top >= x.top && top < x.top + x.height;
280
+ const index = (() => {
281
+ const withH1 = h1 ? s.sections.findIndex((x) => x.kind === "hero" && inside(h1.top, x)) : -1;
282
+ return withH1 >= 0 ? withH1 : s.sections.findIndex((x) => x.kind === "hero");
283
+ })();
284
+ if (index < 0) return void 0;
285
+ const section = s.sections[index];
286
+ const headline = h1 && inside(h1.top, section) ? { sizePx: h1.sizePx, weight: h1.weight, class: fontClass(normaliseFamily(h1.family)), italic: h1.italic || h1.italicPart } : null;
287
+ if (!s.images) return { headline };
288
+ const picture2 = s.images.filter((i) => (i.role === "photo" || i.role === "illustration") && (i.section === index || i.section === null && inside(i.top, section))).sort((a, b) => b.width * b.height - a.width * a.height)[0];
289
+ if (!picture2) return { headline, image: "none" };
290
+ if (picture2.left === void 0) return { headline };
291
+ const image = picture2.width >= FULL_WIDTH_IMAGE * s.viewport.width ? "full" : picture2.left + picture2.width / 2 < s.viewport.width / 2 ? "left" : "right";
292
+ return { headline, image };
293
+ }
276
294
  function fingerprint(s) {
277
295
  const effects = [];
278
296
  if (s.effects.glass > 0) effects.push("glass-panel");
@@ -284,6 +302,7 @@ function fingerprint(s) {
284
302
  if (s.effects.eyebrowChip) effects.push("hero-eyebrow-chip");
285
303
  if (s.motion.introOverlay) effects.push("intro-cinematic");
286
304
  const below = s.sections.filter((x) => x.top > s.viewport.height).length;
305
+ const hero = heroOf(s);
287
306
  return {
288
307
  version: FINGERPRINT_VERSION,
289
308
  accent: accentOf(s),
@@ -307,10 +326,28 @@ function fingerprint(s) {
307
326
  carousel: x.carousel ?? false,
308
327
  figures: x.figures ?? 0
309
328
  }))
310
- } : {}
329
+ } : {},
330
+ ...hero ? { hero } : {}
311
331
  };
312
332
  }
313
- var DISTANCE_WEIGHTS = { accent: 0.25, type: 0.25, ground: 0.1, shape: 0.1, motion: 0.1, effects: 0.1, layout: 0.1 };
333
+ var WARM_HUE = [
334
+ [330, 360],
335
+ [0, 130]
336
+ ];
337
+ var COOL_HUE = [[160, 310]];
338
+ var TEMPERATURE_FULL = 6e-3;
339
+ var TEMPERATURE_NAMED = 0.4;
340
+ function groundTemperature(o) {
341
+ const h = (o.h % 360 + 360) % 360;
342
+ const within = (bands) => bands.some(([lo, hi]) => h >= lo && h < hi);
343
+ const strength = Math.min(1, o.c / TEMPERATURE_FULL);
344
+ return within(WARM_HUE) ? strength : within(COOL_HUE) ? -strength : 0;
345
+ }
346
+ function temperatureName(o) {
347
+ const t = groundTemperature(o);
348
+ return t >= TEMPERATURE_NAMED ? "warm" : t <= -TEMPERATURE_NAMED ? "cool" : "neutral";
349
+ }
350
+ var DISTANCE_WEIGHTS = { accent: 0.2, type: 0.2, ground: 0.1, shape: 0.1, motion: 0.05, effects: 0.1, layout: 0.1, rhythm: 0.1, hero: 0.05 };
314
351
  function levenshtein(a, b, cost = (x, y) => x === y ? 0 : 1) {
315
352
  const row = Array.from({ length: b.length + 1 }, (_, i) => i);
316
353
  for (let i = 1; i <= a.length; i += 1) {
@@ -337,18 +374,62 @@ function layoutDistance(a, b) {
337
374
  return longest === 0 ? 0 : levenshtein(a.layout, b.layout) / longest;
338
375
  }
339
376
  var clamp = (n) => Math.max(0, Math.min(1, n));
377
+ function bandOf(l) {
378
+ return l < 0.4 ? "dark" : l >= 0.75 ? "light" : "mid";
379
+ }
380
+ function rhythmOf(fp) {
381
+ if (!fp.sections) return null;
382
+ const out = [];
383
+ for (const s of fp.sections) {
384
+ if (s.background === void 0) return null;
385
+ const band = bandOf((s.background ?? fp.ground).l);
386
+ if (out[out.length - 1] !== band) out.push(band);
387
+ }
388
+ return out;
389
+ }
390
+ var OPENING_BANDS = 3;
391
+ function rhythmDistance(a, b) {
392
+ const ra = rhythmOf(a);
393
+ const rb = rhythmOf(b);
394
+ if (!ra || !rb) return null;
395
+ const over = (x, y) => {
396
+ const longest = Math.max(x.length, y.length);
397
+ return longest === 0 ? 0 : levenshtein(x, y) / longest;
398
+ };
399
+ return (over(ra.slice(0, OPENING_BANDS), rb.slice(0, OPENING_BANDS)) + over(ra, rb)) / 2;
400
+ }
401
+ var HEADLINE_OCTAVES = 1;
402
+ function heroSection(fp) {
403
+ return fp.sections?.find((s) => s.role === "hero");
404
+ }
405
+ function heroDistance(a, b) {
406
+ const sa = heroSection(a);
407
+ const sb = heroSection(b);
408
+ if (!sa || !sb) return null;
409
+ const parts2 = [Math.abs(sa.centred - sb.centred), clamp(Math.abs(sa.width - sb.width) / 0.5)];
410
+ const ha = a.hero?.headline;
411
+ const hb = b.hero?.headline;
412
+ if (ha && hb) parts2.push(0.5 * (ha.class === hb.class ? 0 : 1) + 0.5 * clamp(Math.abs(Math.log2(ha.sizePx / hb.sizePx)) / HEADLINE_OCTAVES));
413
+ else if (a.hero && b.hero && (ha || hb)) parts2.push(1);
414
+ if (a.hero?.image && b.hero?.image) parts2.push(a.hero.image === b.hero.image ? 0 : 1);
415
+ return parts2.reduce((s, p) => s + p, 0) / parts2.length;
416
+ }
340
417
  function faceDistance(a, b) {
341
418
  if (a.family.toLowerCase() === b.family.toLowerCase()) return 0;
342
419
  return a.class === b.class ? 0.5 : 1;
343
420
  }
344
421
  var ACCENT_SCALE = 0.3;
345
422
  var GROUND_SCALE = 0.15;
423
+ var TEMPERATURE_WEIGHT = 0.6;
346
424
  var SHARED_PART = 0.2;
347
425
  var ACCENT_SHARED = ACCENT_SCALE * SHARED_PART;
348
426
  var GROUND_SHARED = GROUND_SCALE * SHARED_PART;
427
+ function groundDistance(a, b) {
428
+ return clamp(deltaEOk(a, b) / GROUND_SCALE + TEMPERATURE_WEIGHT * Math.abs(groundTemperature(a) - groundTemperature(b)) / 2);
429
+ }
349
430
  function fingerprintDistance(a, b) {
350
431
  const accent = a.accent && b.accent ? clamp(deltaEOk(a.accent, b.accent) / ACCENT_SCALE) : a.accent || b.accent ? 1 : 0;
351
- const ground = clamp(deltaEOk(a.ground, b.ground) / GROUND_SCALE);
432
+ const ground = groundDistance(a.ground, b.ground);
352
433
  const type = 0.6 * faceDistance(a.display, b.display) + 0.4 * faceDistance(a.body, b.body);
353
434
  const shape = a.roundness === null || b.roundness === null ? a.roundness === b.roundness ? 0 : 0.5 : clamp(Math.abs(a.roundness - b.roundness) / 0.5);
354
435
  const motion = clamp(Math.abs(a.motion - b.motion));
@@ -356,9 +437,18 @@ function fingerprintDistance(a, b) {
356
437
  const shared = a.effects.filter((e) => b.effects.includes(e)).length;
357
438
  const effects = union.size === 0 ? 0 : 1 - shared / union.size;
358
439
  const layout = layoutDistance(a, b);
359
- const parts2 = { accent, ground, type, shape, motion, effects, layout };
360
- const total = Object.keys(DISTANCE_WEIGHTS).reduce((sum, k) => sum + DISTANCE_WEIGHTS[k] * parts2[k], 0);
361
- return { total: Math.round(total * 1e3) / 1e3, parts: parts2 };
440
+ const rhythm = rhythmDistance(a, b);
441
+ const hero = heroDistance(a, b);
442
+ const parts2 = { accent, ground, type, shape, motion, effects, layout, rhythm, hero };
443
+ let sum = 0;
444
+ let weight = 0;
445
+ for (const k of Object.keys(DISTANCE_WEIGHTS)) {
446
+ const p = parts2[k];
447
+ if (p === null) continue;
448
+ sum += DISTANCE_WEIGHTS[k] * p;
449
+ weight += DISTANCE_WEIGHTS[k];
450
+ }
451
+ return { total: Math.round(sum / weight * 1e3) / 1e3, parts: parts2 };
362
452
  }
363
453
 
364
454
  // src/character/tells/source.ts
@@ -1323,6 +1413,12 @@ function groundName(o) {
1323
1413
  if (o.l >= 0.97 && o.c < 0.012) return "white";
1324
1414
  return o.c < 0.02 ? "grey" : "tinted";
1325
1415
  }
1416
+ function groundLabel(o) {
1417
+ const name = groundName(o);
1418
+ if (name === "cream" || name === "tinted") return name;
1419
+ const temperature = temperatureName(o);
1420
+ return temperature === "neutral" ? name : `${temperature} ${name}`;
1421
+ }
1326
1422
  function shapeName(roundness) {
1327
1423
  if (roundness === null) return "no buttons";
1328
1424
  if (roundness >= 0.45) return "pill";
@@ -3057,7 +3153,9 @@ export {
3057
3153
  holdHouseBlocks,
3058
3154
  normaliseFamily,
3059
3155
  fingerprint,
3156
+ temperatureName,
3060
3157
  levenshtein,
3158
+ rhythmOf,
3061
3159
  SHARED_PART,
3062
3160
  ACCENT_SHARED,
3063
3161
  GROUND_SHARED,
@@ -3068,7 +3166,7 @@ export {
3068
3166
  MIN_RUNS,
3069
3167
  typicality,
3070
3168
  hueName,
3071
- groundName,
3169
+ groundLabel,
3072
3170
  shapeName,
3073
3171
  harvest,
3074
3172
  hueSwatch,
@@ -4,15 +4,15 @@ import {
4
4
  formatPairs,
5
5
  loadEstate,
6
6
  runEstate
7
- } from "./chunk-RJVG5KA6.js";
8
- import "./chunk-6TSDYETQ.js";
9
- import "./chunk-ZUYERAFM.js";
7
+ } from "./chunk-P37LLKL4.js";
8
+ import "./chunk-4LJFZH2I.js";
9
+ import "./chunk-ORFILP5L.js";
10
10
  import "./chunk-OQXTQQAU.js";
11
- import "./chunk-O5B5ELJH.js";
11
+ import "./chunk-DUUDGR56.js";
12
12
  import "./chunk-QIEDBL4J.js";
13
13
  import "./chunk-WUACK2HX.js";
14
- import "./chunk-WFB6F3BN.js";
15
- import "./chunk-4QWEAL4G.js";
14
+ import "./chunk-WK2WMWTH.js";
15
+ import "./chunk-CZ7KT452.js";
16
16
  import "./chunk-QYBAVU3P.js";
17
17
  export {
18
18
  formatComponentPairs,
@@ -4,16 +4,16 @@ import {
4
4
  merge,
5
5
  resolvePages,
6
6
  runAudit
7
- } from "./chunk-DARKHE6A.js";
8
- import "./chunk-SL5VZJ2J.js";
7
+ } from "./chunk-37ARRJAD.js";
8
+ import "./chunk-VDUIIWG7.js";
9
9
  import "./chunk-JYNXGCYZ.js";
10
- import "./chunk-ZUYERAFM.js";
10
+ import "./chunk-ORFILP5L.js";
11
11
  import "./chunk-OQXTQQAU.js";
12
- import "./chunk-O5B5ELJH.js";
12
+ import "./chunk-DUUDGR56.js";
13
13
  import "./chunk-QIEDBL4J.js";
14
14
  import "./chunk-WUACK2HX.js";
15
- import "./chunk-WFB6F3BN.js";
16
- import "./chunk-4QWEAL4G.js";
15
+ import "./chunk-WK2WMWTH.js";
16
+ import "./chunk-CZ7KT452.js";
17
17
  import "./chunk-QYBAVU3P.js";
18
18
  export {
19
19
  describeComponents,
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  TELL_HEAVY
3
- } from "./chunk-NTIPJ5U7.js";
3
+ } from "./chunk-OKVHWXYV.js";
4
4
  import {
5
5
  expandNullTargets,
6
6
  loadNull
7
- } from "./chunk-ZUYERAFM.js";
7
+ } from "./chunk-ORFILP5L.js";
8
8
  import "./chunk-OQXTQQAU.js";
9
9
  import {
10
10
  parseFlags
11
- } from "./chunk-O5B5ELJH.js";
11
+ } from "./chunk-DUUDGR56.js";
12
12
  import {
13
13
  toJson
14
14
  } from "./chunk-QIEDBL4J.js";
@@ -21,8 +21,8 @@ import {
21
21
  fingerprint,
22
22
  scanSource,
23
23
  typicality
24
- } from "./chunk-WFB6F3BN.js";
25
- import "./chunk-4QWEAL4G.js";
24
+ } from "./chunk-WK2WMWTH.js";
25
+ import "./chunk-CZ7KT452.js";
26
26
  import {
27
27
  SNAPSHOT_VERSION
28
28
  } from "./chunk-QYBAVU3P.js";
@@ -188,7 +188,7 @@ async function runCalibrate(args, io) {
188
188
  if (toTake.length) {
189
189
  io.err(`craft calibrate: taking ${toTake.length} snapshot${toTake.length === 1 ? "" : "s"}`);
190
190
  try {
191
- const { snapshotUrls } = await import("./audit-4SFSLCKY.js");
191
+ const { snapshotUrls } = await import("./audit-43PC4LMS.js");
192
192
  const results = await snapshotUrls(toTake.map((t) => t.url));
193
193
  results.forEach((r, i) => {
194
194
  const { entry, keep } = toTake[i];
@@ -1,23 +1,23 @@
1
1
  import {
2
2
  characterReport
3
- } from "./chunk-NTIPJ5U7.js";
3
+ } from "./chunk-OKVHWXYV.js";
4
4
  import {
5
5
  merge
6
- } from "./chunk-DARKHE6A.js";
7
- import "./chunk-SL5VZJ2J.js";
6
+ } from "./chunk-37ARRJAD.js";
7
+ import "./chunk-VDUIIWG7.js";
8
8
  import "./chunk-JYNXGCYZ.js";
9
9
  import {
10
10
  SIBLING_AT,
11
11
  compareToEstate,
12
12
  loadEstate
13
- } from "./chunk-RJVG5KA6.js";
13
+ } from "./chunk-P37LLKL4.js";
14
14
  import {
15
15
  PAGE_TYPES,
16
16
  validateDirection
17
- } from "./chunk-6TSDYETQ.js";
17
+ } from "./chunk-4LJFZH2I.js";
18
18
  import {
19
19
  loadNull
20
- } from "./chunk-ZUYERAFM.js";
20
+ } from "./chunk-ORFILP5L.js";
21
21
  import "./chunk-OQXTQQAU.js";
22
22
  import {
23
23
  COPY_FILE,
@@ -28,7 +28,7 @@ import {
28
28
  loadExceptions,
29
29
  parseFlags,
30
30
  readPaths
31
- } from "./chunk-O5B5ELJH.js";
31
+ } from "./chunk-DUUDGR56.js";
32
32
  import {
33
33
  toJson
34
34
  } from "./chunk-QIEDBL4J.js";
@@ -42,8 +42,8 @@ import {
42
42
  fingerprint,
43
43
  scanSource,
44
44
  typicality
45
- } from "./chunk-WFB6F3BN.js";
46
- import "./chunk-4QWEAL4G.js";
45
+ } from "./chunk-WK2WMWTH.js";
46
+ import "./chunk-CZ7KT452.js";
47
47
  import "./chunk-QYBAVU3P.js";
48
48
 
49
49
  // src/report/cli.ts
@@ -126,7 +126,7 @@ async function runReport(command, args, io) {
126
126
  if (/\.json$/i.test(target2) && existsSync(local)) {
127
127
  snap = readSnapshot(JSON.parse(readFileSync(local, "utf8")), target2);
128
128
  } else {
129
- const { snapshotUrl } = await import("./audit-4SFSLCKY.js");
129
+ const { snapshotUrl } = await import("./audit-43PC4LMS.js");
130
130
  snap = await snapshotUrl(target2);
131
131
  }
132
132
  const fp = fingerprint(snap);
@@ -9,13 +9,13 @@ import {
9
9
  runHarvest,
10
10
  runNull,
11
11
  serveDir
12
- } from "./chunk-ZUYERAFM.js";
12
+ } from "./chunk-ORFILP5L.js";
13
13
  import "./chunk-OQXTQQAU.js";
14
- import "./chunk-O5B5ELJH.js";
14
+ import "./chunk-DUUDGR56.js";
15
15
  import "./chunk-QIEDBL4J.js";
16
16
  import "./chunk-WUACK2HX.js";
17
- import "./chunk-WFB6F3BN.js";
18
- import "./chunk-4QWEAL4G.js";
17
+ import "./chunk-WK2WMWTH.js";
18
+ import "./chunk-CZ7KT452.js";
19
19
  import "./chunk-QYBAVU3P.js";
20
20
  export {
21
21
  describeTypicality,
@@ -7,10 +7,10 @@ import {
7
7
  PROPOSED,
8
8
  researchPrompt,
9
9
  validateDirection
10
- } from "./chunk-6TSDYETQ.js";
10
+ } from "./chunk-4LJFZH2I.js";
11
11
  import {
12
12
  parseFlags
13
- } from "./chunk-O5B5ELJH.js";
13
+ } from "./chunk-DUUDGR56.js";
14
14
  import {
15
15
  toJson
16
16
  } from "./chunk-QIEDBL4J.js";
@@ -26,8 +26,8 @@ import {
26
26
  isCream,
27
27
  oklchToRgb,
28
28
  parseColour
29
- } from "./chunk-WFB6F3BN.js";
30
- import "./chunk-4QWEAL4G.js";
29
+ } from "./chunk-WK2WMWTH.js";
30
+ import "./chunk-CZ7KT452.js";
31
31
  import "./chunk-QYBAVU3P.js";
32
32
 
33
33
  // src/direction/cli.ts
@@ -208,7 +208,7 @@ var readJson = (path) => JSON.parse(readFileSync(path, "utf8"));
208
208
  async function snapshotFrom(target, cwd) {
209
209
  if (!target) return void 0;
210
210
  if (/^https?:\/\//.test(target)) {
211
- const { snapshotUrl } = await import("./audit-4SFSLCKY.js");
211
+ const { snapshotUrl } = await import("./audit-43PC4LMS.js");
212
212
  return snapshotUrl(target);
213
213
  }
214
214
  return readSnapshot(readJson(resolve(cwd, target)), target);
@@ -9,7 +9,7 @@ import {
9
9
  STOCK_PHRASES,
10
10
  countWords,
11
11
  specificity
12
- } from "./chunk-4QWEAL4G.js";
12
+ } from "./chunk-CZ7KT452.js";
13
13
 
14
14
  // src/register/cli.ts
15
15
  import { existsSync, readFileSync } from "fs";
package/dist/cli.js CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  run
4
- } from "./chunk-O5B5ELJH.js";
4
+ } from "./chunk-DUUDGR56.js";
5
5
  import "./chunk-QIEDBL4J.js";
6
- import "./chunk-WFB6F3BN.js";
7
- import "./chunk-4QWEAL4G.js";
6
+ import "./chunk-WK2WMWTH.js";
7
+ import "./chunk-CZ7KT452.js";
8
8
  import "./chunk-QYBAVU3P.js";
9
9
 
10
10
  // src/cli.ts