@domandigital/craft 0.14.0 → 0.16.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.
@@ -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-WAV6AKR4.js";
8
11
  import {
9
12
  parseFlags,
10
13
  toJson
11
- } from "./chunk-3OTKMKJQ.js";
14
+ } from "./chunk-LXDNIJFG.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-GQVEYTBY.js";
27
+ } from "./chunk-B2MJLGF6.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 = (input.estate ?? []).map((e) => ({ id: e.id, d: e.fingerprint.accent ? deltaEOk(o, e.fingerprint.accent) : 1 }));
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 fingerprintFrom(path, cwd) {
178
- return path ? fingerprint(readJson(resolve(cwd, path))) : void 0;
205
+ async function snapshotFrom(target, cwd) {
206
+ if (!target) return void 0;
207
+ if (/^https?:\/\//.test(target)) {
208
+ const { snapshotUrl } = await import("./audit-UJCUWORL.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
- const current = fingerprintFrom(flags.snapshot, io.cwd);
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 report = validateDirection(readJson(file), {
269
+ const direction = readJson(file);
270
+ const estate = declaredFrom(flags.estate, io.cwd);
271
+ const report = validateDirection(direction, {
221
272
  fingerprint: current,
222
- pathExists: (p) => existsSync(resolve(dirname(file), p))
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
- io.out(`craft direction: ${report.valid ? "valid" : "not valid"}, ${report.decided} of 7 choices decided with a reason.`);
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 result = propose({ client: base.client, brief: base.brief, sources, current, estate: estateFrom(flags.estate, io.cwd) });
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;
@@ -4,11 +4,13 @@ import {
4
4
  formatPairs,
5
5
  loadEstate,
6
6
  runEstate
7
- } from "./chunk-PBK6SYH5.js";
8
- import "./chunk-XVBR2UPH.js";
9
- import "./chunk-3OTKMKJQ.js";
7
+ } from "./chunk-CVG5GSMN.js";
8
+ import "./chunk-WAV6AKR4.js";
9
+ import "./chunk-LWVGIJ5A.js";
10
+ import "./chunk-OQXTQQAU.js";
11
+ import "./chunk-LXDNIJFG.js";
10
12
  import "./chunk-WUACK2HX.js";
11
- import "./chunk-GQVEYTBY.js";
13
+ import "./chunk-B2MJLGF6.js";
12
14
  import "./chunk-QYBAVU3P.js";
13
15
  export {
14
16
  formatComponentPairs,
package/dist/cli.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  run
4
- } from "./chunk-3OTKMKJQ.js";
5
- import "./chunk-GQVEYTBY.js";
4
+ } from "./chunk-LXDNIJFG.js";
5
+ import "./chunk-B2MJLGF6.js";
6
6
  import "./chunk-QYBAVU3P.js";
7
7
 
8
8
  // src/cli.ts
@@ -0,0 +1,6 @@
1
+ import {
2
+ generate
3
+ } from "./chunk-OQXTQQAU.js";
4
+ export {
5
+ generate
6
+ };