beehaven 0.8.24 → 0.8.29
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/dist/agent-state.js +19 -0
- package/dist/browser-login.js +283 -0
- package/dist/build-channel.js +2 -2
- package/dist/clearly.js +5 -4
- package/dist/cli.js +14 -266
- package/dist/commands/agent.js +186 -59
- package/dist/commands/canvas.js +1 -1
- package/dist/commands/compose.js +2 -2
- package/dist/commands/help.js +2 -0
- package/dist/commands/self-update.js +2 -2
- package/dist/commands/workspace.js +30 -8
- package/dist/daemon.js +9 -9
- package/dist/folder-sync.js +1 -1
- package/dist/local-app-manager.js +1 -1
- package/dist/relay-presets.js +3 -4
- package/dist/setup-relay.js +20 -235
- package/package.json +1 -1
- package/skills/beehaven-cli/SKILL.md +12 -0
- package/skills/brand-identity/SKILL.md +15 -9
- package/skills/clearly-agent/SKILL.md +27 -3
- package/skills/clearly-canvas/SKILL.md +67 -7
- package/skills/clearly-canvas/authoring.md +27 -4
- package/skills/clearly-canvas/review.mjs +106 -12
- package/skills/design-craft/SKILL.md +5 -0
- package/skills/design-craft/references.md +95 -0
- package/skills/index.json +7 -6
|
@@ -49,6 +49,12 @@ do not blindly resend only skipped operations. Mixed live/headless actions may h
|
|
|
49
49
|
different rollback behavior. Use deterministic layer names and returned IDs to avoid
|
|
50
50
|
duplicate content during retries.
|
|
51
51
|
|
|
52
|
+
Also check ignored/rejected fields, warnings, mutation outcome and actual changed IDs. A matched
|
|
53
|
+
node or `ok:true` receipt alone is not proof of an update. The bundled `assertReceipt` helper can
|
|
54
|
+
reject partial drops; `requireChange:true` rejects an unexpected no-op. A deliberate replay may
|
|
55
|
+
be unchanged and should reuse its previous evidence instead of recreating nodes.
|
|
56
|
+
Keep executed payloads and returned-ID manifests separate from regenerated plans.
|
|
57
|
+
|
|
52
58
|
## Node shape and coordinates
|
|
53
59
|
|
|
54
60
|
Fields are flat: `{name,type,x,y,w,h,text,size,family,fontWeight,fill,...}`.
|
|
@@ -71,6 +77,13 @@ Create normalizes hex colours; raw stored scenes use RGBA arrays in 0..1.
|
|
|
71
77
|
moves/resizes use the catalog's frame/arrangement/scale actions; do not assume a patch
|
|
72
78
|
reflows children.
|
|
73
79
|
- Build backgrounds before foregrounds. Use `canvas.order` to change stacking.
|
|
80
|
+
|
|
81
|
+
For create/update geometry, `coordinateSpace:"world"` or `"parent"` makes the intent explicit.
|
|
82
|
+
Default behavior remains compatible with the rules above. Receipts include normalized world
|
|
83
|
+
bounds and parent offsets where available. Template coordinates remain relative to their root.
|
|
84
|
+
An SVG's bytes may be stored in `svgRef` once it exceeds the inline limit; this is valid artwork,
|
|
85
|
+
not a missing node. Replacing `svg` clears and rebuilds its reference automatically. Never patch
|
|
86
|
+
`svgRef` manually or guess its storage URL. Image `fit` is explicitly `contain` or `cover`.
|
|
74
87
|
- Name nodes uniquely; use returned IDs where names collide. Parent content to its
|
|
75
88
|
frame so movement, selection, clipping and reviews can treat it as one artifact.
|
|
76
89
|
- Give an intended stroke both `stroke` and `strokeWidth`. Zero explicitly removes it.
|
|
@@ -106,6 +119,16 @@ and tokens when relevant. Constraints reflow children on frame resize; they do n
|
|
|
106
119
|
a static canvas into a responsive website. Design explicit mobile artboards when the
|
|
107
120
|
deliverable requires them, with content order and hierarchy reconsidered for the width.
|
|
108
121
|
|
|
122
|
+
## Technique
|
|
123
|
+
|
|
124
|
+
The same rules the in-app assistant works to (canvas-catalog returns them as `technique`).
|
|
125
|
+
|
|
126
|
+
- AUTO-LAYOUT: rows and stacks (nav links, button groups, card rows, form fields) are a frame whose children you create in reading order, then canvas.auto-layout { id, direction, gap, padding, align, justify, hug }. hug:false keeps the frame size, which a full-width bar needs with justify:"space-between"; set a child to layoutSizingH/V "fill" | "hug" | "fixed" with canvas.update-nodes.
|
|
127
|
+
- GRADIENTS: gradient { kind, angle, stops: [{ offset, color }] } replaces the solid fill. angle is the direction the colour travels: 0 = left to right, 90 = top to bottom, 45 = top-left to bottom-right (not CSS degrees). Use two or three stops from the brand palette, and keep text off a busy gradient or check its contrast in canvas.review.
|
|
128
|
+
- SHADOWS AND BLUR: effects on any node. A soft card shadow is { type: "drop-shadow", x: 0, y: 4, blur: 16, spread: 0, color: "#0000001F" }; frosted glass is { type: "background-blur", blur: 16 }. Never fake a shadow with an extra rectangle.
|
|
129
|
+
- SVG PLACEHOLDERS: until real imagery exists, draw it as an svg node at its final size in a muted tone from the brand palette, never a flat accent block, e.g. for a 400×240 slot svg: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 240"><rect width="400" height="240" rx="12" fill="#EDE6DF"/><circle cx="170" cy="96" r="14" fill="#CDBFB2"/><path d="M150 168l40-48 30 34 22-20 40 34z" fill="#CDBFB2"/></svg>' — <svg viewBox="0 0 W H"> always matches the node w and h. Put a label in its own text node and name the svg "<section> image placeholder". Hand-write only simple marks like this (placeholders, dividers, UI icons); logos, illustrations and finished artwork come from svg-make.
|
|
130
|
+
- SECTIONS: one frame per page section, named exactly as its ticket says, inside the page artboard. Build a section in one canvas-act batch that ends with canvas.review of the artboard and carries ticket: { id: "<its key>", status: "done", resolution: "<what changed>" }. The ticket moves only when that review is clean or look; result.ticket says whether it did. Then start the next section.
|
|
131
|
+
|
|
109
132
|
Use `arrow.create {from,to,routing,label}` for connected diagrams, so arrows remain bound
|
|
110
133
|
when nodes move. Use native shapes and SVG for custom vector details. Keep labels as
|
|
111
134
|
native text so they remain editable and measurable.
|
|
@@ -163,11 +186,11 @@ the backing shows through as dark wedges at the corners.
|
|
|
163
186
|
|
|
164
187
|
### Images from MCP
|
|
165
188
|
|
|
166
|
-
The
|
|
189
|
+
The artifact MCP tools do not upload local binary files or dispatch `canvas-act`.
|
|
167
190
|
For an image already reachable by an absolute URL, read the composition with
|
|
168
191
|
`clearly_read {type:"composition",target:<id>}`; append an image node
|
|
169
192
|
`{id,type:"image",src,x,y,w,h,fit:"contain",parentId?}` to the returned complete node
|
|
170
|
-
array; then call `
|
|
193
|
+
array; then call `clearly_replace {type:"composition",target:<id>,content:<JSON array>,
|
|
171
194
|
expectedRev:<canvasNodesRev>}`. Preserve every existing node. A stale revision is a
|
|
172
195
|
merge prompt, never a reason to drop `expectedRev`. `clearly_edit` can change an
|
|
173
196
|
existing image's `src`, `fit` or geometry, but cannot create a node. For a local file,
|
|
@@ -212,8 +235,8 @@ MCP's artifact tools are split by operation:
|
|
|
212
235
|
|
|
213
236
|
- `clearly_catalog {type:"composition"}` exposes the current schemas.
|
|
214
237
|
- `clearly_read` returns nodes and `canvasNodesRev`.
|
|
215
|
-
- `
|
|
216
|
-
`expectedRev` from the read
|
|
238
|
+
- `clearly_create {type:"composition"}` makes a composition; `clearly_replace` replaces its full
|
|
239
|
+
node array — pass `expectedRev` from the read.
|
|
217
240
|
- `clearly_edit` patches existing nodes with `changes:{ids,patch}`.
|
|
218
241
|
|
|
219
242
|
Canvas action dispatch and targeted pixels use the CLI. Do not invent
|
|
@@ -1,20 +1,69 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/** Save review pixels outside the model context. No shell interpolation, no workspace mutations. */
|
|
3
3
|
import { execFileSync } from 'node:child_process';
|
|
4
|
-
import {
|
|
4
|
+
import { createHash } from 'node:crypto';
|
|
5
|
+
import { existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from 'node:fs';
|
|
5
6
|
import { dirname, resolve } from 'node:path';
|
|
6
7
|
import { fileURLToPath } from 'node:url';
|
|
7
8
|
|
|
8
|
-
export function
|
|
9
|
+
export function assertReceipt(result, { requireChange = false } = {}) {
|
|
10
|
+
if (result?.ok === false || result?.error || result?.results?.some(r => r.ok === false)) {
|
|
11
|
+
throw new Error(result.error || result.results?.find(r => !r.ok)?.error || 'Canvas request failed');
|
|
12
|
+
}
|
|
13
|
+
for (const row of result?.results ?? [result]) {
|
|
14
|
+
const value = row.value ?? row;
|
|
15
|
+
if (value.ignoredFields?.length || value.rejectedFields?.length) {
|
|
16
|
+
throw new Error(`Fields were not applied: ${[...(value.ignoredFields ?? []), ...(value.rejectedFields ?? [])].join(', ')}. Some work may have landed; inspect the saved scene before retrying.`);
|
|
17
|
+
}
|
|
18
|
+
if (requireChange) {
|
|
19
|
+
const delta = value.canvasChanges;
|
|
20
|
+
const changed = ['changed', 'partial'].includes(value.outcome) || (Array.isArray(delta) ? delta.length > 0 : delta && typeof delta === 'object'
|
|
21
|
+
? Number(delta.added || 0) + Number(delta.changed || 0) + Number(delta.removed || 0) > 0
|
|
22
|
+
: value.placed === true || /\.create(-node)?$/.test(String(row.action || '')) && !!value.id && !value.duplicate);
|
|
23
|
+
if (!changed) throw new Error('No nodes changed, or the receipt does not prove a change. Inspect the saved scene; do not treat a match as an update.');
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return result;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function rpc(action, payload, options) {
|
|
9
30
|
const stdout = execFileSync('beehaven', ['call', action, '-'], {
|
|
10
31
|
input: JSON.stringify(payload), encoding: 'utf8', maxBuffer: 32 * 1024 * 1024,
|
|
11
32
|
stdio: ['pipe', 'pipe', 'pipe'], timeout: 120_000,
|
|
12
33
|
});
|
|
13
34
|
const result = JSON.parse(stdout);
|
|
14
|
-
|
|
15
|
-
|
|
35
|
+
return assertReceipt(result, options);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function sceneNodes(result) {
|
|
39
|
+
if (!Array.isArray(result?.nodes)) throw new Error('Expected composition-detail includeScene:true to return top-level nodes. Refusing to interpret a missing field as an empty canvas.');
|
|
40
|
+
return result.nodes;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Resume creates only. Existing layers are preserved, including the person's later edits. */
|
|
44
|
+
export function resumeCanvasPlan(plan, snapshot, idManifest = {}) {
|
|
45
|
+
const nodes = sceneNodes(snapshot), byId = new Map(nodes.map(n => [n.id, n]));
|
|
46
|
+
const names = new Map();
|
|
47
|
+
for (const n of nodes) { const list = names.get(n.name) ?? []; list.push(n); names.set(n.name, list); }
|
|
48
|
+
const roots = new Map(), batch = [], skipped = [];
|
|
49
|
+
for (const step of plan.batch ?? []) {
|
|
50
|
+
const args = { ...(step.args ?? {}) };
|
|
51
|
+
if (!['frame.create', 'canvas.create-node'].includes(step.action)) { skipped.push({ action: step.action, reason: 'Resume preserves existing mutations; revise explicitly.' }); continue; }
|
|
52
|
+
const type = step.action === 'frame.create' ? 'frame' : args.type;
|
|
53
|
+
const pinned = args.id || idManifest[args.name];
|
|
54
|
+
const matches = names.get(args.name) ?? [];
|
|
55
|
+
if (!pinned && matches.length > 1) throw new Error(`Ambiguous layer name ${args.name}; use its recorded ID.`);
|
|
56
|
+
const existing = pinned ? byId.get(pinned) : matches[0];
|
|
57
|
+
if (existing) {
|
|
58
|
+
if (existing.type !== type) throw new Error(`Layer ${args.name} has type ${existing.type}, expected ${type}. No automatic replacement.`);
|
|
59
|
+
roots.set(args.name, existing.id); skipped.push({ id: existing.id, name: args.name, reason: 'Already exists; preserved.' }); continue;
|
|
60
|
+
}
|
|
61
|
+
args.id = pinned || `plan-${createHash('sha256').update(`${plan.compositionId}:${args.name}`).digest('hex').slice(0, 24)}`;
|
|
62
|
+
if (args.parentId && roots.has(args.parentId)) args.parentId = roots.get(args.parentId);
|
|
63
|
+
roots.set(args.name, args.id);
|
|
64
|
+
batch.push({ ...step, args });
|
|
16
65
|
}
|
|
17
|
-
return
|
|
66
|
+
return { compositionId: plan.compositionId, expectedCanvasNodesRev: snapshot.canvasNodesRev, batch, skipped };
|
|
18
67
|
}
|
|
19
68
|
|
|
20
69
|
export function saveReview(report, prefix) {
|
|
@@ -24,7 +73,21 @@ export function saveReview(report, prefix) {
|
|
|
24
73
|
}
|
|
25
74
|
const bytes = Buffer.from(preview.dataUrl.slice('data:image/png;base64,'.length), 'base64');
|
|
26
75
|
if (!bytes.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))) throw new Error('Invalid PNG response');
|
|
27
|
-
|
|
76
|
+
if (bytes.length < 45 || bytes.readUInt32BE(8) !== 13 || bytes.subarray(12,16).toString() !== 'IHDR'
|
|
77
|
+
|| !bytes.readUInt32BE(16) || !bytes.readUInt32BE(20)) throw new Error('Invalid PNG response: missing image header/body');
|
|
78
|
+
let offset = 8, imageData = false, end = false;
|
|
79
|
+
while (offset + 12 <= bytes.length) {
|
|
80
|
+
const length = bytes.readUInt32BE(offset), tag = bytes.subarray(offset+4,offset+8).toString();
|
|
81
|
+
if (offset + length + 12 > bytes.length) throw new Error('Invalid PNG response: truncated image chunk');
|
|
82
|
+
if (tag === 'IDAT' && length > 0) imageData = true;
|
|
83
|
+
offset += length + 12;
|
|
84
|
+
if (tag === 'IEND') { end = length === 0; break; }
|
|
85
|
+
}
|
|
86
|
+
if (!imageData || !end) throw new Error('Invalid PNG response: missing image data/end');
|
|
87
|
+
let base = resolve(prefix);
|
|
88
|
+
const original = base;
|
|
89
|
+
let version = 0;
|
|
90
|
+
while (existsSync(`${base}.png`) || existsSync(`${base}.json`)) base = `${original}-r${report.canvasNodesRev ?? 'unknown'}-${++version}`;
|
|
28
91
|
mkdirSync(dirname(base), { recursive: true });
|
|
29
92
|
const { dataUrl, url, ...metadata } = preview;
|
|
30
93
|
const saved = { ...report, preview: { ...metadata, path: `${base}.png` } };
|
|
@@ -34,16 +97,47 @@ export function saveReview(report, prefix) {
|
|
|
34
97
|
return { png: `${base}.png`, report: `${base}.json`,
|
|
35
98
|
// `fix` = a probable defect remains: fix it, or pass its check/id in `ignore` with a reason.
|
|
36
99
|
// `look` = only things worth a second look. `clean` = nothing mechanical found — NOT "good".
|
|
37
|
-
verdict: report.verdict ?? 'unknown',
|
|
100
|
+
verdict: report.verdict ?? 'unknown', acceptanceStatus: report.acceptanceStatus,
|
|
101
|
+
acceptedFindings: report.acceptedFindings ?? [], blocking: report.blocking,
|
|
38
102
|
craft: craft.map((f) => `${f.severity} ${f.check}: ${f.detail}`),
|
|
103
|
+
audit: (report.audit?.violations ?? []).map(v => ({check:v.kind,layer:v.name || v.id,detail:v.detail || v.message || v.reason || `${v.kind} on ${v.name || v.id}`})),
|
|
104
|
+
scopeWarnings: report.scopeWarnings ?? [],
|
|
105
|
+
assets: (report.assets ?? []).filter(a => a.status !== 'ready'),
|
|
106
|
+
typography: (report.typography?.text ?? []).filter(t => t.issue || t.horizontalOverflow > 1 || !t.autoHeight && t.verticalOverflow > 1 || t.exact === false)
|
|
107
|
+
.map(t => ({layer:t.name || t.id,issue:t.issue,horizontalOverflow:t.horizontalOverflow,verticalOverflow:t.verticalOverflow,
|
|
108
|
+
requestedFont:t.font?.requestedFamily,requestedWeight:t.font?.requestedWeight,resolvedFile:t.font?.file,exact:t.exact})),
|
|
39
109
|
nodes: report.scope?.nodeCount, findings: report.audit?.total, textChecked: report.typography?.checked,
|
|
40
110
|
textOmitted: report.typography?.omitted, visualReviewRequired: true,
|
|
41
111
|
note: report.verdict === 'fix'
|
|
42
|
-
? 'Fix every warn (or
|
|
112
|
+
? 'Fix every warn (or accept an exact finding with a reason), then review changed artboards. Then open the PNG and critique it.'
|
|
43
113
|
: 'Open the PNG with your image-viewing tool, critique it against the studio bar, revise and render again.' };
|
|
44
114
|
}
|
|
45
115
|
|
|
46
|
-
export function
|
|
116
|
+
export async function saveReviewPixels(report, prefix, fetchImage = fetch) {
|
|
117
|
+
if (report.preview?.dataUrl) return saveReview(report, prefix);
|
|
118
|
+
const url = report.preview?.url;
|
|
119
|
+
if (!url || !/^https?:\/\//.test(url)) throw new Error(report.previewError || 'No downloadable preview. Visual verification is incomplete.');
|
|
120
|
+
const response = await fetchImage(url);
|
|
121
|
+
if (!response.ok) throw new Error(`Preview download failed (HTTP ${response.status}); request a fresh review.`);
|
|
122
|
+
const bytes = Buffer.from(await response.arrayBuffer());
|
|
123
|
+
return saveReview({ ...report, preview: { ...report.preview, dataUrl: `data:image/png;base64,${bytes.toString('base64')}` } }, prefix);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export async function main(argv = process.argv.slice(2)) {
|
|
127
|
+
if (argv.includes('--many')) {
|
|
128
|
+
const args = argv.filter(a => a !== '--many');
|
|
129
|
+
if (args.length !== 3) throw new Error('Usage: node review.mjs --many <compositionId> <artboard-ids.json> <output-directory>');
|
|
130
|
+
const [compositionId, manifest, directory] = args;
|
|
131
|
+
const ids = JSON.parse(readFileSync(manifest, 'utf8'));
|
|
132
|
+
if (!Array.isArray(ids) || !ids.length) throw new Error('The manifest must be a non-empty artboard ID/name array.');
|
|
133
|
+
const response = rpc('canvas-act', { compositionId, action: 'canvas.review-many', args: { ids, preview: 'files' } });
|
|
134
|
+
const reports = response.results?.[0]?.value?.reviews;
|
|
135
|
+
if (!Array.isArray(reports) || reports.length !== ids.length) throw new Error('Incomplete review-many result; do not claim all artboards passed.');
|
|
136
|
+
const saved = [];
|
|
137
|
+
for (let i = 0; i < reports.length; i++) saved.push(await saveReviewPixels(reports[i], resolve(directory, `${String(i + 1).padStart(2, '0')}-${String(ids[i]).replace(/[^a-z0-9_-]/gi, '-')}`)));
|
|
138
|
+
process.stdout.write(JSON.stringify(saved, null, 2) + '\n');
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
47
141
|
const legacy = argv.includes('--perceive');
|
|
48
142
|
const args = argv.filter((a) => a !== '--perceive');
|
|
49
143
|
if (args.length !== 3) throw new Error('Usage: node review.mjs <compositionId> <frame-id-or-name> <output-prefix> [--perceive]\n--perceive: older workers, preview only; pass a frame ID.');
|
|
@@ -60,9 +154,9 @@ export function main(argv = process.argv.slice(2)) {
|
|
|
60
154
|
report = response.results?.[0]?.value;
|
|
61
155
|
if (!report?.scope) throw new Error('canvas.review unavailable. Check canvas-catalog; use --perceive for an explicitly preview-only check on an older worker.');
|
|
62
156
|
}
|
|
63
|
-
process.stdout.write(JSON.stringify(
|
|
157
|
+
process.stdout.write(JSON.stringify(await saveReviewPixels(report, prefix), null, 2) + '\n');
|
|
64
158
|
}
|
|
65
159
|
|
|
66
|
-
if (process.argv[1] &&
|
|
67
|
-
|
|
160
|
+
if (process.argv[1] && existsSync(process.argv[1]) && realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url))) {
|
|
161
|
+
main().catch(error => { process.stderr.write(`${error.message}\n`); process.exitCode = 1; });
|
|
68
162
|
}
|
|
@@ -298,6 +298,11 @@ fewer elements, tighter type, one accent*. Every ornament added is a step away f
|
|
|
298
298
|
|
|
299
299
|
## Sources
|
|
300
300
|
|
|
301
|
+
The full shelf — about fifty books across grid, type, print production, colour, graphic form,
|
|
302
|
+
brand identity, information and interface design, each reduced to one rule — is in
|
|
303
|
+
[references.md](references.md). In Research pick two or three whose lens fits the brief, and name
|
|
304
|
+
the one behind each direction in its rationale.
|
|
305
|
+
|
|
301
306
|
Josef Müller-Brockmann, *Grid Systems in Graphic Design* (1981) · Massimo Vignelli, *The
|
|
302
307
|
Vignelli Canon* (semantics / syntactics / pragmatics; "Design is one") · Michael Bierut,
|
|
303
308
|
*How to* (Pentagram; brief → rationale → trade-offs) · Paula Scher / Pentagram, The Public
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# The design shelf
|
|
2
|
+
|
|
3
|
+
<!-- Generated from apps/cloudflare/src/agent/design-references.ts (the design-references and brand-references house skills).
|
|
4
|
+
Edit the list there, then run: WRITE_DESIGN_REFERENCES=1 npx vitest run src/__tests__/verify-design-references.test.ts -->
|
|
5
|
+
|
|
6
|
+
Books a studio designer works from, each reduced to one rule you can act on. The in-app
|
|
7
|
+
assistant gets the same list as the `design-references` and `brand-references` workspace skills.
|
|
8
|
+
|
|
9
|
+
## How to use it
|
|
10
|
+
|
|
11
|
+
- In Research, pick two or three references whose lens fits the brief: a print piece or poster → grid, type, print; an identity → brand; a chart or report → information; a screen or flow → interface. Write what each one changes in this design.
|
|
12
|
+
- In Direction, name the reference behind each direction's idea in one line ("a Müller-Brockmann modular grid, type as the image after Scher").
|
|
13
|
+
- In Review, critique against the same rules you chose.
|
|
14
|
+
- These are paraphrased principles, not quotations. Never invent a quote or page number; cite author and title only.
|
|
15
|
+
|
|
16
|
+
## Method — deciding before drawing
|
|
17
|
+
|
|
18
|
+
- Massimo Vignelli, *The Vignelli Canon* (2010): Answer semantics (what it means, to whom), syntactics (its visual grammar) and pragmatics (will they get it) before placing a node; few typefaces, a grid, discipline.
|
|
19
|
+
- Michael Bierut, *How to* (2015): Present brief → idea → trade-offs; the idea comes out of the problem, and the rationale is part of the deliverable.
|
|
20
|
+
- Paul Rand, *Thoughts on Design* (1947): Form and content are one: simplify until the idea is legible, and allow wit — play is a way of making it memorable.
|
|
21
|
+
- Bruno Munari, *Design as Art* (1966): Solve the problem with the simplest means; decoration that solves nothing is noise.
|
|
22
|
+
- Karl Gerstner, *Designing Programmes* (1964): Design the rules, not one layout: define the variables (size, position, colour) and generate the set from them.
|
|
23
|
+
- Dieter Rams, *Less but Better* (1995): As little design as possible: every element must earn its place by use; remove what does not.
|
|
24
|
+
- Alan Fletcher, *The Art of Looking Sideways* (2001): Look for the idea sideways — a visual pun or found connection beats a literal illustration of the brief.
|
|
25
|
+
|
|
26
|
+
## Grid and layout
|
|
27
|
+
|
|
28
|
+
- Josef Müller-Brockmann, *Grid Systems in Graphic Design* (1981): Construct margins, columns, gutter and baseline first; every element lands on the grid; break it once, on purpose.
|
|
29
|
+
- Timothy Samara, *Making and Breaking the Grid* (2002): Pick the grid for the content (manuscript, column, modular, hierarchical); a deliberate break needs a grid to break.
|
|
30
|
+
- Kimberly Elam, *Geometry of Design* (2001): Place the focal point on a proportional line (golden section, root rectangles, thirds), not at dead centre.
|
|
31
|
+
- Beth Tondreau, *Layout Essentials* (2009): Grids by job: a single column for reading, multi-column for mixed content, modular for catalogues and dashboards.
|
|
32
|
+
|
|
33
|
+
## Typography
|
|
34
|
+
|
|
35
|
+
- Robert Bringhurst, *The Elements of Typographic Style* (1992): Measure 45–75 characters, leading to suit the measure, honour the content: type exists to be read.
|
|
36
|
+
- Ellen Lupton, *Thinking with Type* (2004): Build hierarchy by changing one variable at a time — size, weight, colour or space — and track capitals open.
|
|
37
|
+
- Jan Tschichold, *The New Typography* (1928): Asymmetric, functional layout: hierarchy by size, weight and position, never ornament.
|
|
38
|
+
- Emil Ruder, *Typographie* (1967): White space is active form: the counter, the gap and the margin are composed like the type itself.
|
|
39
|
+
- Jost Hochuli, *Detail in Typography* (1987): Letter, word and line spacing decide readability: tighten display type, keep word spaces even, open the leading for long lines.
|
|
40
|
+
- Erik Spiekermann and E. M. Ginger, *Stop Stealing Sheep & Find Out How Type Works* (1993): A typeface is a voice: choose it for the message and audience, then set it with care before anything else.
|
|
41
|
+
- Ellen Lupton, *Type on Screen* (2014): On screen, type is motion and interaction too: sizes, scales and states are a system, not one setting.
|
|
42
|
+
|
|
43
|
+
## Print and editorial production
|
|
44
|
+
|
|
45
|
+
- Jan Tschichold, *The Form of the Book*: Classical page proportions: inner margin smallest, foot margin largest; the text block sits high and toward the spine.
|
|
46
|
+
- Richard Hendel, *On Book Design* (1998): A book design serves the reading; decide trim, margins, type and running heads together, and stay invisible.
|
|
47
|
+
- Andrew Haslam, *Book Design* (2006): Plan the whole object — format, grid, pagination, binding — before designing a spread.
|
|
48
|
+
- Jan V. White, *Editing by Design* (1974): Editorial layout starts from what the story says; headline, picture and caption are one unit.
|
|
49
|
+
- International Paper, *Pocket Pal*: Production rules: CMYK for press, 300 dpi images at final size, 3 mm (0.125 in) bleed, a safe area inside the trim.
|
|
50
|
+
- Alan Pipes, *Production for Graphic Designers*: Specify for the press: paper stock, ink coverage, rich black for large areas only, overprint and trap small type.
|
|
51
|
+
|
|
52
|
+
## Colour
|
|
53
|
+
|
|
54
|
+
- Josef Albers, *Interaction of Color* (1963): Colour is relative: judge it beside its neighbours on the real ground, never as a swatch alone.
|
|
55
|
+
- Johannes Itten, *The Art of Color* (1961): Choose the one contrast that leads (light–dark, warm–cool, complementary, saturation, extent) and let the rest support it.
|
|
56
|
+
|
|
57
|
+
## Graphic form and visual perception
|
|
58
|
+
|
|
59
|
+
- Armin Hofmann, *Graphic Design Manual* (1965): Build with elementary contrasts — point, line, plane, black and white, large and small — before colour.
|
|
60
|
+
- Ellen Lupton and Jennifer Cole Phillips, *Graphic Design: The New Basics* (2008): Point, line, plane, rhythm, scale, texture, figure/ground and framing are variables; change one deliberately.
|
|
61
|
+
- Wucius Wong, *Principles of Two-Dimensional Design* (1972): Repetition, gradation and radiation give a field structure; an anomaly in that structure is the focal point.
|
|
62
|
+
- Rudolf Arnheim, *Art and Visual Perception* (1954): Visual weight balances on the frame's hidden axes; size, value, isolation and position all add weight.
|
|
63
|
+
- Robin Williams, *The Non-Designer's Design Book* (1994): Contrast, repetition, alignment, proximity: if two things differ, make them clearly different.
|
|
64
|
+
- Alex W. White, *The Elements of Graphic Design* (2002): Treat space as an element: emptiness gives the content its shape and its importance.
|
|
65
|
+
- Kenya Hara, *Designing Design* (2007): Emptiness invites the viewer in: leave room for the reader's imagination instead of filling the frame.
|
|
66
|
+
- Paula Scher, *Make It Bigger* (2002): Type can be the image: scale and crop a word until it carries the identity on its own.
|
|
67
|
+
- Philip B. Meggs, *A History of Graphic Design* (1983): Know the lineage of a style so a reference is chosen, not a pastiche of the first thing found.
|
|
68
|
+
- Adrian Frutiger, *Signs and Symbols*: A sign reads by its simplest form; test a mark small, at a distance and in one colour.
|
|
69
|
+
|
|
70
|
+
## Brand and identity
|
|
71
|
+
|
|
72
|
+
- Alina Wheeler, *Designing Brand Identity* (2003): Identity is a process — research, strategy, design, touchpoints, asset management; design the touchpoints, not just the logo.
|
|
73
|
+
- Marty Neumeier, *The Brand Gap* (2003): A brand is the customer's gut feeling; close the gap between strategy and the look by testing what people actually perceive.
|
|
74
|
+
- Marty Neumeier, *Zag* (2006): When everyone zigs, zag: state what makes this the only one, then design to that difference.
|
|
75
|
+
- Wally Olins, *On Brand* (2003): A brand shows in product, environment, communication and behaviour; keep all four saying the same thing.
|
|
76
|
+
- Byron Sharp, *How Brands Grow* (2010): Growth comes from being easy to notice and buy: protect distinctive assets and stay consistent.
|
|
77
|
+
- Jenni Romaniuk, *Building Distinctive Brand Assets* (2018): An asset (colour, shape, character) works once it is famous and unique to the brand; use it everywhere, change it rarely.
|
|
78
|
+
- Tom Geismar, Ivan Chermayeff and Sagi Haviv, *Identify* (2011): A mark must be appropriate, distinctive and simple; it earns meaning through use over time.
|
|
79
|
+
- David Airey, *Logo Design Love* (2009): Test a logo at 16 px, in one colour and in reverse; if it needs detail or colour to survive, simplify it.
|
|
80
|
+
- Danne & Blackburn, *NASA Graphics Standards Manual* (1975): A standards manual fixes the mark, clear space, colours, type, grid and applications so anyone can apply the system.
|
|
81
|
+
- Unimark International (Vignelli, Noorda), *New York City Transit Authority Graphics Standards Manual* (1970): A system scales by rules: one typeface, fixed modules, colour coding and consistent placement.
|
|
82
|
+
|
|
83
|
+
## Information design
|
|
84
|
+
|
|
85
|
+
- Edward R. Tufte, *The Visual Display of Quantitative Information* (1983): Maximise data-ink: remove chart junk, label directly, show the comparison; use small multiples for change.
|
|
86
|
+
- Edward R. Tufte, *Envisioning Information* (1990): Layer and separate: quiet the grid and background so the data reads first.
|
|
87
|
+
- Otto Neurath, *International Picture Language* (1936): Isotype: count with repeated identical pictograms, never by enlarging one.
|
|
88
|
+
|
|
89
|
+
## Interface and product design
|
|
90
|
+
|
|
91
|
+
- Don Norman, *The Design of Everyday Things* (1988): Make the action visible: signifiers, feedback, mapping and constraints; a control must say how it is used.
|
|
92
|
+
- Steve Krug, *Don't Make Me Think* (2000): People scan, they do not read: obvious hierarchy, conventions, one clear next action per page.
|
|
93
|
+
- Adam Wathan and Steve Schoger, *Refactoring UI* (2018): Hierarchy by weight and colour before size, de-emphasise secondary text, use a spacing and shadow scale.
|
|
94
|
+
- Jenifer Tidwell, *Designing Interfaces* (2005): Use the known pattern for the job (navigation, forms, lists, dashboards) before inventing one.
|
|
95
|
+
- William Lidwell, Kritina Holden and Jill Butler, *Universal Principles of Design* (2003): Check the design against named principles (Fitts, Hick, proximity, progressive disclosure) and say which one a choice serves.
|
package/skills/index.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"files": [
|
|
5
5
|
"SKILL.md"
|
|
6
6
|
],
|
|
7
|
-
"sha": "
|
|
7
|
+
"sha": "6233057bd1a44b51"
|
|
8
8
|
},
|
|
9
9
|
{
|
|
10
10
|
"name": "clearly-canvas",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"project-plans.md",
|
|
18
18
|
"review.mjs"
|
|
19
19
|
],
|
|
20
|
-
"sha": "
|
|
20
|
+
"sha": "7d79406b37f611a8"
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
23
|
"name": "agent-home",
|
|
@@ -36,9 +36,10 @@
|
|
|
36
36
|
{
|
|
37
37
|
"name": "design-craft",
|
|
38
38
|
"files": [
|
|
39
|
-
"SKILL.md"
|
|
39
|
+
"SKILL.md",
|
|
40
|
+
"references.md"
|
|
40
41
|
],
|
|
41
|
-
"sha": "
|
|
42
|
+
"sha": "bf2f4fac6f57ea46"
|
|
42
43
|
},
|
|
43
44
|
{
|
|
44
45
|
"name": "layout-systems",
|
|
@@ -52,7 +53,7 @@
|
|
|
52
53
|
"files": [
|
|
53
54
|
"SKILL.md"
|
|
54
55
|
],
|
|
55
|
-
"sha": "
|
|
56
|
+
"sha": "cd891499599ccb7b"
|
|
56
57
|
},
|
|
57
58
|
{
|
|
58
59
|
"name": "pair-on-canvas",
|
|
@@ -81,6 +82,6 @@
|
|
|
81
82
|
"files": [
|
|
82
83
|
"SKILL.md"
|
|
83
84
|
],
|
|
84
|
-
"sha": "
|
|
85
|
+
"sha": "b3f47988cb9235b9"
|
|
85
86
|
}
|
|
86
87
|
]
|