@cychien/ezdesign 0.0.0-stage → 0.9.1
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/LICENSE +21 -0
- package/README.md +615 -2
- package/dist/announcements/template-ERNKAKI5.svg +33 -0
- package/dist/cli.mjs +10095 -0
- package/dist/constants.d.ts +73 -0
- package/dist/events-worker.js +77 -0
- package/dist/extension/g/0.9.1-16d05e759b/dialog.css +1 -0
- package/dist/extension/g/0.9.1-16d05e759b/dialog.html +13 -0
- package/dist/extension/g/0.9.1-16d05e759b/dialog.js +1 -0
- package/dist/extension/g/0.9.1-16d05e759b/icon-128.png +0 -0
- package/dist/extension/g/0.9.1-16d05e759b/icon-16.png +0 -0
- package/dist/extension/g/0.9.1-16d05e759b/icon-32.png +0 -0
- package/dist/extension/g/0.9.1-16d05e759b/icon-48.png +0 -0
- package/dist/extension/g/0.9.1-16d05e759b/media.html +9 -0
- package/dist/extension/g/0.9.1-16d05e759b/media.js +1 -0
- package/dist/extension/g/0.9.1-16d05e759b/page.js +39 -0
- package/dist/extension/g/0.9.1-16d05e759b/setup.css +1 -0
- package/dist/extension/g/0.9.1-16d05e759b/setup.html +17 -0
- package/dist/extension/g/0.9.1-16d05e759b/setup.js +1 -0
- package/dist/extension/g/0.9.1-16d05e759b/worker.js +57 -0
- package/dist/extension/manifest.json +57 -0
- package/dist/native-host.mjs +20594 -0
- package/dist/overlay.css +422 -0
- package/dist/overlay.js +2642 -0
- package/dist/project-root.d.ts +10 -0
- package/dist/shell.css +4060 -0
- package/dist/shell.js +8284 -0
- package/dist/vite.d.ts +26 -0
- package/dist/vite.mjs +105 -0
- package/package.json +82 -4
- package/skills/ezdesign/SKILL.md +35 -0
- package/skills/ezdesign/reference/baseline.md +131 -0
- package/skills/ezdesign/reference/components.md +102 -0
- package/skills/ezdesign/reference/critique.md +129 -0
- package/skills/ezdesign/reference/design.md +247 -0
- package/skills/ezdesign/reference/inspiration.md +125 -0
- package/skills/ezdesign/reference/make.md +331 -0
- package/skills/ezdesign/reference/principles.md +130 -0
- package/skills/ezdesign/reference/product.md +127 -0
- package/skills/ezdesign/reference/stack.md +123 -0
- package/skills/ezdesign/reference/start.md +78 -0
- package/skills/ezdesign/scripts/page.d.mts +61 -0
- package/skills/ezdesign/scripts/page.mjs +908 -0
package/dist/vite.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural subset of Vite's Plugin type. Deliberately not imported from
|
|
3
|
+
* 'vite' so the emitted d.ts never pins a Vite major — the object is
|
|
4
|
+
* assignable to Plugin in every Vite version that has these hooks (5+).
|
|
5
|
+
*/
|
|
6
|
+
export interface EzdesignSourcePlugin {
|
|
7
|
+
name: string;
|
|
8
|
+
apply: 'serve';
|
|
9
|
+
enforce: 'pre';
|
|
10
|
+
configResolved(config: {
|
|
11
|
+
root: string;
|
|
12
|
+
}): void;
|
|
13
|
+
transform(code: string, id: string): {
|
|
14
|
+
code: string;
|
|
15
|
+
map: string;
|
|
16
|
+
} | null;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Dev-only JSX transform: stamps `data-ez-source="relative/file.tsx:line"` on
|
|
20
|
+
* host elements (lowercase tags) so overlay annotations resolve to exact
|
|
21
|
+
* source locations. `apply: 'serve'` — never part of a production build.
|
|
22
|
+
*
|
|
23
|
+
* Relative to the *project*, not to Vite's root: the path is for the agent to
|
|
24
|
+
* open, and the agent stands where the CLI was run - see `projectRoot`.
|
|
25
|
+
*/
|
|
26
|
+
export declare function ezdesignSource(): EzdesignSourcePlugin;
|
package/dist/vite.mjs
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
// src/vite.ts
|
|
2
|
+
import { relative } from "node:path";
|
|
3
|
+
import { parse as parse2 } from "@babel/parser";
|
|
4
|
+
import MagicString from "magic-string";
|
|
5
|
+
|
|
6
|
+
// src/constants.ts
|
|
7
|
+
import { homedir } from "node:os";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
var APP_NAME = "ezdesign";
|
|
10
|
+
var ENV_PREFIX = APP_NAME.toUpperCase();
|
|
11
|
+
var DATA_DIR = process.env[`${ENV_PREFIX}_DATA_DIR`] ?? join(homedir(), `.${APP_NAME}`);
|
|
12
|
+
var SESSIONS_DIR = join(DATA_DIR, "sessions");
|
|
13
|
+
var LEGACY_SESSIONS_DIR = join(DATA_DIR, "sessions-legacy");
|
|
14
|
+
var REGISTRY_FILE = join(DATA_DIR, "daemon.json");
|
|
15
|
+
var DAEMON_LOG = join(DATA_DIR, "daemon.log");
|
|
16
|
+
var VERSIONS_DIR = join(DATA_DIR, "versions");
|
|
17
|
+
var UPDATE_CHECK_FILE = join(DATA_DIR, "update-check.json");
|
|
18
|
+
var LIBRARY_HOME_ENV = `${ENV_PREFIX}_LIBRARY_HOME`;
|
|
19
|
+
var NO_UPDATE_CHECK_ENV = `${ENV_PREFIX}_NO_UPDATE_CHECK`;
|
|
20
|
+
var DESIGN_HARNESS_ENV = `${ENV_PREFIX}_DESIGN_HARNESS`;
|
|
21
|
+
var API_URL_ENV = `${ENV_PREFIX}_API_URL`;
|
|
22
|
+
var NO_IDLE_STOP_ENV = `${ENV_PREFIX}_NO_IDLE_STOP`;
|
|
23
|
+
var CONTROL_PORT_ENV = `${ENV_PREFIX}_CONTROL_PORT`;
|
|
24
|
+
var MAX_ATTACHMENT_BYTES = 8 * 1024 * 1024;
|
|
25
|
+
var ATTACHMENT_GRACE_MS = 24 * 60 * 6e4;
|
|
26
|
+
var UPDATE_CHECK_TTL_MS = 4 * 60 * 6e4;
|
|
27
|
+
var IDLE_STOP_MS = 30 * 6e4;
|
|
28
|
+
var SOURCE_ATTR = "data-ez-source";
|
|
29
|
+
|
|
30
|
+
// src/project-root.ts
|
|
31
|
+
import { existsSync } from "node:fs";
|
|
32
|
+
import { dirname, join as join2, parse, resolve } from "node:path";
|
|
33
|
+
function projectRoot(from) {
|
|
34
|
+
let dir = resolve(from);
|
|
35
|
+
const { root } = parse(dir);
|
|
36
|
+
for (; ; ) {
|
|
37
|
+
if (existsSync(join2(dir, ".git")) || existsSync(join2(dir, "package.json"))) return dir;
|
|
38
|
+
if (dir === root) return resolve(from);
|
|
39
|
+
dir = dirname(dir);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// src/vite.ts
|
|
44
|
+
function walk(node, visit) {
|
|
45
|
+
visit(node);
|
|
46
|
+
for (const value of Object.values(node)) {
|
|
47
|
+
if (Array.isArray(value)) {
|
|
48
|
+
for (const child of value) {
|
|
49
|
+
if (child && typeof child === "object" && "type" in child) {
|
|
50
|
+
walk(child, visit);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
} else if (value && typeof value === "object" && "type" in value) {
|
|
54
|
+
walk(value, visit);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function ezdesignSource() {
|
|
59
|
+
let root = projectRoot(process.cwd());
|
|
60
|
+
return {
|
|
61
|
+
name: "ezdesign:source",
|
|
62
|
+
apply: "serve",
|
|
63
|
+
enforce: "pre",
|
|
64
|
+
configResolved(config) {
|
|
65
|
+
root = projectRoot(config.root);
|
|
66
|
+
},
|
|
67
|
+
transform(code, id) {
|
|
68
|
+
const [file] = id.split("?");
|
|
69
|
+
if (!file || !/\.[jt]sx$/.test(file) || file.includes("node_modules") || id.startsWith("\0")) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
let ast;
|
|
73
|
+
try {
|
|
74
|
+
ast = parse2(code, {
|
|
75
|
+
sourceType: "module",
|
|
76
|
+
plugins: ["jsx", "typescript", "decorators-legacy"]
|
|
77
|
+
});
|
|
78
|
+
} catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
const rel = relative(root, file);
|
|
82
|
+
const s = new MagicString(code);
|
|
83
|
+
let changed = false;
|
|
84
|
+
walk(ast.program, (node) => {
|
|
85
|
+
if (node.type !== "JSXOpeningElement") return;
|
|
86
|
+
const name = node.name;
|
|
87
|
+
if (!name || name.type !== "JSXIdentifier") return;
|
|
88
|
+
const tag = name["name"];
|
|
89
|
+
if (typeof tag !== "string" || !/^[a-z]/.test(tag)) return;
|
|
90
|
+
const attrs = node["attributes"];
|
|
91
|
+
const hasAttr = attrs?.some(
|
|
92
|
+
(a) => a.type === "JSXAttribute" && a["name"]?.["name"] === SOURCE_ATTR
|
|
93
|
+
);
|
|
94
|
+
if (hasAttr || typeof name.end !== "number" || !node.loc) return;
|
|
95
|
+
s.appendRight(name.end, ` ${SOURCE_ATTR}="${rel}:${node.loc.start.line}"`);
|
|
96
|
+
changed = true;
|
|
97
|
+
});
|
|
98
|
+
if (!changed) return null;
|
|
99
|
+
return { code: s.toString(), map: s.generateMap({ hires: true }).toString() };
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
export {
|
|
104
|
+
ezdesignSource
|
|
105
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,84 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cychien/ezdesign",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.9.1",
|
|
4
|
+
"description": "Annotate your live dev app in the browser; feedback flows to your local coding agent, which edits the source. Review, iterate, repeat.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"author": "Justin Chien",
|
|
7
|
+
"homepage": "https://github.com/cychien/ezdesign#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/cychien/ezdesign.git"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/cychien/ezdesign/issues"
|
|
14
|
+
},
|
|
15
|
+
"bin": {
|
|
16
|
+
"ezdesign": "dist/cli.mjs"
|
|
17
|
+
},
|
|
18
|
+
"exports": {
|
|
19
|
+
"./vite": {
|
|
20
|
+
"types": "./dist/vite.d.ts",
|
|
21
|
+
"import": "./dist/vite.mjs"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"skills/ezdesign",
|
|
27
|
+
"LICENSE",
|
|
28
|
+
"README.md"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "node scripts/build.mjs",
|
|
32
|
+
"dev": "node scripts/dev.mjs",
|
|
33
|
+
"dev:agent": "node scripts/dev-agent.mjs",
|
|
34
|
+
"dev:cli": "node scripts/dev-cli.mjs",
|
|
35
|
+
"brand": "node scripts/brand-wordmark.mjs && node --import tsx scripts/brand-assets.mjs",
|
|
36
|
+
"check": "npm run build && npm run typecheck && npm test",
|
|
37
|
+
"typecheck": "tsc --noEmit && tsc -p src/extension --noEmit && tsc -p fixtures/playground --noEmit",
|
|
38
|
+
"test": "node scripts/test.mjs",
|
|
39
|
+
"version": "node scripts/sync-skill-version.mjs && git add skills/ezdesign/SKILL.md",
|
|
40
|
+
"prepublishOnly": "npm run check"
|
|
41
|
+
},
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=22.13"
|
|
47
|
+
},
|
|
48
|
+
"keywords": [
|
|
49
|
+
"agent",
|
|
50
|
+
"review",
|
|
51
|
+
"annotation",
|
|
52
|
+
"feedback",
|
|
53
|
+
"dev-server",
|
|
54
|
+
"vite",
|
|
55
|
+
"claude-code"
|
|
56
|
+
],
|
|
57
|
+
"license": "MIT",
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"@agentclientprotocol/sdk": "^1.4.0",
|
|
60
|
+
"@babel/parser": "^7.26.0",
|
|
61
|
+
"@hugeicons/core-free-icons": "^4.3.5",
|
|
62
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
63
|
+
"express": "^5.1.0",
|
|
64
|
+
"http-proxy-middleware": "^3.0.3",
|
|
65
|
+
"magic-string": "^0.30.17",
|
|
66
|
+
"open": "^10.1.0",
|
|
67
|
+
"zod": "^4.5.4"
|
|
68
|
+
},
|
|
69
|
+
"devDependencies": {
|
|
70
|
+
"@fontsource/geist-sans": "5.3.0",
|
|
71
|
+
"@types/express": "^5.0.0",
|
|
72
|
+
"@types/node": "^22.10.0",
|
|
73
|
+
"@types/react": "^19.2.18",
|
|
74
|
+
"@types/react-dom": "^19.2.5",
|
|
75
|
+
"@vitejs/plugin-react": "^4.7.0",
|
|
76
|
+
"esbuild": "^0.25.0",
|
|
77
|
+
"opentype.js": "1.3.4",
|
|
78
|
+
"react": "^19.2.8",
|
|
79
|
+
"react-dom": "^19.2.8",
|
|
80
|
+
"tsx": "^4.23.11",
|
|
81
|
+
"typescript": "^5.7.0",
|
|
82
|
+
"vite": "^6.0.0"
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ezdesign
|
|
3
|
+
description: Open an ezdesign review session on a running web page. The user annotates the page in the browser, and each batch reaches the coding agent with exact source locations. Use when the user invokes /ezdesign, or asks to review, annotate or give visual feedback on a running page, or to explore designs on a canvas.
|
|
4
|
+
metadata:
|
|
5
|
+
version: 0.9.1
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# ezdesign
|
|
9
|
+
|
|
10
|
+
Open a review: [reference/start.md](reference/start.md). A bare `/ezdesign`, with nothing after
|
|
11
|
+
it, is that request: open a review on the project's running dev server.
|
|
12
|
+
|
|
13
|
+
## Inside a review session
|
|
14
|
+
|
|
15
|
+
The daemon has done the routing: its prompt says whether the turn is a feedback batch or an
|
|
16
|
+
explore round, and what to do with it. Do not route again. A batch carries the page's `url`, the
|
|
17
|
+
annotated `items` with their `anchor` (source `file:line`, selector, viewport), `references`, and
|
|
18
|
+
`attachments`. An explore adds one element's capture - its markup, computed styles, slot and the
|
|
19
|
+
page's tokens - and takes each variant back through a tool, editing no file.
|
|
20
|
+
|
|
21
|
+
## Canvases
|
|
22
|
+
|
|
23
|
+
An ACP review session also gives you the `ezdesign-canvas` tools. Each turn's prompt says which tab
|
|
24
|
+
the user sent the batch from and which canvases exist.
|
|
25
|
+
|
|
26
|
+
- When the user wants to explore directions, compare options or see a design before it is built,
|
|
27
|
+
put each one in a frame on a canvas: one `add_frame` per design, as soon as each is ready, on the
|
|
28
|
+
canvas the turn's prompt names. Call `create_canvas` only when no canvas is open or the user asks
|
|
29
|
+
for a new one. Leave `position` out; frames are placed for you.
|
|
30
|
+
- A frame is a complete HTML document with its own `<head>` and styles, at a real viewport width
|
|
31
|
+
(390 phone, 768 tablet, 1280 desktop). Nothing is shared between frames.
|
|
32
|
+
- An item whose `anchor.frame` is set was made inside a frame: `anchor.source` is a line of that
|
|
33
|
+
frame's file under `.ezdesign/canvases/`. Change a frame with `update_frame` (pass the
|
|
34
|
+
`expectedHash` you last read) or by editing its file.
|
|
35
|
+
- Never edit `canvas.json` or `layout.json` by hand. Where a frame sits and its size are the user's.
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Baseline
|
|
2
|
+
|
|
3
|
+
> Only for a turn whose prompt says 增強設計 (the design harness) is on for it, or a request that
|
|
4
|
+
> asks for it by name. In any other turn, handle the request as if you had never read this file.
|
|
5
|
+
|
|
6
|
+
This is the taste the harness is built on, and the standard every piece is held to, whatever its
|
|
7
|
+
direction. `DESIGN.md` sets how this product looks; the baseline is what good means under any look.
|
|
8
|
+
The user's own words, in the request or in `.ezdesign/taste.md`, can move a rule here; nothing else
|
|
9
|
+
does.
|
|
10
|
+
|
|
11
|
+
Everything below serves one goal: the person on the page understands it quickly and gets done what
|
|
12
|
+
they came to do.
|
|
13
|
+
|
|
14
|
+
## Usability comes first
|
|
15
|
+
|
|
16
|
+
- **Never hide what matters to look minimal.** The information and the actions the page exists for
|
|
17
|
+
stay in view and within one step. Only what matters less goes a level deeper, behind a menu, a
|
|
18
|
+
tab, a disclosure or a settings page. A product with a lot to do, like a Stripe or Shopify
|
|
19
|
+
dashboard, cannot be sparse, but it keeps the everyday work in front.
|
|
20
|
+
- **Easy to use first; the looks follow.** A paragraph set in one colour leaves the reader to find
|
|
21
|
+
what matters. Darken what carries the point and lighten the rest, and it reads at ease and looks
|
|
22
|
+
better for it.
|
|
23
|
+
- **Design serves the viewer and the business.** `PRODUCT.md` says who they are and what they need.
|
|
24
|
+
A striking home page is not there to be beautiful; it is there so the visitor stays.
|
|
25
|
+
- **Start from the least the screen can do its job with.** Add an element only when it is needed;
|
|
26
|
+
each one has to earn its place.
|
|
27
|
+
|
|
28
|
+
The check: every choice can be justified by what it does for the viewer or the business, and taking
|
|
29
|
+
any element away makes the screen worse at its job.
|
|
30
|
+
|
|
31
|
+
## Aim for outstanding, never average
|
|
32
|
+
|
|
33
|
+
- **Do not settle for the safe average.** The first design that comes to mind is usually the mean
|
|
34
|
+
of every interface you have seen: competent, interchangeable, and forgettable. Aim for the
|
|
35
|
+
version an outstanding designer would ship, and for refined over merely correct.
|
|
36
|
+
- **Outstanding comes from judgment and precision, not novelty.** Hierarchy that is exactly right,
|
|
37
|
+
spacing that is exactly right, details finished down to the states: that is what sets great work
|
|
38
|
+
apart. A clever gimmick is not the way there.
|
|
39
|
+
- **Safe is not the same as right.** Do not reach for the conventional treatment because it cannot
|
|
40
|
+
be wrong. Use it when it is the best answer for this screen, and then make it better than usual.
|
|
41
|
+
|
|
42
|
+
The check: someone who cares about design would stop and notice how well it is made. A piece
|
|
43
|
+
nobody would comment on is not done.
|
|
44
|
+
|
|
45
|
+
## Know what the point of the page is
|
|
46
|
+
|
|
47
|
+
The point is what the person most wants to get done on this page, or what the business most wants
|
|
48
|
+
them to do there. Purpose, in `make.md`, works it out; every rule below leans on it, so decide it
|
|
49
|
+
before you style anything.
|
|
50
|
+
|
|
51
|
+
It depends on the page, not on the kind of element:
|
|
52
|
+
|
|
53
|
+
- On a blog post, the body text is the point.
|
|
54
|
+
- On a list of posts, each post's title is the point, and its excerpt is not.
|
|
55
|
+
- On a pricing page, the plans and the action that buys one are the point.
|
|
56
|
+
|
|
57
|
+
One point per view, sometimes one per repeated item. When everything is emphasised, nothing is:
|
|
58
|
+
a page with many points has none.
|
|
59
|
+
|
|
60
|
+
## Design for the whole screen
|
|
61
|
+
|
|
62
|
+
- **A component is designed in its screen, never on its own.** Before changing one, work out what
|
|
63
|
+
this screen is for: what the person should get done here, and what they should feel, such as
|
|
64
|
+
confident, calm or eager to start. Style the component for that.
|
|
65
|
+
- **Its role on the screen sets its weight.** Its size, colour, emphasis and spacing are decided
|
|
66
|
+
against its neighbours and the point of the screen: as loud as its role, no louder.
|
|
67
|
+
- **Better alone and worse on the screen is worse.** A change that polishes the component and
|
|
68
|
+
pulls the screen out of balance has failed.
|
|
69
|
+
|
|
70
|
+
The check: look at the whole screen after the change, not only the component, and it reads better
|
|
71
|
+
as a whole.
|
|
72
|
+
|
|
73
|
+
## Hierarchy is always visible
|
|
74
|
+
|
|
75
|
+
- **Rank shows before a word is read.** Size, weight, spacing, borders, colour and type set what
|
|
76
|
+
leads, what follows and what recedes. The eye lands first on the point.
|
|
77
|
+
- **Nearness and likeness mean relation.** Put related things close together and give them a
|
|
78
|
+
shared style: a ground laid under the group, the same colour, the same shape or treatment. A
|
|
79
|
+
ground or border that holds a group is its one container, so its members are not boxed again.
|
|
80
|
+
- **Distance and difference mean separation.** Unrelated information sits further apart, in a
|
|
81
|
+
different style, or across a divider. The same means that set groups apart bind the members of a
|
|
82
|
+
group together.
|
|
83
|
+
|
|
84
|
+
The check: at a glance, before reading anything, it is plain what leads and which things go
|
|
85
|
+
together.
|
|
86
|
+
|
|
87
|
+
## Grey first, colour sparingly
|
|
88
|
+
|
|
89
|
+
- **Build on the grey tokens.** Most of a page is greys: the ground, the surfaces, the text, the
|
|
90
|
+
lines. The primary colour appears in small amounts, on what the user acts on or needs to find,
|
|
91
|
+
such as the main action, the current selection or a live state.
|
|
92
|
+
- **The darkest greys are for the point.** Near-black is kept for what matters most, such as the
|
|
93
|
+
key heading or the key text. Everything else steps down to a lighter grey, so the point is what
|
|
94
|
+
stands out.
|
|
95
|
+
- **Lines and borders are light grey.** A divider, a card's edge or a table rule uses the lightest
|
|
96
|
+
grey that still separates. Do not reach for a dark-grey token to draw a line. A form field's
|
|
97
|
+
outline can be a step darker so it can be found, but not dark enough to compete with the text.
|
|
98
|
+
- **Weight and colour balance each other.** A heavier weight emphasises and a lighter colour
|
|
99
|
+
recedes, so the two can be set against each other: a list a step above regular weight (around
|
|
100
|
+
450) in a lighter grey stays quiet beside the heading while still reading full. Regular weight
|
|
101
|
+
made lighter looks thin, and the whole UI with it. An in-between weight needs a variable font;
|
|
102
|
+
with only static weights it snaps to the nearest one, so check it renders.
|
|
103
|
+
|
|
104
|
+
## Quality comes from proportion and detail
|
|
105
|
+
|
|
106
|
+
- **Quality is built, not added.** It comes from proportion, a clear type scale, precise spacing,
|
|
107
|
+
exact alignment and finished states: hover, focus, pressed, selected, disabled, empty, loading,
|
|
108
|
+
error. It never comes from a gimmick.
|
|
109
|
+
- **Spacing is a decision each time, and it depends on what the page is for.**
|
|
110
|
+
- On a page that presents, such as a landing or marketing page, spacing only has to feel
|
|
111
|
+
comfortable: room to breathe around each section and its content.
|
|
112
|
+
- On a page people work in, such as a dashboard, a table, a settings page or an editor, spacing
|
|
113
|
+
is compact but easy to scan: tight rows, consistent columns and edges the eye can run down, and
|
|
114
|
+
groups set apart by a clearly larger gap than the one inside them.
|
|
115
|
+
- **Type runs small.** Most text is set smaller than a first instinct picks, and that restraint is
|
|
116
|
+
much of what makes a screen look refined. Rank comes from weight, colour and space as much as
|
|
117
|
+
from size, so a heading does not need to be big to lead. Large, heavy type needs a real reason,
|
|
118
|
+
such as the one headline of a landing page or the number that is the point of a dashboard; it is
|
|
119
|
+
never the default for titles, labels or buttons. Small never means hard to read: the craft
|
|
120
|
+
floor's legibility still holds.
|
|
121
|
+
- **Few elements, each finished with care.** Simple is about how many things there are, not how
|
|
122
|
+
plainly each is made.
|
|
123
|
+
|
|
124
|
+
## Decoration serves, never competes
|
|
125
|
+
|
|
126
|
+
- **Decoration is welcome when it belongs.** A considered shadow, ground, icon, illustration or
|
|
127
|
+
transition that makes a piece feel finished is worth it. Flatness and bareness are not virtues in
|
|
128
|
+
themselves; refined beats plain.
|
|
129
|
+
- **It stays in harmony with the whole,** in the page's colours, radii and weight.
|
|
130
|
+
- **It never takes attention from the task.** Nothing decorative is louder than the point of the
|
|
131
|
+
page.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Build the project's design system and components
|
|
2
|
+
|
|
3
|
+
> Only for a turn whose prompt says 增強設計 (the design harness) is on for it, or a request that
|
|
4
|
+
> asks for it by name. In any other turn, handle the request as if you had never read this file.
|
|
5
|
+
|
|
6
|
+
## What this step is
|
|
7
|
+
|
|
8
|
+
`DESIGN.md` says what the visual language is, in broad strokes. This step makes it real in the
|
|
9
|
+
stack: its anchors and its character become the design system the project actually builds with, and a set of components is built on
|
|
10
|
+
that system in the project's own component library. That set is the foundation the making step
|
|
11
|
+
builds UI from.
|
|
12
|
+
|
|
13
|
+
The stack step has already recorded which design system and which component library the project
|
|
14
|
+
uses, in `design_system` and `components` in `.ezdesign/config.json`. This step builds to them and
|
|
15
|
+
adds one field, the list of primitives that now exist, so the making step reuses rather than
|
|
16
|
+
reinvents:
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
"components": {
|
|
20
|
+
"library": "shadcn",
|
|
21
|
+
"path": "src/components/ui",
|
|
22
|
+
"primitives": ["button", "input", "card", "dialog"]
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## The invariant: nothing visible changes
|
|
27
|
+
|
|
28
|
+
When this step is done, every existing screen renders exactly as it did. The project's rules and
|
|
29
|
+
documents get more complete; its UI does not move. Concretely:
|
|
30
|
+
|
|
31
|
+
- **The existing system wins.** Where the project's config or components disagree with
|
|
32
|
+
`DESIGN.md`, keep the project's value untouched and report the divergence to the user; the
|
|
33
|
+
correction, if they want one, is a refresh of `DESIGN.md`, not an edit to what renders.
|
|
34
|
+
- **New tokens take new keys.** A token `DESIGN.md` has and the system lacks is added under a key
|
|
35
|
+
nothing uses yet. An existing key is never renamed, retuned or repurposed.
|
|
36
|
+
- **New components do not replace used ones.** An existing component stays as it is, even when a
|
|
37
|
+
cleaner one could be built; new primitives are added beside it and wired into nothing.
|
|
38
|
+
|
|
39
|
+
## Step 1: Load current state
|
|
40
|
+
|
|
41
|
+
`PRODUCT.md`, `DESIGN.md` and `.ezdesign/config.json` with a stack and a browser tool are the
|
|
42
|
+
prerequisites; run [product.md](product.md), [design.md](design.md) or [stack.md](stack.md) for
|
|
43
|
+
whichever is missing, and come back.
|
|
44
|
+
|
|
45
|
+
Then read what the project already has: the design system at `design_system.path`, and the
|
|
46
|
+
components at `components.path`, with each one's variants and states. If `components.primitives`
|
|
47
|
+
is recorded and still matches the directory, there is nothing to do here; say so in one line and
|
|
48
|
+
go on.
|
|
49
|
+
|
|
50
|
+
## Step 2: The design system
|
|
51
|
+
|
|
52
|
+
Carry `DESIGN.md`'s anchors - its colours, type roles and radius - into the design system, in the
|
|
53
|
+
idiom `design_system.kind` names, under the invariant above. When `design_system` is `null`, create
|
|
54
|
+
one in the stack's own idiom and record it there. Keep `DESIGN.md`'s descriptive names (`canvas`,
|
|
55
|
+
`oxblood-deep`) as the keys, so a class or variable reads like the document.
|
|
56
|
+
|
|
57
|
+
The document is deliberately broad, so the scales it does not write out are this step's: where the
|
|
58
|
+
system has none yet, derive them in the language's character - a type ramp with the contrast its
|
|
59
|
+
Typography describes, a spacing scale with the density its Layout describes, shadows as its
|
|
60
|
+
Elevation & Depth describes. They live in the design system; `DESIGN.md` is not edited for them.
|
|
61
|
+
|
|
62
|
+
## Step 3: The component set
|
|
63
|
+
|
|
64
|
+
Build the primitives the product needs and the project lacks: from `PRODUCT.md`'s workflows, the
|
|
65
|
+
set the making step will reach for - button, input, select, checkbox, card, dialog, navigation,
|
|
66
|
+
badge, tooltip, table - and no component the product has no use for. Each carries every state it
|
|
67
|
+
can be in: hover, focus-visible, active, disabled, error, loading where it applies.
|
|
68
|
+
|
|
69
|
+
**Write them the way the project already writes components.** `components.library` says how:
|
|
70
|
+
|
|
71
|
+
- **A component library** (`shadcn`, `mui`, `chakra`, `mantine`, and so on): new primitives come
|
|
72
|
+
from that library, used the way it is meant to be used. With `shadcn`, add them through its own
|
|
73
|
+
CLI (`npx shadcn@latest add <name>`) so they land where `components.json` says and in shadcn's
|
|
74
|
+
structure; check first which ones exist, add only the missing ones, and never pass `--overwrite`.
|
|
75
|
+
The CLI can touch shared files, the global stylesheet and the Tailwind config, so read its diff
|
|
76
|
+
against the invariant. With any other library, compose its components and theme them through its
|
|
77
|
+
theme, not with overrides on each instance.
|
|
78
|
+
- **`custom`**: written by hand, after reading two or three of the existing components, in their
|
|
79
|
+
file layout, naming, props, variant API and styling approach.
|
|
80
|
+
- **`none`**: written by hand in the stack's idiom, at `components.path`.
|
|
81
|
+
|
|
82
|
+
Whatever the source, style every primitive from the design system's tokens, so it speaks the
|
|
83
|
+
product's language. A library's default look stays where it already fits `DESIGN.md`'s direction;
|
|
84
|
+
it is restyled only where it does not.
|
|
85
|
+
|
|
86
|
+
Look for precedent before designing a component: [inspiration.md](inspiration.md), searched by what
|
|
87
|
+
the component is and has to show.
|
|
88
|
+
|
|
89
|
+
## Step 4: Verify the invariant, then record
|
|
90
|
+
|
|
91
|
+
Before writing anything, screenshot the project's main screens:
|
|
92
|
+
`node <skill>/scripts/page.mjs shots <url> <dir>/before <path> ...`, where `<skill>` is the folder
|
|
93
|
+
above this file's `reference/`, `<url>` the running page and `<dir>` a folder outside the project;
|
|
94
|
+
it shoots every path at desktop and at 390px. After writing, shoot them again into `<dir>/after`
|
|
95
|
+
and run `node <skill>/scripts/page.mjs compare <dir>/before <dir>/after`. Every line must say
|
|
96
|
+
`same`; any difference is a change to undo, not a finding to report. Only when the script cannot
|
|
97
|
+
run - there is no Chrome - do the same with the browser tool. Then write `components.primitives` into
|
|
98
|
+
`.ezdesign/config.json`, and `design_system` when this step created one.
|
|
99
|
+
|
|
100
|
+
Tell the user in a few lines what the design system now carries, which primitives were added and
|
|
101
|
+
where, and every divergence between `DESIGN.md` and the existing system that you left as it was.
|
|
102
|
+
When this step runs as part of a larger request - a setup turn, or a batch that needed it first - skip this and let that request's reply cover it. Then end, or resume the request that invoked this.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
# Review a piece of UI
|
|
2
|
+
|
|
3
|
+
> Only for a turn whose prompt says 增強設計 (the design harness) is on for it, or a request that
|
|
4
|
+
> asks for it by name. In any other turn, handle the request as if you had never read this file.
|
|
5
|
+
|
|
6
|
+
You are an independent reviewer. Someone else made this change; you did not see them make it, and
|
|
7
|
+
their reasons are not your concern. You judge what is on the screen, the way the person who asked
|
|
8
|
+
for it will when they look. You change nothing: no file is edited, created or deleted.
|
|
9
|
+
|
|
10
|
+
Be the hard one to please. A maker's view of their own work is generous by nature, and you are
|
|
11
|
+
here because it is. Assume the piece is not good enough until what you see proves it is, and
|
|
12
|
+
praise nothing you cannot point at.
|
|
13
|
+
|
|
14
|
+
## What you have
|
|
15
|
+
|
|
16
|
+
- The user's request, in their words.
|
|
17
|
+
- Where it lives: the page's url, the viewport the request was made at, and the element or file the
|
|
18
|
+
change is in.
|
|
19
|
+
- Screenshots of the piece in place, taken by the maker at that viewport - and at the narrowest
|
|
20
|
+
width when the piece reflows - with enough of the page around it to see how it sits, and one of
|
|
21
|
+
the whole page.
|
|
22
|
+
- `PRODUCT.md` at the project root: who the page is for, and what it has to do for them and for the
|
|
23
|
+
business behind it.
|
|
24
|
+
- `DESIGN.md`: the visual language the page speaks. A direction, not a rule; judge whether the
|
|
25
|
+
piece belongs, not whether it complies.
|
|
26
|
+
- `baseline.md` and `principles.md`, which the brief names: the standard every piece is held to,
|
|
27
|
+
which these criteria are drawn from, and the craft floor. Read both before you score.
|
|
28
|
+
- `.ezdesign/taste.md`, when it exists: what this user has shown they consider good, learned from
|
|
29
|
+
their own feedback. Read it before you judge; it outranks the examples below where they disagree.
|
|
30
|
+
|
|
31
|
+
## Look first
|
|
32
|
+
|
|
33
|
+
Study the screenshots before you score: the pixels are the work, and they are what the user will
|
|
34
|
+
see. Judge details on the piece's own screenshots, which are at twice the page's pixels, and the
|
|
35
|
+
rhythm of the page on the whole-page one; the files named `-hover`, `-focus` and `-active` are the
|
|
36
|
+
piece in those states, and a state that looks wrong is a flaw of the piece. Open the page yourself, with the browser tool `.ezdesign/config.json` names, only for what a
|
|
37
|
+
screenshot cannot show - a hover, an open menu, a state behind a click. Then glance at the diff
|
|
38
|
+
(`git diff`) for one thing only: whether a control the project has a component for - a button, an
|
|
39
|
+
input, a card, a dialog, the ones `.ezdesign/config.json` lists under `components` - was built from
|
|
40
|
+
that component rather than by hand. Do not tour other widths or measure the DOM: a review takes a
|
|
41
|
+
minute, and the user is waiting on it. Judge **what changed**,
|
|
42
|
+
and what it does to the page: a flaw elsewhere that the change did not cause is not this piece's.
|
|
43
|
+
|
|
44
|
+
## The bar
|
|
45
|
+
|
|
46
|
+
The baseline, in `baseline.md`: outstanding rather than the safe average, designed for the whole
|
|
47
|
+
screen rather than the component alone, the point of the page first and plain, hierarchy visible
|
|
48
|
+
before a word is read, greys carrying the page and colour kept for the point, spacing fit for what
|
|
49
|
+
the page is for, and quality built from proportion and finished detail. A clever idea earns
|
|
50
|
+
nothing on its own; a useful move done cleanly is what impresses. Hold the piece to that bar, not
|
|
51
|
+
to what is usual for the page it is on.
|
|
52
|
+
|
|
53
|
+
## The criteria
|
|
54
|
+
|
|
55
|
+
Score each from 1 to 10. They are listed in order of weight.
|
|
56
|
+
|
|
57
|
+
- **Useful**: at a glance, before reading a word, can the viewer tell what matters here and what to
|
|
58
|
+
do? Does it serve the person and the business `PRODUCT.md` describes? Emphasis falls on what
|
|
59
|
+
carries the point, and the rest recedes.
|
|
60
|
+
- *Hierarchy*: is what the eye lands on first what the user needs most?
|
|
61
|
+
- **Refined**: is it built to the baseline's detail? Proportion and a clear type scale, with type
|
|
62
|
+
kept small unless size has a reason; spacing comfortable on a page that presents, compact and
|
|
63
|
+
easy to scan on a page people work in; edges that line up; hover, focus, selected, disabled,
|
|
64
|
+
empty and error states finished. Greys carry it,
|
|
65
|
+
lines are light, the darkest text and the colour go to the point only, and any decoration sits in
|
|
66
|
+
harmony without pulling focus. It comes from precision and refinement, never from a gimmick. A
|
|
67
|
+
competent piece nobody would comment on scores 5.
|
|
68
|
+
- **Clear**: related things sit together and share a style; unrelated things are set apart by
|
|
69
|
+
space, a faint rule, a different ground or shape. There is one primary element and it is obvious.
|
|
70
|
+
Nothing is there that the job does not need.
|
|
71
|
+
- *Proportion*: does anything secondary take more room than it earns?
|
|
72
|
+
- *Density*: is the information too spread out, or too crowded, for what it holds?
|
|
73
|
+
- **Fits**: it belongs on this page - its type, colour, spacing, radii and tone agree with what
|
|
74
|
+
surrounds it, or depart on purpose and look it - and the page as a whole still looks good with
|
|
75
|
+
it: it does not pull focus from what the page leads with, crowd its neighbours, or break the
|
|
76
|
+
rhythm of the sections around it. A piece that is lovely alone and hurts the page scores low.
|
|
77
|
+
- **Craft**: alignment, spacing rhythm, contrast, type hierarchy, states. Nothing overflows or
|
|
78
|
+
clips, and no card is nested in a card; a row's hover, selected or current-step fill is a
|
|
79
|
+
highlight, not a card. A control the project has a component for is built from it; a
|
|
80
|
+
hand-made copy that only looks the same is a flaw, since the two drift apart the next time the
|
|
81
|
+
component changes.
|
|
82
|
+
- *Consistency*: do headings, spacing and actions at the same level match each other?
|
|
83
|
+
- *States*: would empty data, long text or an error message break the layout? Judge it from the
|
|
84
|
+
diff - fixed heights, text that cannot wrap, a list with no empty state - without building the
|
|
85
|
+
states.
|
|
86
|
+
|
|
87
|
+
## What good has looked like
|
|
88
|
+
|
|
89
|
+
What this skill's author picked when judging blind, as the kind of judgment to make - never as
|
|
90
|
+
treatments to ask for. `.ezdesign/taste.md` carries on from here for each user.
|
|
91
|
+
|
|
92
|
+
- Hierarchy beat decoration: making what matters stand out did more than restyling everything.
|
|
93
|
+
- Plain beat clever: a well-made clever idea lost to a clear, direct treatment every time.
|
|
94
|
+
- A card nested in a card lost every time, however polished the cards.
|
|
95
|
+
|
|
96
|
+
## The verdict
|
|
97
|
+
|
|
98
|
+
- **ship**: every criterion is 7 or above and nothing breaks the craft floor.
|
|
99
|
+
- **refine**: the direction is right and the execution is not. Say exactly what to change; when
|
|
100
|
+
Refined is the low score, one of the items is the move that would get it there.
|
|
101
|
+
- **pivot**: the direction itself fails Useful or Clear, and polishing it will not fix that. Say why
|
|
102
|
+
in one line, and what a better direction would get right. A failure that one adjustment can fix -
|
|
103
|
+
an order, a count, one item that must not be missed brought forward - is refine, with that
|
|
104
|
+
adjustment as the fix, whatever the direction.
|
|
105
|
+
|
|
106
|
+
## When there is an earlier version
|
|
107
|
+
|
|
108
|
+
The brief may name a screenshot of an earlier version of the same piece. Open it and compare it with
|
|
109
|
+
what is on the page now, at the same crop. Score the version on the page, and say which of the two
|
|
110
|
+
is better as a whole - not which fixed more of a list.
|
|
111
|
+
|
|
112
|
+
## Reply
|
|
113
|
+
|
|
114
|
+
Reply with exactly this, and nothing before or after it:
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
useful 0/10 · refined 0/10 · clear 0/10 · fits 0/10 · craft 0/10
|
|
118
|
+
verdict: ship | refine | pivot
|
|
119
|
+
better: current | earlier - <one reason>
|
|
120
|
+
1. <where on the page> - <what is wrong> - <what to do instead>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
The `better:` line only when there is an earlier version to compare with.
|
|
124
|
+
|
|
125
|
+
At most three numbered items, the one that matters most first, each specific enough to act on
|
|
126
|
+
without looking again: name the element and the change, not a quality ("the three status labels in
|
|
127
|
+
the right column: same grey as the body text, so the stuck one does not stand out - make it the
|
|
128
|
+
risk colour and the others muted"). A **ship** verdict may list up to two small improvements, or
|
|
129
|
+
none.
|