@domandigital/craft 0.11.0 → 0.12.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/CHANGELOG.md +16 -0
- package/CHARACTER.md +9 -1
- package/COPY.md +6 -2
- package/README.md +5 -0
- package/dist/{audit-QIPXL7E4.js → audit-5QWFLZ6L.js} +6 -3
- package/dist/audit.cjs +276 -15
- package/dist/audit.d.cts +81 -4
- package/dist/audit.d.ts +81 -4
- package/dist/audit.js +275 -15
- package/dist/{chunk-3NGWXZZE.js → chunk-AL6MRG6V.js} +40 -3
- package/dist/{chunk-HOHHQB4N.js → chunk-CDAJUS5U.js} +9 -8
- package/dist/{chunk-2AJB27BJ.js → chunk-J3QUROJ5.js} +24 -10
- package/dist/chunk-JYNXGCYZ.js +75 -0
- package/dist/{chunk-3OZQ4C5R.js → chunk-LHGY43LL.js} +8 -5
- package/dist/{chunk-MSQ57PJU.js → chunk-PU5G5DUC.js} +11 -11
- package/dist/{chunk-QMYERCBB.js → chunk-PZ4MF2VV.js} +19 -7
- package/dist/{chunk-JWHC67ZS.js → chunk-S3NZAVU6.js} +121 -1
- package/dist/{chunk-JP75P2DU.js → chunk-ULPI335S.js} +178 -12
- package/dist/chunk-ZKVJ7K6X.js +19 -0
- package/dist/cli-2DY3V3SF.js +17 -0
- package/dist/{cli-4ESXOLKV.js → cli-C3JLFXMG.js} +5 -4
- package/dist/cli-QM2AQQMF.js +19 -0
- package/dist/{cli-DBZMVYYV.js → cli-ZB2VJ7CY.js} +14 -12
- package/dist/{cli-U3SZVHSK.js → cli-ZPJIWVEP.js} +12 -78
- package/dist/cli.js +2 -2
- package/dist/index.cjs +270 -9
- package/dist/index.d.cts +180 -6
- package/dist/index.d.ts +180 -6
- package/dist/index.js +260 -9
- package/licences.json +104 -0
- package/package.json +3 -2
- package/word-lists.json +1 -1
- package/dist/cli-T5RD2BTK.js +0 -17
- package/dist/cli-VREYWCT4.js +0 -16
package/dist/index.cjs
CHANGED
|
@@ -37,13 +37,16 @@ __export(index_exports, {
|
|
|
37
37
|
DURATION_S: () => DURATION_S,
|
|
38
38
|
EASE: () => EASE,
|
|
39
39
|
EASE_TUPLE: () => EASE_TUPLE,
|
|
40
|
+
EDGE_STEP: () => EDGE_STEP,
|
|
40
41
|
ESTATE_VERSION: () => ESTATE_VERSION,
|
|
41
42
|
EXIT_RATIO: () => EXIT_RATIO,
|
|
42
43
|
FINGERPRINT_VERSION: () => FINGERPRINT_VERSION,
|
|
43
44
|
HOUSE: () => HOUSE,
|
|
44
45
|
HOUSE_BUDGET: () => HOUSE_BUDGET,
|
|
46
|
+
HOUSE_LOCKED: () => HOUSE_LOCKED,
|
|
45
47
|
HOUSE_TYPE: () => HOUSE_TYPE,
|
|
46
48
|
JSON_SCHEMA_VERSION: () => JSON_SCHEMA_VERSION,
|
|
49
|
+
LICENCES: () => LICENCES,
|
|
47
50
|
LIGHTNESS_CURVE: () => LIGHTNESS_CURVE,
|
|
48
51
|
MIN_DECIDED: () => MIN_DECIDED,
|
|
49
52
|
MIN_RUNS: () => MIN_RUNS,
|
|
@@ -53,6 +56,7 @@ __export(index_exports, {
|
|
|
53
56
|
PLAINER_WORDS: () => PLAINER_WORDS,
|
|
54
57
|
PROPOSED: () => PROPOSED,
|
|
55
58
|
RAMP_STEPS: () => RAMP_STEPS,
|
|
59
|
+
READABLE_SNAPSHOT_VERSIONS: () => READABLE_SNAPSHOT_VERSIONS,
|
|
56
60
|
REFLEX_FONTS_1: () => REFLEX_FONTS_1,
|
|
57
61
|
REFLEX_FONTS_2: () => REFLEX_FONTS_2,
|
|
58
62
|
RENDERED_PATHS: () => RENDERED_PATHS,
|
|
@@ -97,6 +101,7 @@ __export(index_exports, {
|
|
|
97
101
|
estatePairs: () => estatePairs,
|
|
98
102
|
exitDuration: () => exitDuration,
|
|
99
103
|
extractHtml: () => extractHtml,
|
|
104
|
+
faceLicence: () => faceLicence,
|
|
100
105
|
findClaims: () => findClaims,
|
|
101
106
|
findColours: () => findColours,
|
|
102
107
|
findingKey: () => findingKey,
|
|
@@ -114,6 +119,7 @@ __export(index_exports, {
|
|
|
114
119
|
groundName: () => groundName,
|
|
115
120
|
harvest: () => harvest,
|
|
116
121
|
hexToOklch: () => hexToOklch,
|
|
122
|
+
holdHouseBlocks: () => holdHouseBlocks,
|
|
117
123
|
houseRule: () => houseRule,
|
|
118
124
|
hueName: () => hueName,
|
|
119
125
|
hueSwatch: () => hueSwatch,
|
|
@@ -123,6 +129,8 @@ __export(index_exports, {
|
|
|
123
129
|
initDirection: () => initDirection,
|
|
124
130
|
isAiViolet: () => isAiViolet,
|
|
125
131
|
isCream: () => isCream,
|
|
132
|
+
layoutDistance: () => layoutDistance,
|
|
133
|
+
licencesJson: () => licencesJson,
|
|
126
134
|
makeSnapshot: () => makeSnapshot,
|
|
127
135
|
motionTokens: () => motionTokens,
|
|
128
136
|
namedSources: () => namedSources,
|
|
@@ -140,6 +148,7 @@ __export(index_exports, {
|
|
|
140
148
|
protectedFacts: () => protectedFacts,
|
|
141
149
|
ramp: () => ramp,
|
|
142
150
|
rampFromAnchors: () => rampFromAnchors,
|
|
151
|
+
readSnapshot: () => readSnapshot,
|
|
143
152
|
retrofitPlan: () => retrofitPlan,
|
|
144
153
|
runTell: () => runTell,
|
|
145
154
|
scanSource: () => scanSource,
|
|
@@ -160,6 +169,7 @@ __export(index_exports, {
|
|
|
160
169
|
typicality: () => typicality,
|
|
161
170
|
validateDirection: () => validateDirection,
|
|
162
171
|
visibleText: () => visibleText,
|
|
172
|
+
visualMeasures: () => visualMeasures,
|
|
163
173
|
wcagContrast: () => wcagContrast,
|
|
164
174
|
wordListsJson: () => wordListsJson
|
|
165
175
|
});
|
|
@@ -2909,6 +2919,7 @@ var REFLEX_FONTS_2 = [
|
|
|
2909
2919
|
"Plus Jakarta Sans",
|
|
2910
2920
|
"Outfit",
|
|
2911
2921
|
"Geist",
|
|
2922
|
+
"Cal Sans",
|
|
2912
2923
|
"Sora",
|
|
2913
2924
|
"Instrument Sans",
|
|
2914
2925
|
"Instrument Serif",
|
|
@@ -3130,7 +3141,7 @@ function iconTiles(file) {
|
|
|
3130
3141
|
return sized && has(unit, /^rounded-(?:md|lg|xl|2xl|full)$/) && has(unit, /^(?:items-center|place-items-center|place-content-center)$/) && has(unit, /^bg-/);
|
|
3131
3142
|
});
|
|
3132
3143
|
}
|
|
3133
|
-
var ICON_LIBRARY = /from\s+["'](?:lucide-react|@heroicons\/react[^"']*|react-icons[^"']*|@phosphor-icons\/react|@tabler\/icons-react|@radix-ui\/react-icons)["']/;
|
|
3144
|
+
var ICON_LIBRARY = /from\s+["'](?:lucide-react|@heroicons\/react[^"']*|react-icons[^"']*|@phosphor-icons\/react|@tabler\/icons-react|@radix-ui\/react-icons|@iconscout\/react-unicons[^"']*|@mui\/icons-material[^"']*|react-material-symbols[^"']*)["']/;
|
|
3134
3145
|
function iconTileStackHits(ctx) {
|
|
3135
3146
|
const hits = [];
|
|
3136
3147
|
for (const file of ctx.files) {
|
|
@@ -3277,6 +3288,12 @@ function shadcnHits(ctx) {
|
|
|
3277
3288
|
const detail = unused.length > 0 ? `; ${unused.length} imported nowhere` : "";
|
|
3278
3289
|
return [{ path: ui[0].path, offset: 0, message: `${ui.length} stock shadcn primitives${detail}` }];
|
|
3279
3290
|
}
|
|
3291
|
+
function shadcnCardHits(ctx) {
|
|
3292
|
+
return eachUnit(
|
|
3293
|
+
ctx,
|
|
3294
|
+
(unit) => has(unit, /^bg-card$/) && has(unit, /^text-card-foreground$/) && has(unit, /^border$/) && has(unit, /^rounded-(?:lg|xl)$/) && has(unit, /^shadow(?:-sm)?$/) ? "the registry Card recipe, unchanged" : null
|
|
3295
|
+
);
|
|
3296
|
+
}
|
|
3280
3297
|
function marqueeHits(ctx) {
|
|
3281
3298
|
const hits = [];
|
|
3282
3299
|
const pattern = /@keyframes\s+[\w-]*marquee|animate-(?:marquee|scroll-x|infinite-scroll)\b|<Marquee\b|["']react-fast-marquee["']|ui\/marquee["']|animation(?:-name)?\s*:\s*[^;]*marquee/i;
|
|
@@ -3494,6 +3511,23 @@ const inter = Inter({ subsets: ["latin"] });`),
|
|
|
3494
3511
|
pass: [f3("src/components/ui/button.tsx", `export function Button() { return null; }`)]
|
|
3495
3512
|
}
|
|
3496
3513
|
},
|
|
3514
|
+
{
|
|
3515
|
+
id: "shadcn-card-stock",
|
|
3516
|
+
name: "Stock shadcn Card",
|
|
3517
|
+
generation: 1,
|
|
3518
|
+
severity: "warn",
|
|
3519
|
+
surface: "source",
|
|
3520
|
+
why: "The registry Card (bg-card, a plain border, rounded-lg or rounded-xl, shadow-sm) is the container every generated page is built from. Left as shipped, every card on the site looks like every other shadcn site's.",
|
|
3521
|
+
fix: "Give the card this site's radius and shadow from the restraint budget, or drop the shadow and let a visible border carry it.",
|
|
3522
|
+
detect: shadcnCardHits,
|
|
3523
|
+
fixtures: {
|
|
3524
|
+
flag: [
|
|
3525
|
+
f3("components/ui/card.tsx", `className={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)}`),
|
|
3526
|
+
f3("components/ui/card.tsx", `className={cn("bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm", className)}`)
|
|
3527
|
+
],
|
|
3528
|
+
pass: [f3("components/ui/card.tsx", `className={cn("rounded-sm border-2 border-ink bg-card text-card-foreground", className)}`)]
|
|
3529
|
+
}
|
|
3530
|
+
},
|
|
3497
3531
|
{
|
|
3498
3532
|
id: "cream-palette",
|
|
3499
3533
|
name: "Cream ground",
|
|
@@ -3720,6 +3754,17 @@ var HOUSE = {
|
|
|
3720
3754
|
function houseRule(id, name = id) {
|
|
3721
3755
|
return HOUSE[id] ?? { tier: "review", label: name };
|
|
3722
3756
|
}
|
|
3757
|
+
var HOUSE_LOCKED = "the house blocking tier cannot be lowered by a repo. A genuine one-off takes copy-ok on its line; a rule that is wrong is changed in craft's house.ts and COPY.md";
|
|
3758
|
+
function holdHouseBlocks(exceptions, severity) {
|
|
3759
|
+
const blocks = (tell) => HOUSE[tell]?.tier === "block";
|
|
3760
|
+
const refusedSeverity = Object.entries(severity).filter(([tell, o]) => blocks(tell) && o.level !== "block").map(([tell]) => tell);
|
|
3761
|
+
return {
|
|
3762
|
+
exceptions: exceptions.filter((e) => !blocks(e.tell)),
|
|
3763
|
+
refused: exceptions.filter((e) => blocks(e.tell)),
|
|
3764
|
+
severity: Object.fromEntries(Object.entries(severity).filter(([tell]) => !refusedSeverity.includes(tell))),
|
|
3765
|
+
refusedSeverity
|
|
3766
|
+
};
|
|
3767
|
+
}
|
|
3723
3768
|
|
|
3724
3769
|
// src/character/parse.ts
|
|
3725
3770
|
var MARKUP = /\.(tsx|jsx|html?|astro|vue|svelte|mdx)$/i;
|
|
@@ -4053,7 +4098,7 @@ function extractStrings(file) {
|
|
|
4053
4098
|
}
|
|
4054
4099
|
|
|
4055
4100
|
// src/snapshot/types.ts
|
|
4056
|
-
var SNAPSHOT_VERSION =
|
|
4101
|
+
var SNAPSHOT_VERSION = 2;
|
|
4057
4102
|
|
|
4058
4103
|
// src/snapshot/fixture.ts
|
|
4059
4104
|
function makeSnapshot(patch = {}) {
|
|
@@ -4387,7 +4432,7 @@ var RENDERED_PATHS = {
|
|
|
4387
4432
|
};
|
|
4388
4433
|
|
|
4389
4434
|
// src/character/check.ts
|
|
4390
|
-
var CATALOGUE_VERSION = "2026.09.
|
|
4435
|
+
var CATALOGUE_VERSION = "2026.09.9";
|
|
4391
4436
|
var CATALOGUE = [...SOURCE_TELLS, ...COPY_TELLS, ...RESEARCH_TELLS, ...DENSITY_TELLS].map(
|
|
4392
4437
|
(t) => RENDERED_PATHS[t.id] ? { ...t, rendered: RENDERED_PATHS[t.id] } : t
|
|
4393
4438
|
);
|
|
@@ -4954,8 +4999,79 @@ function toJson(data) {
|
|
|
4954
4999
|
return JSON.stringify({ schemaVersion: JSON_SCHEMA_VERSION, ...data }, null, 2);
|
|
4955
5000
|
}
|
|
4956
5001
|
|
|
5002
|
+
// src/snapshot/migrate.ts
|
|
5003
|
+
var READABLE_SNAPSHOT_VERSIONS = [1, 2];
|
|
5004
|
+
function readSnapshot(data, source = "snapshot") {
|
|
5005
|
+
const v = data?.version;
|
|
5006
|
+
if (typeof data !== "object" || data === null || !READABLE_SNAPSHOT_VERSIONS.includes(v)) {
|
|
5007
|
+
throw new Error(`${source} is not a snapshot this craft reads (version ${String(v)}; reads ${READABLE_SNAPSHOT_VERSIONS.join(" and ")})`);
|
|
5008
|
+
}
|
|
5009
|
+
const snap = data;
|
|
5010
|
+
if (snap.version === 1) return { ...snap, version: SNAPSHOT_VERSION, migratedFrom: 1 };
|
|
5011
|
+
return snap;
|
|
5012
|
+
}
|
|
5013
|
+
|
|
5014
|
+
// src/snapshot/visual.ts
|
|
5015
|
+
var EDGE_STEP = 32;
|
|
5016
|
+
function visualMeasures(img, maxSamples = 1e6) {
|
|
5017
|
+
const { width: w, height: h, rgba } = img;
|
|
5018
|
+
if (w < 2 || h < 2 || rgba.length < w * h * 4) throw new Error(`visualMeasures: ${w}x${h} image with ${rgba.length} bytes`);
|
|
5019
|
+
const step = Math.max(1, Math.ceil(Math.sqrt(w * h / maxSamples)));
|
|
5020
|
+
const lum = (x, y) => {
|
|
5021
|
+
const i = (y * w + x) * 4;
|
|
5022
|
+
return 0.2126 * rgba[i] + 0.7152 * rgba[i + 1] + 0.0722 * rgba[i + 2];
|
|
5023
|
+
};
|
|
5024
|
+
let n = 0;
|
|
5025
|
+
let sumRg = 0;
|
|
5026
|
+
let sumYb = 0;
|
|
5027
|
+
let sumRg2 = 0;
|
|
5028
|
+
let sumYb2 = 0;
|
|
5029
|
+
let edges = 0;
|
|
5030
|
+
let edgeN = 0;
|
|
5031
|
+
let asym = 0;
|
|
5032
|
+
let asymN = 0;
|
|
5033
|
+
for (let y = 0; y < h; y += step) {
|
|
5034
|
+
for (let x = 0; x < w; x += step) {
|
|
5035
|
+
const i = (y * w + x) * 4;
|
|
5036
|
+
const r = rgba[i];
|
|
5037
|
+
const g = rgba[i + 1];
|
|
5038
|
+
const b = rgba[i + 2];
|
|
5039
|
+
const rg = r - g;
|
|
5040
|
+
const yb = 0.5 * (r + g) - b;
|
|
5041
|
+
sumRg += rg;
|
|
5042
|
+
sumYb += yb;
|
|
5043
|
+
sumRg2 += rg * rg;
|
|
5044
|
+
sumYb2 += yb * yb;
|
|
5045
|
+
n += 1;
|
|
5046
|
+
const l = lum(x, y);
|
|
5047
|
+
if (x + step < w && y + step < h) {
|
|
5048
|
+
if (Math.abs(lum(x + step, y) - l) + Math.abs(lum(x, y + step) - l) > EDGE_STEP) edges += 1;
|
|
5049
|
+
edgeN += 1;
|
|
5050
|
+
}
|
|
5051
|
+
if (x < w / 2) {
|
|
5052
|
+
asym += Math.abs(l - lum(w - 1 - x, y));
|
|
5053
|
+
asymN += 1;
|
|
5054
|
+
}
|
|
5055
|
+
}
|
|
5056
|
+
}
|
|
5057
|
+
const meanRg = sumRg / n;
|
|
5058
|
+
const meanYb = sumYb / n;
|
|
5059
|
+
const sdRg = Math.sqrt(Math.max(0, sumRg2 / n - meanRg * meanRg));
|
|
5060
|
+
const sdYb = Math.sqrt(Math.max(0, sumYb2 / n - meanYb * meanYb));
|
|
5061
|
+
const colourfulness = Math.sqrt(sdRg ** 2 + sdYb ** 2) + 0.3 * Math.sqrt(meanRg ** 2 + meanYb ** 2);
|
|
5062
|
+
const round2 = (v, d = 3) => Math.round(v * 10 ** d) / 10 ** d;
|
|
5063
|
+
return {
|
|
5064
|
+
colourfulness: round2(colourfulness, 1),
|
|
5065
|
+
edgeDensity: round2(edgeN ? edges / edgeN : 0),
|
|
5066
|
+
symmetry: round2(asymN ? 1 - asym / asymN / 255 : 1),
|
|
5067
|
+
width: w,
|
|
5068
|
+
height: h,
|
|
5069
|
+
sampled: n
|
|
5070
|
+
};
|
|
5071
|
+
}
|
|
5072
|
+
|
|
4957
5073
|
// src/fingerprint/index.ts
|
|
4958
|
-
var FINGERPRINT_VERSION =
|
|
5074
|
+
var FINGERPRINT_VERSION = 2;
|
|
4959
5075
|
var CHROMATIC = 0.03;
|
|
4960
5076
|
var MIN_ACCENT_AREA = 2e4;
|
|
4961
5077
|
var oklch = (value) => {
|
|
@@ -5010,23 +5126,36 @@ function fingerprint(s) {
|
|
|
5010
5126
|
roundness: median(s.controls.map((c) => Math.min(0.5, c.radiusPx / Math.max(1, c.heightPx)))),
|
|
5011
5127
|
motion: below ? Math.min(1, s.motion.hiddenSections / below) : 0,
|
|
5012
5128
|
effects: effects.sort(),
|
|
5013
|
-
layout: s.sections.map((x) => x.kind)
|
|
5129
|
+
layout: s.sections.map((x) => x.kind),
|
|
5130
|
+
...s.sections.length > 0 && s.sections.every((x) => x.role && x.geometry) ? { sections: s.sections.map((x) => ({ role: x.role, centred: x.geometry.centredShare, width: x.geometry.contentWidthRatio })) } : {}
|
|
5014
5131
|
};
|
|
5015
5132
|
}
|
|
5016
5133
|
var DISTANCE_WEIGHTS = { accent: 0.25, type: 0.25, ground: 0.1, shape: 0.1, motion: 0.1, effects: 0.1, layout: 0.1 };
|
|
5017
|
-
function levenshtein(a, b) {
|
|
5134
|
+
function levenshtein(a, b, cost = (x, y) => x === y ? 0 : 1) {
|
|
5018
5135
|
const row = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
5019
5136
|
for (let i = 1; i <= a.length; i += 1) {
|
|
5020
5137
|
let prev = row[0];
|
|
5021
5138
|
row[0] = i;
|
|
5022
5139
|
for (let j = 1; j <= b.length; j += 1) {
|
|
5023
5140
|
const cur = row[j];
|
|
5024
|
-
row[j] = Math.min(row[j] + 1, row[j - 1] + 1, prev + (a[i - 1]
|
|
5141
|
+
row[j] = Math.min(row[j] + 1, row[j - 1] + 1, prev + cost(a[i - 1], b[j - 1]));
|
|
5025
5142
|
prev = cur;
|
|
5026
5143
|
}
|
|
5027
5144
|
}
|
|
5028
5145
|
return row[b.length];
|
|
5029
5146
|
}
|
|
5147
|
+
function sectionCost(x, y) {
|
|
5148
|
+
if (x.role !== y.role) return 1;
|
|
5149
|
+
return Math.min(0.5, (Math.abs(x.centred - y.centred) + Math.abs(x.width - y.width)) / 2);
|
|
5150
|
+
}
|
|
5151
|
+
function layoutDistance(a, b) {
|
|
5152
|
+
if (a.sections && b.sections) {
|
|
5153
|
+
const longest2 = Math.max(a.sections.length, b.sections.length);
|
|
5154
|
+
return longest2 === 0 ? 0 : levenshtein(a.sections, b.sections, sectionCost) / longest2;
|
|
5155
|
+
}
|
|
5156
|
+
const longest = Math.max(a.layout.length, b.layout.length);
|
|
5157
|
+
return longest === 0 ? 0 : levenshtein(a.layout, b.layout) / longest;
|
|
5158
|
+
}
|
|
5030
5159
|
var clamp = (n) => Math.max(0, Math.min(1, n));
|
|
5031
5160
|
function faceDistance(a, b) {
|
|
5032
5161
|
if (a.family.toLowerCase() === b.family.toLowerCase()) return 0;
|
|
@@ -5041,8 +5170,7 @@ function fingerprintDistance(a, b) {
|
|
|
5041
5170
|
const union = /* @__PURE__ */ new Set([...a.effects, ...b.effects]);
|
|
5042
5171
|
const shared = a.effects.filter((e) => b.effects.includes(e)).length;
|
|
5043
5172
|
const effects = union.size === 0 ? 0 : 1 - shared / union.size;
|
|
5044
|
-
const
|
|
5045
|
-
const layout = longest === 0 ? 0 : levenshtein(a.layout, b.layout) / longest;
|
|
5173
|
+
const layout = layoutDistance(a, b);
|
|
5046
5174
|
const parts2 = { accent, ground, type, shape, motion, effects, layout };
|
|
5047
5175
|
const total = Object.keys(DISTANCE_WEIGHTS).reduce((sum, k) => sum + DISTANCE_WEIGHTS[k] * parts2[k], 0);
|
|
5048
5176
|
return { total: Math.round(total * 1e3) / 1e3, parts: parts2 };
|
|
@@ -5070,6 +5198,117 @@ var SOURCE_KINDS = [
|
|
|
5070
5198
|
];
|
|
5071
5199
|
var CHOICE_KEYS = ["accent", "ground", "display", "body", "shape", "motif", "signature"];
|
|
5072
5200
|
|
|
5201
|
+
// src/direction/licences.ts
|
|
5202
|
+
var LICENCES = [
|
|
5203
|
+
{
|
|
5204
|
+
name: "Basteleur",
|
|
5205
|
+
subject: "face",
|
|
5206
|
+
licence: "OFL-1.1",
|
|
5207
|
+
multiClient: "yes",
|
|
5208
|
+
checked: "2026-09-24",
|
|
5209
|
+
source: "https://velvetyne.fr/fonts/basteleur/",
|
|
5210
|
+
note: "Velvetyne, designed by Keussel. Keep the OFL notice with the font files."
|
|
5211
|
+
},
|
|
5212
|
+
{
|
|
5213
|
+
name: "Gulax",
|
|
5214
|
+
subject: "face",
|
|
5215
|
+
licence: "OFL-1.1",
|
|
5216
|
+
multiClient: "yes",
|
|
5217
|
+
checked: "2026-09-24",
|
|
5218
|
+
source: "https://velvetyne.fr/fonts/gulax/",
|
|
5219
|
+
note: "Velvetyne. Keep the OFL notice with the font files."
|
|
5220
|
+
},
|
|
5221
|
+
{
|
|
5222
|
+
name: "Le Murmure",
|
|
5223
|
+
subject: "face",
|
|
5224
|
+
licence: "OFL-1.1",
|
|
5225
|
+
multiClient: "yes",
|
|
5226
|
+
checked: "2026-09-24",
|
|
5227
|
+
source: "https://velvetyne.fr/fonts/le-murmure/",
|
|
5228
|
+
note: "Velvetyne. Keep the OFL notice with the font files."
|
|
5229
|
+
},
|
|
5230
|
+
{
|
|
5231
|
+
name: "Blaze Type",
|
|
5232
|
+
subject: "foundry",
|
|
5233
|
+
licence: "commercial",
|
|
5234
|
+
multiClient: "per-site",
|
|
5235
|
+
cap: 10,
|
|
5236
|
+
checked: "2026-09-24",
|
|
5237
|
+
source: "https://blazetype.eu/eula",
|
|
5238
|
+
note: "The EULA sells a professional licence for 1 to 10 websites, and lets an agency buy in a client's name at the listed price. Its /license page says one licence covers every domain; the EULA is the contract, so buy per client."
|
|
5239
|
+
},
|
|
5240
|
+
{
|
|
5241
|
+
name: "Power Type",
|
|
5242
|
+
subject: "foundry",
|
|
5243
|
+
licence: "commercial",
|
|
5244
|
+
multiClient: "unverified",
|
|
5245
|
+
checked: "2026-09-24",
|
|
5246
|
+
source: "https://power-type.com/eula",
|
|
5247
|
+
note: "Research on 2026-09-11 reported a Super License capped at 10 self-hosted domains. The EULA page did not load when checked; read it before relying on the cap."
|
|
5248
|
+
},
|
|
5249
|
+
{
|
|
5250
|
+
name: "Grilli Type",
|
|
5251
|
+
subject: "foundry",
|
|
5252
|
+
licence: "commercial",
|
|
5253
|
+
multiClient: "per-site",
|
|
5254
|
+
checked: "2026-09-24",
|
|
5255
|
+
source: "https://www.grillitype.com/information",
|
|
5256
|
+
note: "Web licences cover any number of domains controlled by the licensee, priced by monthly visitors, so a client site needs the client's own licence. Unlimited tiers start at $10,000 per style."
|
|
5257
|
+
},
|
|
5258
|
+
{
|
|
5259
|
+
name: "Phosphor",
|
|
5260
|
+
subject: "icons",
|
|
5261
|
+
licence: "MIT",
|
|
5262
|
+
multiClient: "yes",
|
|
5263
|
+
checked: "2026-09-24",
|
|
5264
|
+
source: "https://github.com/phosphor-icons/core/blob/main/LICENSE",
|
|
5265
|
+
note: "Six weights. The icon-tile tells treat it exactly as they treat Lucide: the library is not the tell, the tinted tile is."
|
|
5266
|
+
},
|
|
5267
|
+
{
|
|
5268
|
+
name: "Tabler Icons",
|
|
5269
|
+
subject: "icons",
|
|
5270
|
+
licence: "MIT",
|
|
5271
|
+
multiClient: "yes",
|
|
5272
|
+
checked: "2026-09-24",
|
|
5273
|
+
source: "https://github.com/tabler/tabler-icons/blob/main/LICENSE",
|
|
5274
|
+
note: "One stroke grid across the set."
|
|
5275
|
+
},
|
|
5276
|
+
{
|
|
5277
|
+
name: "Iconoir",
|
|
5278
|
+
subject: "icons",
|
|
5279
|
+
licence: "MIT",
|
|
5280
|
+
multiClient: "yes",
|
|
5281
|
+
checked: "2026-09-24",
|
|
5282
|
+
source: "https://github.com/iconoir-icons/iconoir/blob/main/LICENSE",
|
|
5283
|
+
note: "Smaller set, no paid tier."
|
|
5284
|
+
},
|
|
5285
|
+
{
|
|
5286
|
+
name: "Unicons",
|
|
5287
|
+
subject: "icons",
|
|
5288
|
+
licence: "IconScout Simple License",
|
|
5289
|
+
multiClient: "yes",
|
|
5290
|
+
checked: "2026-09-24",
|
|
5291
|
+
source: "https://github.com/Iconscout/unicons/blob/master/LICENSE",
|
|
5292
|
+
note: "IconScout's free set, personal and commercial use, attribution optional."
|
|
5293
|
+
},
|
|
5294
|
+
{
|
|
5295
|
+
name: "IconScout premium",
|
|
5296
|
+
subject: "icons",
|
|
5297
|
+
licence: "commercial",
|
|
5298
|
+
multiClient: "per-site",
|
|
5299
|
+
checked: "2026-09-24",
|
|
5300
|
+
source: "https://iconscout.com/licenses",
|
|
5301
|
+
note: "Each licence covers a single end product. Never in a tool, template or shared kit (create-site, craft, a house component library), even modified. Record the assets each site uses. Assets come from different contributors, so take one site's set from one contributor."
|
|
5302
|
+
}
|
|
5303
|
+
];
|
|
5304
|
+
function faceLicence(family) {
|
|
5305
|
+
const name = family.trim().toLowerCase();
|
|
5306
|
+
return LICENCES.find((l) => l.subject === "face" && l.name.toLowerCase() === name);
|
|
5307
|
+
}
|
|
5308
|
+
function licencesJson() {
|
|
5309
|
+
return JSON.stringify({ licences: LICENCES }, null, 2) + "\n";
|
|
5310
|
+
}
|
|
5311
|
+
|
|
5073
5312
|
// src/direction/validate.ts
|
|
5074
5313
|
var PREFERENCE = /\b(?:client (?:likes|liked|wants|wanted|asked|chose|prefers?)|(?:we|they|i) (?:like|liked|love|loved|prefer)|on[- ]trend|trendy|popular|what (?:everyone|competitors) (?:uses?|does)|brand (?:colou?r|font|guidelines?) (?:is|are|says?))\b/i;
|
|
5075
5314
|
var MOOD = /\b(?:modern|clean|fresh|professional|trustworthy|premium|elegant|sleek|timeless|minimal(?:ist)?|bold|vibrant|friendly|approachable|luxur(?:y|ious)|sophisticated|contemporary|stylish|warm|inviting|high[- ]end)\b/gi;
|
|
@@ -5193,6 +5432,18 @@ function validateDirection(input, ctx = {}) {
|
|
|
5193
5432
|
ok = false;
|
|
5194
5433
|
}
|
|
5195
5434
|
}
|
|
5435
|
+
if ((key === "display" || key === "body") && typeof c.value === "string" && c.value.trim()) {
|
|
5436
|
+
const family = normaliseFamily(c.value);
|
|
5437
|
+
const entry = faceLicence(family);
|
|
5438
|
+
if (!entry) {
|
|
5439
|
+
warn(`${at}.value`, `${family}: licence not in craft's register. Read the licensor's own terms for use on this client's site, then add it to src/direction/licences.ts.`);
|
|
5440
|
+
} else if (entry.multiClient === "unverified") {
|
|
5441
|
+
warn(`${at}.value`, `${family}: licence unverified (${entry.note})`);
|
|
5442
|
+
} else if (entry.multiClient === "capped" || entry.multiClient === "per-site") {
|
|
5443
|
+
const scope = entry.multiClient === "capped" ? `covers up to ${entry.cap} sites` : "needs a licence for this site, in the client's name";
|
|
5444
|
+
warn(`${at}.value`, `${family}: ${entry.licence} licence ${scope}. Record the purchase before launch.`);
|
|
5445
|
+
}
|
|
5446
|
+
}
|
|
5196
5447
|
const fp = ctx.fingerprint;
|
|
5197
5448
|
if (fp && typeof c.value === "string") {
|
|
5198
5449
|
if ((key === "accent" || key === "ground") && parseColour(c.value)) {
|
|
@@ -5863,13 +6114,16 @@ function retrofitPlan(report2, direction) {
|
|
|
5863
6114
|
DURATION_S,
|
|
5864
6115
|
EASE,
|
|
5865
6116
|
EASE_TUPLE,
|
|
6117
|
+
EDGE_STEP,
|
|
5866
6118
|
ESTATE_VERSION,
|
|
5867
6119
|
EXIT_RATIO,
|
|
5868
6120
|
FINGERPRINT_VERSION,
|
|
5869
6121
|
HOUSE,
|
|
5870
6122
|
HOUSE_BUDGET,
|
|
6123
|
+
HOUSE_LOCKED,
|
|
5871
6124
|
HOUSE_TYPE,
|
|
5872
6125
|
JSON_SCHEMA_VERSION,
|
|
6126
|
+
LICENCES,
|
|
5873
6127
|
LIGHTNESS_CURVE,
|
|
5874
6128
|
MIN_DECIDED,
|
|
5875
6129
|
MIN_RUNS,
|
|
@@ -5879,6 +6133,7 @@ function retrofitPlan(report2, direction) {
|
|
|
5879
6133
|
PLAINER_WORDS,
|
|
5880
6134
|
PROPOSED,
|
|
5881
6135
|
RAMP_STEPS,
|
|
6136
|
+
READABLE_SNAPSHOT_VERSIONS,
|
|
5882
6137
|
REFLEX_FONTS_1,
|
|
5883
6138
|
REFLEX_FONTS_2,
|
|
5884
6139
|
RENDERED_PATHS,
|
|
@@ -5923,6 +6178,7 @@ function retrofitPlan(report2, direction) {
|
|
|
5923
6178
|
estatePairs,
|
|
5924
6179
|
exitDuration,
|
|
5925
6180
|
extractHtml,
|
|
6181
|
+
faceLicence,
|
|
5926
6182
|
findClaims,
|
|
5927
6183
|
findColours,
|
|
5928
6184
|
findingKey,
|
|
@@ -5940,6 +6196,7 @@ function retrofitPlan(report2, direction) {
|
|
|
5940
6196
|
groundName,
|
|
5941
6197
|
harvest,
|
|
5942
6198
|
hexToOklch,
|
|
6199
|
+
holdHouseBlocks,
|
|
5943
6200
|
houseRule,
|
|
5944
6201
|
hueName,
|
|
5945
6202
|
hueSwatch,
|
|
@@ -5949,6 +6206,8 @@ function retrofitPlan(report2, direction) {
|
|
|
5949
6206
|
initDirection,
|
|
5950
6207
|
isAiViolet,
|
|
5951
6208
|
isCream,
|
|
6209
|
+
layoutDistance,
|
|
6210
|
+
licencesJson,
|
|
5952
6211
|
makeSnapshot,
|
|
5953
6212
|
motionTokens,
|
|
5954
6213
|
namedSources,
|
|
@@ -5966,6 +6225,7 @@ function retrofitPlan(report2, direction) {
|
|
|
5966
6225
|
protectedFacts,
|
|
5967
6226
|
ramp,
|
|
5968
6227
|
rampFromAnchors,
|
|
6228
|
+
readSnapshot,
|
|
5969
6229
|
retrofitPlan,
|
|
5970
6230
|
runTell,
|
|
5971
6231
|
scanSource,
|
|
@@ -5986,6 +6246,7 @@ function retrofitPlan(report2, direction) {
|
|
|
5986
6246
|
typicality,
|
|
5987
6247
|
validateDirection,
|
|
5988
6248
|
visibleText,
|
|
6249
|
+
visualMeasures,
|
|
5989
6250
|
wcagContrast,
|
|
5990
6251
|
wordListsJson
|
|
5991
6252
|
});
|