@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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +615 -2
  3. package/dist/announcements/template-ERNKAKI5.svg +33 -0
  4. package/dist/cli.mjs +10095 -0
  5. package/dist/constants.d.ts +73 -0
  6. package/dist/events-worker.js +77 -0
  7. package/dist/extension/g/0.9.1-16d05e759b/dialog.css +1 -0
  8. package/dist/extension/g/0.9.1-16d05e759b/dialog.html +13 -0
  9. package/dist/extension/g/0.9.1-16d05e759b/dialog.js +1 -0
  10. package/dist/extension/g/0.9.1-16d05e759b/icon-128.png +0 -0
  11. package/dist/extension/g/0.9.1-16d05e759b/icon-16.png +0 -0
  12. package/dist/extension/g/0.9.1-16d05e759b/icon-32.png +0 -0
  13. package/dist/extension/g/0.9.1-16d05e759b/icon-48.png +0 -0
  14. package/dist/extension/g/0.9.1-16d05e759b/media.html +9 -0
  15. package/dist/extension/g/0.9.1-16d05e759b/media.js +1 -0
  16. package/dist/extension/g/0.9.1-16d05e759b/page.js +39 -0
  17. package/dist/extension/g/0.9.1-16d05e759b/setup.css +1 -0
  18. package/dist/extension/g/0.9.1-16d05e759b/setup.html +17 -0
  19. package/dist/extension/g/0.9.1-16d05e759b/setup.js +1 -0
  20. package/dist/extension/g/0.9.1-16d05e759b/worker.js +57 -0
  21. package/dist/extension/manifest.json +57 -0
  22. package/dist/native-host.mjs +20594 -0
  23. package/dist/overlay.css +422 -0
  24. package/dist/overlay.js +2642 -0
  25. package/dist/project-root.d.ts +10 -0
  26. package/dist/shell.css +4060 -0
  27. package/dist/shell.js +8284 -0
  28. package/dist/vite.d.ts +26 -0
  29. package/dist/vite.mjs +105 -0
  30. package/package.json +82 -4
  31. package/skills/ezdesign/SKILL.md +35 -0
  32. package/skills/ezdesign/reference/baseline.md +131 -0
  33. package/skills/ezdesign/reference/components.md +102 -0
  34. package/skills/ezdesign/reference/critique.md +129 -0
  35. package/skills/ezdesign/reference/design.md +247 -0
  36. package/skills/ezdesign/reference/inspiration.md +125 -0
  37. package/skills/ezdesign/reference/make.md +331 -0
  38. package/skills/ezdesign/reference/principles.md +130 -0
  39. package/skills/ezdesign/reference/product.md +127 -0
  40. package/skills/ezdesign/reference/stack.md +123 -0
  41. package/skills/ezdesign/reference/start.md +78 -0
  42. package/skills/ezdesign/scripts/page.d.mts +61 -0
  43. 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.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
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.