@domandigital/craft 0.14.0 → 0.15.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 +18 -0
- package/CHARACTER.md +213 -28
- package/README.md +19 -1
- package/art-direction.schema.json +428 -25
- package/dist/{audit-6WHR7ZTV.js → audit-MTUC24XU.js} +2 -2
- package/dist/audit.cjs +5 -0
- package/dist/audit.js +5 -0
- package/dist/{chunk-7T5WR74C.js → chunk-2IQ5OXFR.js} +4 -4
- package/dist/{chunk-GQVEYTBY.js → chunk-LFNSB6W7.js} +13 -3
- package/dist/{chunk-3OTKMKJQ.js → chunk-MUVFECFC.js} +19 -15
- package/dist/chunk-OQXTQQAU.js +27 -0
- package/dist/{chunk-Q7ILL2HV.js → chunk-OUQ25LY2.js} +1 -1
- package/dist/{chunk-PBK6SYH5.js → chunk-QU6Q2QZZ.js} +46 -11
- package/dist/{chunk-XVBR2UPH.js → chunk-RXXRXV3V.js} +7 -24
- package/dist/chunk-YDURAOYR.js +711 -0
- package/dist/{cli-3CGUKXY4.js → cli-22TSWAX6.js} +6 -4
- package/dist/{cli-WSHPSWVU.js → cli-5ACKICBO.js} +6 -5
- package/dist/{cli-O3CDSONO.js → cli-7OZCYOQY.js} +86 -20
- package/dist/{cli-WG3UJNUF.js → cli-FCKHYAVZ.js} +36 -13
- package/dist/{cli-Z2IQG3LC.js → cli-FLG3ZCAF.js} +4 -3
- package/dist/cli.js +2 -2
- package/dist/generate.js +6 -0
- package/dist/index.cjs +850 -115
- package/dist/index.d.cts +455 -56
- package/dist/index.d.ts +455 -56
- package/dist/index.js +804 -115
- package/package.json +1 -1
- package/dist/chunk-OOZOEWGD.js +0 -295
|
@@ -4,11 +4,13 @@ import {
|
|
|
4
4
|
formatPairs,
|
|
5
5
|
loadEstate,
|
|
6
6
|
runEstate
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-QU6Q2QZZ.js";
|
|
8
|
+
import "./chunk-YDURAOYR.js";
|
|
9
|
+
import "./chunk-RXXRXV3V.js";
|
|
10
|
+
import "./chunk-OQXTQQAU.js";
|
|
11
|
+
import "./chunk-MUVFECFC.js";
|
|
10
12
|
import "./chunk-WUACK2HX.js";
|
|
11
|
-
import "./chunk-
|
|
13
|
+
import "./chunk-LFNSB6W7.js";
|
|
12
14
|
import "./chunk-QYBAVU3P.js";
|
|
13
15
|
export {
|
|
14
16
|
formatComponentPairs,
|
|
@@ -4,13 +4,14 @@ import {
|
|
|
4
4
|
merge,
|
|
5
5
|
resolvePages,
|
|
6
6
|
runAudit
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-2IQ5OXFR.js";
|
|
8
|
+
import "./chunk-OUQ25LY2.js";
|
|
9
9
|
import "./chunk-JYNXGCYZ.js";
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
10
|
+
import "./chunk-RXXRXV3V.js";
|
|
11
|
+
import "./chunk-OQXTQQAU.js";
|
|
12
|
+
import "./chunk-MUVFECFC.js";
|
|
12
13
|
import "./chunk-WUACK2HX.js";
|
|
13
|
-
import "./chunk-
|
|
14
|
+
import "./chunk-LFNSB6W7.js";
|
|
14
15
|
import "./chunk-QYBAVU3P.js";
|
|
15
16
|
export {
|
|
16
17
|
describeComponents,
|
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DIRECTION_VERSION,
|
|
3
|
-
validateDirection
|
|
4
|
-
} from "./chunk-OOZOEWGD.js";
|
|
5
1
|
import {
|
|
6
2
|
decodePng
|
|
7
3
|
} from "./chunk-JYNXGCYZ.js";
|
|
4
|
+
import {
|
|
5
|
+
DIRECTION_VERSION,
|
|
6
|
+
PAGE_TYPES,
|
|
7
|
+
PROPOSED,
|
|
8
|
+
researchPrompt,
|
|
9
|
+
validateDirection
|
|
10
|
+
} from "./chunk-YDURAOYR.js";
|
|
8
11
|
import {
|
|
9
12
|
parseFlags,
|
|
10
13
|
toJson
|
|
11
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-MUVFECFC.js";
|
|
12
15
|
import {
|
|
13
16
|
readSnapshot
|
|
14
17
|
} from "./chunk-WUACK2HX.js";
|
|
@@ -21,7 +24,7 @@ import {
|
|
|
21
24
|
isCream,
|
|
22
25
|
oklchToRgb,
|
|
23
26
|
parseColour
|
|
24
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-LFNSB6W7.js";
|
|
25
28
|
import "./chunk-QYBAVU3P.js";
|
|
26
29
|
|
|
27
30
|
// src/direction/cli.ts
|
|
@@ -44,11 +47,31 @@ function initDirection(input) {
|
|
|
44
47
|
body: empty(fp.body.family),
|
|
45
48
|
...fp.roundness !== null ? { shape: empty(fp.roundness >= 0.45 ? "pill buttons" : fp.roundness <= 0.05 ? "square corners" : `corners at ${Math.round(fp.roundness * 100)}% of button height`) } : {}
|
|
46
49
|
} : {};
|
|
50
|
+
const part = () => ({ value: "", evidence: [] });
|
|
47
51
|
return {
|
|
48
52
|
$schema: "https://unpkg.com/@domandigital/craft/art-direction.schema.json",
|
|
49
53
|
version: DIRECTION_VERSION,
|
|
50
54
|
client: input.client ?? "",
|
|
51
55
|
brief: input.brief ?? "",
|
|
56
|
+
// Empty on purpose, like the reasons: the first validate lists the job and the structure as undecided.
|
|
57
|
+
job: {
|
|
58
|
+
statement: { verb: "", object: "", context: "" },
|
|
59
|
+
functional: part(),
|
|
60
|
+
emotional: part(),
|
|
61
|
+
social: part(),
|
|
62
|
+
consideration: { value: "low", because: "", evidence: [] },
|
|
63
|
+
objections: [],
|
|
64
|
+
language: [],
|
|
65
|
+
evidence: []
|
|
66
|
+
},
|
|
67
|
+
hierarchy: {
|
|
68
|
+
home: {
|
|
69
|
+
primaryAction: { value: "", positions: [], because: "" },
|
|
70
|
+
order: fp?.sections?.length ? fp.sections.map((s) => ({ role: s.role, because: "" })) : [],
|
|
71
|
+
lead: { value: "", because: "" },
|
|
72
|
+
journey: []
|
|
73
|
+
}
|
|
74
|
+
},
|
|
52
75
|
sources: [],
|
|
53
76
|
choices,
|
|
54
77
|
...input.exceptions?.length ? { exceptions: input.exceptions } : {}
|
|
@@ -56,7 +79,6 @@ function initDirection(input) {
|
|
|
56
79
|
}
|
|
57
80
|
|
|
58
81
|
// src/direction/propose.ts
|
|
59
|
-
var PROPOSED = "PROPOSED:";
|
|
60
82
|
function srgbToLinear(c) {
|
|
61
83
|
return c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
|
|
62
84
|
}
|
|
@@ -118,7 +140,13 @@ function accentCandidates(input) {
|
|
|
118
140
|
const parsed = parseColour(c);
|
|
119
141
|
if (!parsed || !chromatic(parsed.oklch)) continue;
|
|
120
142
|
const o = parsed.oklch;
|
|
121
|
-
const siblings =
|
|
143
|
+
const siblings = [
|
|
144
|
+
...(input.estate ?? []).map((e) => ({ id: e.id, d: e.fingerprint.accent ? deltaEOk(o, e.fingerprint.accent) : 1 })),
|
|
145
|
+
...(input.declared ?? []).flatMap((e) => {
|
|
146
|
+
const p = parseColour(e.accent);
|
|
147
|
+
return p ? [{ id: `${e.id} (declared)`, d: deltaEOk(o, p.oklch) }] : [];
|
|
148
|
+
})
|
|
149
|
+
];
|
|
122
150
|
const nearest = siblings.sort((a, b) => a.d - b.d)[0];
|
|
123
151
|
const penalty = isAiViolet(o) ? 1 : 0;
|
|
124
152
|
out.push({
|
|
@@ -174,15 +202,26 @@ function propose(input) {
|
|
|
174
202
|
|
|
175
203
|
// src/direction/cli.ts
|
|
176
204
|
var readJson = (path) => JSON.parse(readFileSync(path, "utf8"));
|
|
177
|
-
function
|
|
178
|
-
|
|
205
|
+
async function snapshotFrom(target, cwd) {
|
|
206
|
+
if (!target) return void 0;
|
|
207
|
+
if (/^https?:\/\//.test(target)) {
|
|
208
|
+
const { snapshotUrl } = await import("./audit-MTUC24XU.js");
|
|
209
|
+
return snapshotUrl(target);
|
|
210
|
+
}
|
|
211
|
+
return readSnapshot(readJson(resolve(cwd, target)), target);
|
|
212
|
+
}
|
|
213
|
+
function declaredFrom(path, cwd) {
|
|
214
|
+
if (!path || !path.endsWith(".json")) return [];
|
|
215
|
+
const register = readJson(resolve(cwd, path));
|
|
216
|
+
return register.sites ?? [];
|
|
179
217
|
}
|
|
218
|
+
var TEXT_SOURCE = /\.(?:txt|md|markdown|csv|json|html?)$/i;
|
|
180
219
|
function estateFrom(dir, cwd) {
|
|
181
220
|
if (!dir) return [];
|
|
182
221
|
const root = resolve(cwd, dir);
|
|
183
222
|
if (root.endsWith(".json")) {
|
|
184
223
|
const register = readJson(root);
|
|
185
|
-
return (register.sites ?? []).map((s) => ({ id: s.id, fingerprint: s.fingerprint }));
|
|
224
|
+
return (register.sites ?? []).filter((s) => s.fingerprint).map((s) => ({ id: s.id, fingerprint: s.fingerprint }));
|
|
186
225
|
}
|
|
187
226
|
return readdirSync(root).filter((f) => f.endsWith(".json")).flatMap((f) => {
|
|
188
227
|
try {
|
|
@@ -193,23 +232,33 @@ function estateFrom(dir, cwd) {
|
|
|
193
232
|
}
|
|
194
233
|
});
|
|
195
234
|
}
|
|
196
|
-
function runDirection(args, io) {
|
|
235
|
+
async function runDirection(args, io) {
|
|
197
236
|
try {
|
|
198
237
|
const flags = parseFlags(args);
|
|
199
238
|
if (typeof flags === "string") throw new Error(flags);
|
|
200
239
|
const [sub] = flags.positional;
|
|
201
240
|
const file = resolve(io.cwd, flags.direction ?? "art-direction.json");
|
|
202
|
-
|
|
241
|
+
if (sub === "research") {
|
|
242
|
+
const base = existsSync(file) ? readJson(file) : {};
|
|
243
|
+
const brief = flags.brief ?? base.brief;
|
|
244
|
+
if (!brief || brief.trim().split(/\s+/).length < 5) throw new Error('usage: craft direction research --brief "<who, where, what they do>" [--visual], or run it next to an art-direction.json with a brief');
|
|
245
|
+
io.out(researchPrompt({ brief, client: flags.client ?? base.client, visual: flags.visual }));
|
|
246
|
+
return 0;
|
|
247
|
+
}
|
|
248
|
+
const page = flags.page ?? "home";
|
|
249
|
+
if (!PAGE_TYPES.includes(page)) throw new Error(`--page takes one of ${PAGE_TYPES.join(", ")}`);
|
|
250
|
+
const snapshot = await snapshotFrom(flags.snapshot, io.cwd);
|
|
251
|
+
const current = snapshot ? fingerprint(snapshot) : void 0;
|
|
203
252
|
if (sub === "init") {
|
|
204
253
|
const out = resolve(io.cwd, flags.out ?? "art-direction.json");
|
|
205
254
|
if (existsSync(out) && !flags.out) {
|
|
206
255
|
const existing = readJson(out);
|
|
207
256
|
if (existing.version !== void 0) throw new Error(`${out} already records choices. Pass --out to write elsewhere.`);
|
|
208
|
-
const next = initDirection({ client: flags.client, current, exceptions: existing.exceptions });
|
|
257
|
+
const next = initDirection({ client: flags.client, brief: flags.brief, current, exceptions: existing.exceptions });
|
|
209
258
|
writeFileSync(out, `${JSON.stringify(next, null, 2)}
|
|
210
259
|
`);
|
|
211
260
|
} else {
|
|
212
|
-
writeFileSync(out, `${JSON.stringify(initDirection({ client: flags.client, current }), null, 2)}
|
|
261
|
+
writeFileSync(out, `${JSON.stringify(initDirection({ client: flags.client, brief: flags.brief, current }), null, 2)}
|
|
213
262
|
`);
|
|
214
263
|
}
|
|
215
264
|
io.out(`craft direction: wrote ${out}. Add the sources and a reason for every choice, then run craft direction validate.`);
|
|
@@ -217,14 +266,30 @@ function runDirection(args, io) {
|
|
|
217
266
|
}
|
|
218
267
|
if (sub === "validate") {
|
|
219
268
|
if (!existsSync(file)) throw new Error(`no ${file}. Run craft direction init.`);
|
|
220
|
-
const
|
|
269
|
+
const direction = readJson(file);
|
|
270
|
+
const estate = declaredFrom(flags.estate, io.cwd);
|
|
271
|
+
const report = validateDirection(direction, {
|
|
221
272
|
fingerprint: current,
|
|
222
|
-
|
|
273
|
+
snapshot,
|
|
274
|
+
page,
|
|
275
|
+
strict: flags.strict,
|
|
276
|
+
pathExists: (p) => existsSync(resolve(dirname(file), p)),
|
|
277
|
+
readSource: (s) => {
|
|
278
|
+
if (!s.path || /^https?:/.test(s.path) || !TEXT_SOURCE.test(s.path)) return void 0;
|
|
279
|
+
const path = resolve(dirname(file), s.path);
|
|
280
|
+
return existsSync(path) ? readFileSync(path, "utf8") : void 0;
|
|
281
|
+
},
|
|
282
|
+
estate,
|
|
283
|
+
estateId: estate.find((s) => s.client && s.client === direction.client)?.id
|
|
223
284
|
});
|
|
224
285
|
if (flags.json) io.out(toJson(report));
|
|
225
286
|
else {
|
|
226
287
|
for (const p of report.problems) io.out(` ${p.severity === "error" ? "error" : "warn "} ${p.at || "(file)"} ${p.message}`);
|
|
227
|
-
|
|
288
|
+
const l = report.layers;
|
|
289
|
+
const pages = l.hierarchy.map((h) => `${h.page} ${h.decided} of ${h.total}`).join(", ");
|
|
290
|
+
io.out(
|
|
291
|
+
`craft direction: ${report.valid ? "valid" : "not valid"}. Job ${l.job ? "decided" : "not decided"}; hierarchy ${pages || "not decided"}; tokens ${l.tokens.decided} of ${l.tokens.total}.${l.complete ? " Every layer decided." : ""}`
|
|
292
|
+
);
|
|
228
293
|
}
|
|
229
294
|
return report.valid ? 0 : 1;
|
|
230
295
|
}
|
|
@@ -243,7 +308,8 @@ function runDirection(args, io) {
|
|
|
243
308
|
return s;
|
|
244
309
|
}
|
|
245
310
|
});
|
|
246
|
-
const
|
|
311
|
+
const declared = declaredFrom(flags.estate, io.cwd).flatMap((s) => s.direction?.accent && s.client !== base.client ? [{ id: s.id, accent: s.direction.accent }] : []);
|
|
312
|
+
const result = propose({ client: base.client, brief: base.brief, sources, current, estate: estateFrom(flags.estate, io.cwd), declared });
|
|
247
313
|
const merged = { ...base, sources, choices: { ...result.direction.choices, ...stripUndecided(base.choices) }, ...base.exceptions ? { exceptions: base.exceptions } : {} };
|
|
248
314
|
const text = `${JSON.stringify(merged, null, 2)}
|
|
249
315
|
`;
|
|
@@ -254,7 +320,7 @@ function runDirection(args, io) {
|
|
|
254
320
|
}
|
|
255
321
|
return 0;
|
|
256
322
|
}
|
|
257
|
-
throw new Error("usage: craft direction init | validate | propose");
|
|
323
|
+
throw new Error("usage: craft direction init | validate | propose | research");
|
|
258
324
|
} catch (error) {
|
|
259
325
|
io.err(`craft: ${error.message}`);
|
|
260
326
|
return 2;
|
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
validateDirection
|
|
3
|
-
} from "./chunk-OOZOEWGD.js";
|
|
4
1
|
import {
|
|
5
2
|
merge
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
3
|
+
} from "./chunk-2IQ5OXFR.js";
|
|
4
|
+
import "./chunk-OUQ25LY2.js";
|
|
8
5
|
import "./chunk-JYNXGCYZ.js";
|
|
9
6
|
import {
|
|
10
7
|
SIBLING_AT,
|
|
11
8
|
compareToEstate,
|
|
12
9
|
loadEstate
|
|
13
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-QU6Q2QZZ.js";
|
|
11
|
+
import {
|
|
12
|
+
PAGE_TYPES,
|
|
13
|
+
validateDirection
|
|
14
|
+
} from "./chunk-YDURAOYR.js";
|
|
14
15
|
import {
|
|
15
16
|
loadNull
|
|
16
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-RXXRXV3V.js";
|
|
18
|
+
import "./chunk-OQXTQQAU.js";
|
|
17
19
|
import {
|
|
18
20
|
COPY_FILE,
|
|
19
21
|
NOT_COPY_DIR,
|
|
@@ -24,7 +26,7 @@ import {
|
|
|
24
26
|
parseFlags,
|
|
25
27
|
readPaths,
|
|
26
28
|
toJson
|
|
27
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-MUVFECFC.js";
|
|
28
30
|
import {
|
|
29
31
|
readSnapshot
|
|
30
32
|
} from "./chunk-WUACK2HX.js";
|
|
@@ -35,7 +37,7 @@ import {
|
|
|
35
37
|
fingerprint,
|
|
36
38
|
scanSource,
|
|
37
39
|
typicality
|
|
38
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-LFNSB6W7.js";
|
|
39
41
|
import "./chunk-QYBAVU3P.js";
|
|
40
42
|
|
|
41
43
|
// src/report/cli.ts
|
|
@@ -141,8 +143,16 @@ function characterReport(input, copyTells = /* @__PURE__ */ new Set()) {
|
|
|
141
143
|
}
|
|
142
144
|
if (input.direction) {
|
|
143
145
|
const d = input.direction;
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
+
const layers = d.layers;
|
|
147
|
+
const raised2 = !d.valid || d.decided < MIN_DECIDED || !layers.complete;
|
|
148
|
+
const pages = layers.hierarchy.map((p) => `${p.page} ${p.decided} of ${p.total}`).join(", ");
|
|
149
|
+
const detail = [
|
|
150
|
+
`job ${layers.job ? "decided" : "not decided"}`,
|
|
151
|
+
`hierarchy ${pages || "not decided"}`,
|
|
152
|
+
`${d.decided} of 7 choices decided with a reason`
|
|
153
|
+
].join("; ");
|
|
154
|
+
signals.push({ id: "reasons", raised: raised2, detail: `${detail}${d.valid ? "" : ", art-direction.json has errors"}` });
|
|
155
|
+
if (!layers.job) actions.push({ area: "direction", what: "Write the job map: what the visitor is trying to get done, from reviews, enquiries and conversations. Run craft direction research for the questions.", why: "structure and look are decided from the job", priority: 1 });
|
|
146
156
|
for (const p of d.problems) {
|
|
147
157
|
const key = p.at.match(/^choices\.(\w+)/)?.[1];
|
|
148
158
|
if (p.severity === "error" || p.message === "not decided yet") {
|
|
@@ -258,12 +268,14 @@ async function runReport(command, args, io) {
|
|
|
258
268
|
if (typeof flags === "string") throw new Error(flags);
|
|
259
269
|
const target2 = flags.positional[0];
|
|
260
270
|
if (!target2) throw new Error(USAGE);
|
|
271
|
+
const page = flags.page ?? "home";
|
|
272
|
+
if (!PAGE_TYPES.includes(page)) throw new Error(`--page takes one of ${PAGE_TYPES.join(", ")}`);
|
|
261
273
|
const local = resolve(io.cwd, target2);
|
|
262
274
|
let snap;
|
|
263
275
|
if (/\.json$/i.test(target2) && existsSync(local)) {
|
|
264
276
|
snap = readSnapshot(JSON.parse(readFileSync(local, "utf8")), target2);
|
|
265
277
|
} else {
|
|
266
|
-
const { snapshotUrl } = await import("./audit-
|
|
278
|
+
const { snapshotUrl } = await import("./audit-MTUC24XU.js");
|
|
267
279
|
snap = await snapshotUrl(target2);
|
|
268
280
|
}
|
|
269
281
|
const fp = fingerprint(snap);
|
|
@@ -290,7 +302,18 @@ async function runReport(command, args, io) {
|
|
|
290
302
|
let directionReport = null;
|
|
291
303
|
if (existsSync(directionFile)) {
|
|
292
304
|
direction = JSON.parse(readFileSync(directionFile, "utf8"));
|
|
293
|
-
directionReport = validateDirection(direction, {
|
|
305
|
+
directionReport = validateDirection(direction, {
|
|
306
|
+
fingerprint: fp,
|
|
307
|
+
snapshot: snap,
|
|
308
|
+
page,
|
|
309
|
+
strict: flags.strict,
|
|
310
|
+
pathExists: (p) => existsSync(resolve(dirname(directionFile), p)),
|
|
311
|
+
readSource: (s) => {
|
|
312
|
+
if (!s.path || /^https?:/.test(s.path) || !/\.(?:txt|md|markdown|csv|json|html?)$/i.test(s.path)) return void 0;
|
|
313
|
+
const path = resolve(dirname(directionFile), s.path);
|
|
314
|
+
return existsSync(path) ? readFileSync(path, "utf8") : void 0;
|
|
315
|
+
}
|
|
316
|
+
});
|
|
294
317
|
} else if (flags.direction) {
|
|
295
318
|
throw new Error(`no such file: ${flags.direction}`);
|
|
296
319
|
}
|
|
@@ -6,10 +6,11 @@ import {
|
|
|
6
6
|
nullPath,
|
|
7
7
|
runHarvest,
|
|
8
8
|
runNull
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-RXXRXV3V.js";
|
|
10
|
+
import "./chunk-OQXTQQAU.js";
|
|
11
|
+
import "./chunk-MUVFECFC.js";
|
|
11
12
|
import "./chunk-WUACK2HX.js";
|
|
12
|
-
import "./chunk-
|
|
13
|
+
import "./chunk-LFNSB6W7.js";
|
|
13
14
|
import "./chunk-QYBAVU3P.js";
|
|
14
15
|
export {
|
|
15
16
|
describeTypicality,
|
package/dist/cli.js
CHANGED