@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.js
CHANGED
|
@@ -2742,6 +2742,7 @@ var REFLEX_FONTS_2 = [
|
|
|
2742
2742
|
"Plus Jakarta Sans",
|
|
2743
2743
|
"Outfit",
|
|
2744
2744
|
"Geist",
|
|
2745
|
+
"Cal Sans",
|
|
2745
2746
|
"Sora",
|
|
2746
2747
|
"Instrument Sans",
|
|
2747
2748
|
"Instrument Serif",
|
|
@@ -2963,7 +2964,7 @@ function iconTiles(file) {
|
|
|
2963
2964
|
return sized && has(unit, /^rounded-(?:md|lg|xl|2xl|full)$/) && has(unit, /^(?:items-center|place-items-center|place-content-center)$/) && has(unit, /^bg-/);
|
|
2964
2965
|
});
|
|
2965
2966
|
}
|
|
2966
|
-
var ICON_LIBRARY = /from\s+["'](?:lucide-react|@heroicons\/react[^"']*|react-icons[^"']*|@phosphor-icons\/react|@tabler\/icons-react|@radix-ui\/react-icons)["']/;
|
|
2967
|
+
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[^"']*)["']/;
|
|
2967
2968
|
function iconTileStackHits(ctx) {
|
|
2968
2969
|
const hits = [];
|
|
2969
2970
|
for (const file of ctx.files) {
|
|
@@ -3110,6 +3111,12 @@ function shadcnHits(ctx) {
|
|
|
3110
3111
|
const detail = unused.length > 0 ? `; ${unused.length} imported nowhere` : "";
|
|
3111
3112
|
return [{ path: ui[0].path, offset: 0, message: `${ui.length} stock shadcn primitives${detail}` }];
|
|
3112
3113
|
}
|
|
3114
|
+
function shadcnCardHits(ctx) {
|
|
3115
|
+
return eachUnit(
|
|
3116
|
+
ctx,
|
|
3117
|
+
(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
|
|
3118
|
+
);
|
|
3119
|
+
}
|
|
3113
3120
|
function marqueeHits(ctx) {
|
|
3114
3121
|
const hits = [];
|
|
3115
3122
|
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;
|
|
@@ -3327,6 +3334,23 @@ const inter = Inter({ subsets: ["latin"] });`),
|
|
|
3327
3334
|
pass: [f3("src/components/ui/button.tsx", `export function Button() { return null; }`)]
|
|
3328
3335
|
}
|
|
3329
3336
|
},
|
|
3337
|
+
{
|
|
3338
|
+
id: "shadcn-card-stock",
|
|
3339
|
+
name: "Stock shadcn Card",
|
|
3340
|
+
generation: 1,
|
|
3341
|
+
severity: "warn",
|
|
3342
|
+
surface: "source",
|
|
3343
|
+
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.",
|
|
3344
|
+
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.",
|
|
3345
|
+
detect: shadcnCardHits,
|
|
3346
|
+
fixtures: {
|
|
3347
|
+
flag: [
|
|
3348
|
+
f3("components/ui/card.tsx", `className={cn("rounded-lg border bg-card text-card-foreground shadow-sm", className)}`),
|
|
3349
|
+
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)}`)
|
|
3350
|
+
],
|
|
3351
|
+
pass: [f3("components/ui/card.tsx", `className={cn("rounded-sm border-2 border-ink bg-card text-card-foreground", className)}`)]
|
|
3352
|
+
}
|
|
3353
|
+
},
|
|
3330
3354
|
{
|
|
3331
3355
|
id: "cream-palette",
|
|
3332
3356
|
name: "Cream ground",
|
|
@@ -3553,6 +3577,17 @@ var HOUSE = {
|
|
|
3553
3577
|
function houseRule(id, name = id) {
|
|
3554
3578
|
return HOUSE[id] ?? { tier: "review", label: name };
|
|
3555
3579
|
}
|
|
3580
|
+
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";
|
|
3581
|
+
function holdHouseBlocks(exceptions, severity) {
|
|
3582
|
+
const blocks = (tell) => HOUSE[tell]?.tier === "block";
|
|
3583
|
+
const refusedSeverity = Object.entries(severity).filter(([tell, o]) => blocks(tell) && o.level !== "block").map(([tell]) => tell);
|
|
3584
|
+
return {
|
|
3585
|
+
exceptions: exceptions.filter((e) => !blocks(e.tell)),
|
|
3586
|
+
refused: exceptions.filter((e) => blocks(e.tell)),
|
|
3587
|
+
severity: Object.fromEntries(Object.entries(severity).filter(([tell]) => !refusedSeverity.includes(tell))),
|
|
3588
|
+
refusedSeverity
|
|
3589
|
+
};
|
|
3590
|
+
}
|
|
3556
3591
|
|
|
3557
3592
|
// src/character/parse.ts
|
|
3558
3593
|
var MARKUP = /\.(tsx|jsx|html?|astro|vue|svelte|mdx)$/i;
|
|
@@ -3886,7 +3921,7 @@ function extractStrings(file) {
|
|
|
3886
3921
|
}
|
|
3887
3922
|
|
|
3888
3923
|
// src/snapshot/types.ts
|
|
3889
|
-
var SNAPSHOT_VERSION =
|
|
3924
|
+
var SNAPSHOT_VERSION = 2;
|
|
3890
3925
|
|
|
3891
3926
|
// src/snapshot/fixture.ts
|
|
3892
3927
|
function makeSnapshot(patch = {}) {
|
|
@@ -4220,7 +4255,7 @@ var RENDERED_PATHS = {
|
|
|
4220
4255
|
};
|
|
4221
4256
|
|
|
4222
4257
|
// src/character/check.ts
|
|
4223
|
-
var CATALOGUE_VERSION = "2026.09.
|
|
4258
|
+
var CATALOGUE_VERSION = "2026.09.9";
|
|
4224
4259
|
var CATALOGUE = [...SOURCE_TELLS, ...COPY_TELLS, ...RESEARCH_TELLS, ...DENSITY_TELLS].map(
|
|
4225
4260
|
(t) => RENDERED_PATHS[t.id] ? { ...t, rendered: RENDERED_PATHS[t.id] } : t
|
|
4226
4261
|
);
|
|
@@ -4787,8 +4822,79 @@ function toJson(data) {
|
|
|
4787
4822
|
return JSON.stringify({ schemaVersion: JSON_SCHEMA_VERSION, ...data }, null, 2);
|
|
4788
4823
|
}
|
|
4789
4824
|
|
|
4825
|
+
// src/snapshot/migrate.ts
|
|
4826
|
+
var READABLE_SNAPSHOT_VERSIONS = [1, 2];
|
|
4827
|
+
function readSnapshot(data, source = "snapshot") {
|
|
4828
|
+
const v = data?.version;
|
|
4829
|
+
if (typeof data !== "object" || data === null || !READABLE_SNAPSHOT_VERSIONS.includes(v)) {
|
|
4830
|
+
throw new Error(`${source} is not a snapshot this craft reads (version ${String(v)}; reads ${READABLE_SNAPSHOT_VERSIONS.join(" and ")})`);
|
|
4831
|
+
}
|
|
4832
|
+
const snap = data;
|
|
4833
|
+
if (snap.version === 1) return { ...snap, version: SNAPSHOT_VERSION, migratedFrom: 1 };
|
|
4834
|
+
return snap;
|
|
4835
|
+
}
|
|
4836
|
+
|
|
4837
|
+
// src/snapshot/visual.ts
|
|
4838
|
+
var EDGE_STEP = 32;
|
|
4839
|
+
function visualMeasures(img, maxSamples = 1e6) {
|
|
4840
|
+
const { width: w, height: h, rgba } = img;
|
|
4841
|
+
if (w < 2 || h < 2 || rgba.length < w * h * 4) throw new Error(`visualMeasures: ${w}x${h} image with ${rgba.length} bytes`);
|
|
4842
|
+
const step = Math.max(1, Math.ceil(Math.sqrt(w * h / maxSamples)));
|
|
4843
|
+
const lum = (x, y) => {
|
|
4844
|
+
const i = (y * w + x) * 4;
|
|
4845
|
+
return 0.2126 * rgba[i] + 0.7152 * rgba[i + 1] + 0.0722 * rgba[i + 2];
|
|
4846
|
+
};
|
|
4847
|
+
let n = 0;
|
|
4848
|
+
let sumRg = 0;
|
|
4849
|
+
let sumYb = 0;
|
|
4850
|
+
let sumRg2 = 0;
|
|
4851
|
+
let sumYb2 = 0;
|
|
4852
|
+
let edges = 0;
|
|
4853
|
+
let edgeN = 0;
|
|
4854
|
+
let asym = 0;
|
|
4855
|
+
let asymN = 0;
|
|
4856
|
+
for (let y = 0; y < h; y += step) {
|
|
4857
|
+
for (let x = 0; x < w; x += step) {
|
|
4858
|
+
const i = (y * w + x) * 4;
|
|
4859
|
+
const r = rgba[i];
|
|
4860
|
+
const g = rgba[i + 1];
|
|
4861
|
+
const b = rgba[i + 2];
|
|
4862
|
+
const rg = r - g;
|
|
4863
|
+
const yb = 0.5 * (r + g) - b;
|
|
4864
|
+
sumRg += rg;
|
|
4865
|
+
sumYb += yb;
|
|
4866
|
+
sumRg2 += rg * rg;
|
|
4867
|
+
sumYb2 += yb * yb;
|
|
4868
|
+
n += 1;
|
|
4869
|
+
const l = lum(x, y);
|
|
4870
|
+
if (x + step < w && y + step < h) {
|
|
4871
|
+
if (Math.abs(lum(x + step, y) - l) + Math.abs(lum(x, y + step) - l) > EDGE_STEP) edges += 1;
|
|
4872
|
+
edgeN += 1;
|
|
4873
|
+
}
|
|
4874
|
+
if (x < w / 2) {
|
|
4875
|
+
asym += Math.abs(l - lum(w - 1 - x, y));
|
|
4876
|
+
asymN += 1;
|
|
4877
|
+
}
|
|
4878
|
+
}
|
|
4879
|
+
}
|
|
4880
|
+
const meanRg = sumRg / n;
|
|
4881
|
+
const meanYb = sumYb / n;
|
|
4882
|
+
const sdRg = Math.sqrt(Math.max(0, sumRg2 / n - meanRg * meanRg));
|
|
4883
|
+
const sdYb = Math.sqrt(Math.max(0, sumYb2 / n - meanYb * meanYb));
|
|
4884
|
+
const colourfulness = Math.sqrt(sdRg ** 2 + sdYb ** 2) + 0.3 * Math.sqrt(meanRg ** 2 + meanYb ** 2);
|
|
4885
|
+
const round2 = (v, d = 3) => Math.round(v * 10 ** d) / 10 ** d;
|
|
4886
|
+
return {
|
|
4887
|
+
colourfulness: round2(colourfulness, 1),
|
|
4888
|
+
edgeDensity: round2(edgeN ? edges / edgeN : 0),
|
|
4889
|
+
symmetry: round2(asymN ? 1 - asym / asymN / 255 : 1),
|
|
4890
|
+
width: w,
|
|
4891
|
+
height: h,
|
|
4892
|
+
sampled: n
|
|
4893
|
+
};
|
|
4894
|
+
}
|
|
4895
|
+
|
|
4790
4896
|
// src/fingerprint/index.ts
|
|
4791
|
-
var FINGERPRINT_VERSION =
|
|
4897
|
+
var FINGERPRINT_VERSION = 2;
|
|
4792
4898
|
var CHROMATIC = 0.03;
|
|
4793
4899
|
var MIN_ACCENT_AREA = 2e4;
|
|
4794
4900
|
var oklch = (value) => {
|
|
@@ -4843,23 +4949,36 @@ function fingerprint(s) {
|
|
|
4843
4949
|
roundness: median(s.controls.map((c) => Math.min(0.5, c.radiusPx / Math.max(1, c.heightPx)))),
|
|
4844
4950
|
motion: below ? Math.min(1, s.motion.hiddenSections / below) : 0,
|
|
4845
4951
|
effects: effects.sort(),
|
|
4846
|
-
layout: s.sections.map((x) => x.kind)
|
|
4952
|
+
layout: s.sections.map((x) => x.kind),
|
|
4953
|
+
...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 })) } : {}
|
|
4847
4954
|
};
|
|
4848
4955
|
}
|
|
4849
4956
|
var DISTANCE_WEIGHTS = { accent: 0.25, type: 0.25, ground: 0.1, shape: 0.1, motion: 0.1, effects: 0.1, layout: 0.1 };
|
|
4850
|
-
function levenshtein(a, b) {
|
|
4957
|
+
function levenshtein(a, b, cost = (x, y) => x === y ? 0 : 1) {
|
|
4851
4958
|
const row = Array.from({ length: b.length + 1 }, (_, i) => i);
|
|
4852
4959
|
for (let i = 1; i <= a.length; i += 1) {
|
|
4853
4960
|
let prev = row[0];
|
|
4854
4961
|
row[0] = i;
|
|
4855
4962
|
for (let j = 1; j <= b.length; j += 1) {
|
|
4856
4963
|
const cur = row[j];
|
|
4857
|
-
row[j] = Math.min(row[j] + 1, row[j - 1] + 1, prev + (a[i - 1]
|
|
4964
|
+
row[j] = Math.min(row[j] + 1, row[j - 1] + 1, prev + cost(a[i - 1], b[j - 1]));
|
|
4858
4965
|
prev = cur;
|
|
4859
4966
|
}
|
|
4860
4967
|
}
|
|
4861
4968
|
return row[b.length];
|
|
4862
4969
|
}
|
|
4970
|
+
function sectionCost(x, y) {
|
|
4971
|
+
if (x.role !== y.role) return 1;
|
|
4972
|
+
return Math.min(0.5, (Math.abs(x.centred - y.centred) + Math.abs(x.width - y.width)) / 2);
|
|
4973
|
+
}
|
|
4974
|
+
function layoutDistance(a, b) {
|
|
4975
|
+
if (a.sections && b.sections) {
|
|
4976
|
+
const longest2 = Math.max(a.sections.length, b.sections.length);
|
|
4977
|
+
return longest2 === 0 ? 0 : levenshtein(a.sections, b.sections, sectionCost) / longest2;
|
|
4978
|
+
}
|
|
4979
|
+
const longest = Math.max(a.layout.length, b.layout.length);
|
|
4980
|
+
return longest === 0 ? 0 : levenshtein(a.layout, b.layout) / longest;
|
|
4981
|
+
}
|
|
4863
4982
|
var clamp = (n) => Math.max(0, Math.min(1, n));
|
|
4864
4983
|
function faceDistance(a, b) {
|
|
4865
4984
|
if (a.family.toLowerCase() === b.family.toLowerCase()) return 0;
|
|
@@ -4874,8 +4993,7 @@ function fingerprintDistance(a, b) {
|
|
|
4874
4993
|
const union = /* @__PURE__ */ new Set([...a.effects, ...b.effects]);
|
|
4875
4994
|
const shared = a.effects.filter((e) => b.effects.includes(e)).length;
|
|
4876
4995
|
const effects = union.size === 0 ? 0 : 1 - shared / union.size;
|
|
4877
|
-
const
|
|
4878
|
-
const layout = longest === 0 ? 0 : levenshtein(a.layout, b.layout) / longest;
|
|
4996
|
+
const layout = layoutDistance(a, b);
|
|
4879
4997
|
const parts2 = { accent, ground, type, shape, motion, effects, layout };
|
|
4880
4998
|
const total = Object.keys(DISTANCE_WEIGHTS).reduce((sum, k) => sum + DISTANCE_WEIGHTS[k] * parts2[k], 0);
|
|
4881
4999
|
return { total: Math.round(total * 1e3) / 1e3, parts: parts2 };
|
|
@@ -4903,6 +5021,117 @@ var SOURCE_KINDS = [
|
|
|
4903
5021
|
];
|
|
4904
5022
|
var CHOICE_KEYS = ["accent", "ground", "display", "body", "shape", "motif", "signature"];
|
|
4905
5023
|
|
|
5024
|
+
// src/direction/licences.ts
|
|
5025
|
+
var LICENCES = [
|
|
5026
|
+
{
|
|
5027
|
+
name: "Basteleur",
|
|
5028
|
+
subject: "face",
|
|
5029
|
+
licence: "OFL-1.1",
|
|
5030
|
+
multiClient: "yes",
|
|
5031
|
+
checked: "2026-09-24",
|
|
5032
|
+
source: "https://velvetyne.fr/fonts/basteleur/",
|
|
5033
|
+
note: "Velvetyne, designed by Keussel. Keep the OFL notice with the font files."
|
|
5034
|
+
},
|
|
5035
|
+
{
|
|
5036
|
+
name: "Gulax",
|
|
5037
|
+
subject: "face",
|
|
5038
|
+
licence: "OFL-1.1",
|
|
5039
|
+
multiClient: "yes",
|
|
5040
|
+
checked: "2026-09-24",
|
|
5041
|
+
source: "https://velvetyne.fr/fonts/gulax/",
|
|
5042
|
+
note: "Velvetyne. Keep the OFL notice with the font files."
|
|
5043
|
+
},
|
|
5044
|
+
{
|
|
5045
|
+
name: "Le Murmure",
|
|
5046
|
+
subject: "face",
|
|
5047
|
+
licence: "OFL-1.1",
|
|
5048
|
+
multiClient: "yes",
|
|
5049
|
+
checked: "2026-09-24",
|
|
5050
|
+
source: "https://velvetyne.fr/fonts/le-murmure/",
|
|
5051
|
+
note: "Velvetyne. Keep the OFL notice with the font files."
|
|
5052
|
+
},
|
|
5053
|
+
{
|
|
5054
|
+
name: "Blaze Type",
|
|
5055
|
+
subject: "foundry",
|
|
5056
|
+
licence: "commercial",
|
|
5057
|
+
multiClient: "per-site",
|
|
5058
|
+
cap: 10,
|
|
5059
|
+
checked: "2026-09-24",
|
|
5060
|
+
source: "https://blazetype.eu/eula",
|
|
5061
|
+
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."
|
|
5062
|
+
},
|
|
5063
|
+
{
|
|
5064
|
+
name: "Power Type",
|
|
5065
|
+
subject: "foundry",
|
|
5066
|
+
licence: "commercial",
|
|
5067
|
+
multiClient: "unverified",
|
|
5068
|
+
checked: "2026-09-24",
|
|
5069
|
+
source: "https://power-type.com/eula",
|
|
5070
|
+
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."
|
|
5071
|
+
},
|
|
5072
|
+
{
|
|
5073
|
+
name: "Grilli Type",
|
|
5074
|
+
subject: "foundry",
|
|
5075
|
+
licence: "commercial",
|
|
5076
|
+
multiClient: "per-site",
|
|
5077
|
+
checked: "2026-09-24",
|
|
5078
|
+
source: "https://www.grillitype.com/information",
|
|
5079
|
+
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."
|
|
5080
|
+
},
|
|
5081
|
+
{
|
|
5082
|
+
name: "Phosphor",
|
|
5083
|
+
subject: "icons",
|
|
5084
|
+
licence: "MIT",
|
|
5085
|
+
multiClient: "yes",
|
|
5086
|
+
checked: "2026-09-24",
|
|
5087
|
+
source: "https://github.com/phosphor-icons/core/blob/main/LICENSE",
|
|
5088
|
+
note: "Six weights. The icon-tile tells treat it exactly as they treat Lucide: the library is not the tell, the tinted tile is."
|
|
5089
|
+
},
|
|
5090
|
+
{
|
|
5091
|
+
name: "Tabler Icons",
|
|
5092
|
+
subject: "icons",
|
|
5093
|
+
licence: "MIT",
|
|
5094
|
+
multiClient: "yes",
|
|
5095
|
+
checked: "2026-09-24",
|
|
5096
|
+
source: "https://github.com/tabler/tabler-icons/blob/main/LICENSE",
|
|
5097
|
+
note: "One stroke grid across the set."
|
|
5098
|
+
},
|
|
5099
|
+
{
|
|
5100
|
+
name: "Iconoir",
|
|
5101
|
+
subject: "icons",
|
|
5102
|
+
licence: "MIT",
|
|
5103
|
+
multiClient: "yes",
|
|
5104
|
+
checked: "2026-09-24",
|
|
5105
|
+
source: "https://github.com/iconoir-icons/iconoir/blob/main/LICENSE",
|
|
5106
|
+
note: "Smaller set, no paid tier."
|
|
5107
|
+
},
|
|
5108
|
+
{
|
|
5109
|
+
name: "Unicons",
|
|
5110
|
+
subject: "icons",
|
|
5111
|
+
licence: "IconScout Simple License",
|
|
5112
|
+
multiClient: "yes",
|
|
5113
|
+
checked: "2026-09-24",
|
|
5114
|
+
source: "https://github.com/Iconscout/unicons/blob/master/LICENSE",
|
|
5115
|
+
note: "IconScout's free set, personal and commercial use, attribution optional."
|
|
5116
|
+
},
|
|
5117
|
+
{
|
|
5118
|
+
name: "IconScout premium",
|
|
5119
|
+
subject: "icons",
|
|
5120
|
+
licence: "commercial",
|
|
5121
|
+
multiClient: "per-site",
|
|
5122
|
+
checked: "2026-09-24",
|
|
5123
|
+
source: "https://iconscout.com/licenses",
|
|
5124
|
+
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."
|
|
5125
|
+
}
|
|
5126
|
+
];
|
|
5127
|
+
function faceLicence(family) {
|
|
5128
|
+
const name = family.trim().toLowerCase();
|
|
5129
|
+
return LICENCES.find((l) => l.subject === "face" && l.name.toLowerCase() === name);
|
|
5130
|
+
}
|
|
5131
|
+
function licencesJson() {
|
|
5132
|
+
return JSON.stringify({ licences: LICENCES }, null, 2) + "\n";
|
|
5133
|
+
}
|
|
5134
|
+
|
|
4906
5135
|
// src/direction/validate.ts
|
|
4907
5136
|
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;
|
|
4908
5137
|
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;
|
|
@@ -5026,6 +5255,18 @@ function validateDirection(input, ctx = {}) {
|
|
|
5026
5255
|
ok = false;
|
|
5027
5256
|
}
|
|
5028
5257
|
}
|
|
5258
|
+
if ((key === "display" || key === "body") && typeof c.value === "string" && c.value.trim()) {
|
|
5259
|
+
const family = normaliseFamily(c.value);
|
|
5260
|
+
const entry = faceLicence(family);
|
|
5261
|
+
if (!entry) {
|
|
5262
|
+
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.`);
|
|
5263
|
+
} else if (entry.multiClient === "unverified") {
|
|
5264
|
+
warn(`${at}.value`, `${family}: licence unverified (${entry.note})`);
|
|
5265
|
+
} else if (entry.multiClient === "capped" || entry.multiClient === "per-site") {
|
|
5266
|
+
const scope = entry.multiClient === "capped" ? `covers up to ${entry.cap} sites` : "needs a licence for this site, in the client's name";
|
|
5267
|
+
warn(`${at}.value`, `${family}: ${entry.licence} licence ${scope}. Record the purchase before launch.`);
|
|
5268
|
+
}
|
|
5269
|
+
}
|
|
5029
5270
|
const fp = ctx.fingerprint;
|
|
5030
5271
|
if (fp && typeof c.value === "string") {
|
|
5031
5272
|
if ((key === "accent" || key === "ground") && parseColour(c.value)) {
|
|
@@ -5695,13 +5936,16 @@ export {
|
|
|
5695
5936
|
DURATION_S,
|
|
5696
5937
|
EASE,
|
|
5697
5938
|
EASE_TUPLE,
|
|
5939
|
+
EDGE_STEP,
|
|
5698
5940
|
ESTATE_VERSION,
|
|
5699
5941
|
EXIT_RATIO,
|
|
5700
5942
|
FINGERPRINT_VERSION,
|
|
5701
5943
|
HOUSE,
|
|
5702
5944
|
HOUSE_BUDGET,
|
|
5945
|
+
HOUSE_LOCKED,
|
|
5703
5946
|
HOUSE_TYPE,
|
|
5704
5947
|
JSON_SCHEMA_VERSION,
|
|
5948
|
+
LICENCES,
|
|
5705
5949
|
LIGHTNESS_CURVE,
|
|
5706
5950
|
MIN_DECIDED,
|
|
5707
5951
|
MIN_RUNS,
|
|
@@ -5711,6 +5955,7 @@ export {
|
|
|
5711
5955
|
PLAINER_WORDS,
|
|
5712
5956
|
PROPOSED,
|
|
5713
5957
|
RAMP_STEPS,
|
|
5958
|
+
READABLE_SNAPSHOT_VERSIONS,
|
|
5714
5959
|
REFLEX_FONTS_1,
|
|
5715
5960
|
REFLEX_FONTS_2,
|
|
5716
5961
|
RENDERED_PATHS,
|
|
@@ -5755,6 +6000,7 @@ export {
|
|
|
5755
6000
|
estatePairs,
|
|
5756
6001
|
exitDuration,
|
|
5757
6002
|
extractHtml,
|
|
6003
|
+
faceLicence,
|
|
5758
6004
|
findClaims,
|
|
5759
6005
|
findColours,
|
|
5760
6006
|
findingKey,
|
|
@@ -5772,6 +6018,7 @@ export {
|
|
|
5772
6018
|
groundName,
|
|
5773
6019
|
harvest,
|
|
5774
6020
|
hexToOklch,
|
|
6021
|
+
holdHouseBlocks,
|
|
5775
6022
|
houseRule,
|
|
5776
6023
|
hueName,
|
|
5777
6024
|
hueSwatch,
|
|
@@ -5781,6 +6028,8 @@ export {
|
|
|
5781
6028
|
initDirection,
|
|
5782
6029
|
isAiViolet,
|
|
5783
6030
|
isCream,
|
|
6031
|
+
layoutDistance,
|
|
6032
|
+
licencesJson,
|
|
5784
6033
|
makeSnapshot,
|
|
5785
6034
|
motionTokens,
|
|
5786
6035
|
namedSources,
|
|
@@ -5798,6 +6047,7 @@ export {
|
|
|
5798
6047
|
protectedFacts,
|
|
5799
6048
|
ramp,
|
|
5800
6049
|
rampFromAnchors,
|
|
6050
|
+
readSnapshot,
|
|
5801
6051
|
retrofitPlan,
|
|
5802
6052
|
runTell,
|
|
5803
6053
|
scanSource,
|
|
@@ -5818,6 +6068,7 @@ export {
|
|
|
5818
6068
|
typicality,
|
|
5819
6069
|
validateDirection,
|
|
5820
6070
|
visibleText,
|
|
6071
|
+
visualMeasures,
|
|
5821
6072
|
wcagContrast,
|
|
5822
6073
|
wordListsJson
|
|
5823
6074
|
};
|
package/licences.json
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
{
|
|
2
|
+
"licences": [
|
|
3
|
+
{
|
|
4
|
+
"name": "Basteleur",
|
|
5
|
+
"subject": "face",
|
|
6
|
+
"licence": "OFL-1.1",
|
|
7
|
+
"multiClient": "yes",
|
|
8
|
+
"checked": "2026-09-24",
|
|
9
|
+
"source": "https://velvetyne.fr/fonts/basteleur/",
|
|
10
|
+
"note": "Velvetyne, designed by Keussel. Keep the OFL notice with the font files."
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"name": "Gulax",
|
|
14
|
+
"subject": "face",
|
|
15
|
+
"licence": "OFL-1.1",
|
|
16
|
+
"multiClient": "yes",
|
|
17
|
+
"checked": "2026-09-24",
|
|
18
|
+
"source": "https://velvetyne.fr/fonts/gulax/",
|
|
19
|
+
"note": "Velvetyne. Keep the OFL notice with the font files."
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"name": "Le Murmure",
|
|
23
|
+
"subject": "face",
|
|
24
|
+
"licence": "OFL-1.1",
|
|
25
|
+
"multiClient": "yes",
|
|
26
|
+
"checked": "2026-09-24",
|
|
27
|
+
"source": "https://velvetyne.fr/fonts/le-murmure/",
|
|
28
|
+
"note": "Velvetyne. Keep the OFL notice with the font files."
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"name": "Blaze Type",
|
|
32
|
+
"subject": "foundry",
|
|
33
|
+
"licence": "commercial",
|
|
34
|
+
"multiClient": "per-site",
|
|
35
|
+
"cap": 10,
|
|
36
|
+
"checked": "2026-09-24",
|
|
37
|
+
"source": "https://blazetype.eu/eula",
|
|
38
|
+
"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."
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"name": "Power Type",
|
|
42
|
+
"subject": "foundry",
|
|
43
|
+
"licence": "commercial",
|
|
44
|
+
"multiClient": "unverified",
|
|
45
|
+
"checked": "2026-09-24",
|
|
46
|
+
"source": "https://power-type.com/eula",
|
|
47
|
+
"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."
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"name": "Grilli Type",
|
|
51
|
+
"subject": "foundry",
|
|
52
|
+
"licence": "commercial",
|
|
53
|
+
"multiClient": "per-site",
|
|
54
|
+
"checked": "2026-09-24",
|
|
55
|
+
"source": "https://www.grillitype.com/information",
|
|
56
|
+
"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."
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"name": "Phosphor",
|
|
60
|
+
"subject": "icons",
|
|
61
|
+
"licence": "MIT",
|
|
62
|
+
"multiClient": "yes",
|
|
63
|
+
"checked": "2026-09-24",
|
|
64
|
+
"source": "https://github.com/phosphor-icons/core/blob/main/LICENSE",
|
|
65
|
+
"note": "Six weights. The icon-tile tells treat it exactly as they treat Lucide: the library is not the tell, the tinted tile is."
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"name": "Tabler Icons",
|
|
69
|
+
"subject": "icons",
|
|
70
|
+
"licence": "MIT",
|
|
71
|
+
"multiClient": "yes",
|
|
72
|
+
"checked": "2026-09-24",
|
|
73
|
+
"source": "https://github.com/tabler/tabler-icons/blob/main/LICENSE",
|
|
74
|
+
"note": "One stroke grid across the set."
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"name": "Iconoir",
|
|
78
|
+
"subject": "icons",
|
|
79
|
+
"licence": "MIT",
|
|
80
|
+
"multiClient": "yes",
|
|
81
|
+
"checked": "2026-09-24",
|
|
82
|
+
"source": "https://github.com/iconoir-icons/iconoir/blob/main/LICENSE",
|
|
83
|
+
"note": "Smaller set, no paid tier."
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"name": "Unicons",
|
|
87
|
+
"subject": "icons",
|
|
88
|
+
"licence": "IconScout Simple License",
|
|
89
|
+
"multiClient": "yes",
|
|
90
|
+
"checked": "2026-09-24",
|
|
91
|
+
"source": "https://github.com/Iconscout/unicons/blob/master/LICENSE",
|
|
92
|
+
"note": "IconScout's free set, personal and commercial use, attribution optional."
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"name": "IconScout premium",
|
|
96
|
+
"subject": "icons",
|
|
97
|
+
"licence": "commercial",
|
|
98
|
+
"multiClient": "per-site",
|
|
99
|
+
"checked": "2026-09-24",
|
|
100
|
+
"source": "https://iconscout.com/licenses",
|
|
101
|
+
"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."
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@domandigital/craft",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The Doman Digital house craft standard as numbers: OKLCh colour ramps that snap to existing anchors, derived semantic tokens with measured WCAG and APCA contrast, one shared motion vocabulary for CSS and JS, and a base stylesheet. Zero dependencies.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
"README.md",
|
|
51
51
|
"STANDARD.md",
|
|
52
52
|
"word-lists.json",
|
|
53
|
+
"licences.json",
|
|
53
54
|
"css",
|
|
54
55
|
"dist"
|
|
55
56
|
],
|
|
@@ -86,6 +87,6 @@
|
|
|
86
87
|
"build": "tsup",
|
|
87
88
|
"typecheck": "tsc --noEmit",
|
|
88
89
|
"test": "vitest run",
|
|
89
|
-
"docs": "tsup && node scripts/write-catalogue.mjs && node scripts/write-word-lists.mjs"
|
|
90
|
+
"docs": "tsup && node scripts/write-catalogue.mjs && node scripts/write-word-lists.mjs && node scripts/write-licences.mjs"
|
|
90
91
|
}
|
|
91
92
|
}
|
package/word-lists.json
CHANGED
package/dist/cli-T5RD2BTK.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
describeFingerprint,
|
|
3
|
-
merge,
|
|
4
|
-
resolvePages,
|
|
5
|
-
runAudit
|
|
6
|
-
} from "./chunk-MSQ57PJU.js";
|
|
7
|
-
import "./chunk-JP75P2DU.js";
|
|
8
|
-
import "./chunk-3OZQ4C5R.js";
|
|
9
|
-
import "./chunk-2AJB27BJ.js";
|
|
10
|
-
import "./chunk-QMYERCBB.js";
|
|
11
|
-
import "./chunk-3NGWXZZE.js";
|
|
12
|
-
export {
|
|
13
|
-
describeFingerprint,
|
|
14
|
-
merge,
|
|
15
|
-
resolvePages,
|
|
16
|
-
runAudit
|
|
17
|
-
};
|
package/dist/cli-VREYWCT4.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
formatMatches,
|
|
3
|
-
formatPairs,
|
|
4
|
-
loadEstate,
|
|
5
|
-
runEstate
|
|
6
|
-
} from "./chunk-HOHHQB4N.js";
|
|
7
|
-
import "./chunk-3OZQ4C5R.js";
|
|
8
|
-
import "./chunk-2AJB27BJ.js";
|
|
9
|
-
import "./chunk-QMYERCBB.js";
|
|
10
|
-
import "./chunk-3NGWXZZE.js";
|
|
11
|
-
export {
|
|
12
|
-
formatMatches,
|
|
13
|
-
formatPairs,
|
|
14
|
-
loadEstate,
|
|
15
|
-
runEstate
|
|
16
|
-
};
|