@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
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# Define the project's stack and tools
|
|
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 file is
|
|
7
|
+
|
|
8
|
+
`.ezdesign/config.json` at the project root records the architecture the application is built on,
|
|
9
|
+
the design system and component library it already uses, and where the tools used to work on it
|
|
10
|
+
come from. Later steps read it instead of rediscovering any of it: the stack and the component
|
|
11
|
+
library tell the components and making steps what code to write and in which style, and the tools
|
|
12
|
+
tell every step how to look at the running page.
|
|
13
|
+
|
|
14
|
+
The platform is web only, so the stack is a web stack.
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"platform": "web",
|
|
19
|
+
"stack": ["vite", "react", "typescript", "tailwind"],
|
|
20
|
+
"design_system": { "kind": "tailwind", "path": "tailwind.config.ts" },
|
|
21
|
+
"components": { "library": "shadcn", "path": "src/components/ui" },
|
|
22
|
+
"tools": {
|
|
23
|
+
"browser": {
|
|
24
|
+
"name": "playwright",
|
|
25
|
+
"via": "cli",
|
|
26
|
+
"invoke": "npx playwright",
|
|
27
|
+
"notes": "In the project's devDependencies; `npx playwright screenshot <url> out.png`."
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
- `platform` is always `"web"`.
|
|
34
|
+
- `stack` is a list of lower-case keywords, the names the ecosystem itself uses: `react`, `nextjs`,
|
|
35
|
+
`vite`, `astro`, `sveltekit`, `vue`, `typescript`, `tailwind`, `css-modules`, `static-html`.
|
|
36
|
+
Framework, language and styling approach at least.
|
|
37
|
+
- `design_system` is where the project's tokens live: `kind` is the idiom (`tailwind`,
|
|
38
|
+
`css-variables`, `css-in-js`, or a component library's own theme such as `mui`) and `path` is the
|
|
39
|
+
file an agent reads. `null` when the project has no tokens anywhere, only literal values.
|
|
40
|
+
- `components` is how the project writes components. `library` is the component library's name as
|
|
41
|
+
its ecosystem spells it (`shadcn`, `mui`, `chakra`, `mantine`, `antd`, `headlessui`, `vuetify`),
|
|
42
|
+
`custom` for the project's own components built on no library, or `none` when there is no
|
|
43
|
+
component layer yet. `path` is where the components live, or will. `notes` is optional: a second
|
|
44
|
+
library in use, a wrapper convention, anything the next step would otherwise trip over. The
|
|
45
|
+
components step adds `primitives` here once it has built the set.
|
|
46
|
+
- `tools` is keyed by role. `browser` is the one this step fills; later steps add their own.
|
|
47
|
+
Each tool says what it is (`name`), what kind of thing it is (`via`: `"skill"`, `"mcp"` or
|
|
48
|
+
`"cli"`), and what an agent does to use it (`invoke`: the skill to load, the MCP server whose
|
|
49
|
+
tools to call, or the command to run). `notes` is optional and holds what an agent would
|
|
50
|
+
otherwise have to find out again: a flag, a prerequisite, a limit.
|
|
51
|
+
- A tool that was looked for and not found is recorded as `null`, so later steps know not to look
|
|
52
|
+
again and can tell the user what to install.
|
|
53
|
+
|
|
54
|
+
## Step 1: Load current state
|
|
55
|
+
|
|
56
|
+
If there is no `PRODUCT.md`, run [product.md](product.md) first and come back: the stack of a
|
|
57
|
+
new project is inferred from what the product is.
|
|
58
|
+
|
|
59
|
+
Read `.ezdesign/config.json` if it exists. Take its values as the record of an earlier pass, to be
|
|
60
|
+
checked against the project now, not as truth.
|
|
61
|
+
|
|
62
|
+
## Step 2: Define the architecture
|
|
63
|
+
|
|
64
|
+
### The project already has code
|
|
65
|
+
|
|
66
|
+
Read what it is built on: `package.json` dependencies and scripts, the framework's config file,
|
|
67
|
+
the lockfile, how styles are written, the language. Record the keywords.
|
|
68
|
+
|
|
69
|
+
Then read what it already designs with, because every component added later has to be written the
|
|
70
|
+
way these ones are:
|
|
71
|
+
|
|
72
|
+
- **The design system.** A Tailwind config or a CSS `@theme` block; `:root` custom properties; a
|
|
73
|
+
CSS-in-JS theme; a component library's theme object. Record the one the project treats as the
|
|
74
|
+
source, and `null` when the values are only literal.
|
|
75
|
+
- **The component library.** `components.json` at the root, or `@radix-ui/*` with
|
|
76
|
+
`class-variance-authority` and a `components/ui` directory, is `shadcn`; a library in
|
|
77
|
+
`package.json` (`@mui/material`, `@chakra-ui/react`, `@mantine/core`, `antd`,
|
|
78
|
+
`@headlessui/react`, `vuetify`) is that library; a components directory built on none of them is
|
|
79
|
+
`custom`. When two are in use, record the one most of the components are built on and name the
|
|
80
|
+
other in `notes`. Never assume one from the framework: a React project is not a shadcn project
|
|
81
|
+
until the files say so.
|
|
82
|
+
|
|
83
|
+
If the config already holds a stack, compare it with what you found. A keyword the project no
|
|
84
|
+
longer uses, or a framework it has moved off, makes the record legacy: replace it with the current
|
|
85
|
+
stack and say in one line what changed.
|
|
86
|
+
|
|
87
|
+
### The project is new
|
|
88
|
+
|
|
89
|
+
Recommend a stack from `PRODUCT.md`. A simple site with no data, accounts or growth to plan for is
|
|
90
|
+
served by the simplest architecture that renders it; a product with users, state and screens that
|
|
91
|
+
will multiply is served by a component framework. Match the weight of the stack to the product,
|
|
92
|
+
not to fashion.
|
|
93
|
+
|
|
94
|
+
When more than one stack fits, or `PRODUCT.md` cannot settle it, ask the user. They may not be
|
|
95
|
+
technical, so do the narrowing for them: at most three candidates, each in one line that says what
|
|
96
|
+
choosing it means for *their product* - how fast it ships, what it costs to host, what it can grow
|
|
97
|
+
into, what it locks them out of - in the user's own language, no framework jargon without a
|
|
98
|
+
plain-words gloss. Lead with the one you recommend and mark it as recommended. Use the structured
|
|
99
|
+
question tool when available; otherwise ask in your reply and wait.
|
|
100
|
+
|
|
101
|
+
The design system and the component library follow from the stack. A new React project uses
|
|
102
|
+
`shadcn` with Tailwind as its design system. Any other new project records `custom`, with its
|
|
103
|
+
tokens in the stack's own idiom, unless the user asks for a library by name.
|
|
104
|
+
|
|
105
|
+
Record the decision. Creating the project itself is the first making request, not this step.
|
|
106
|
+
|
|
107
|
+
## Step 3: Define the tool sources
|
|
108
|
+
|
|
109
|
+
**Browser tool.** Every later step looks at the rendered page, so find the tool this agent can
|
|
110
|
+
actually use to open a URL, take a screenshot and read computed styles. Look, in order, at: a skill
|
|
111
|
+
the agent has that drives a browser; an MCP server that does; a CLI the project or the machine has
|
|
112
|
+
installed (Playwright, Puppeteer, a headless Chrome). Prefer the one that needs no install. Record
|
|
113
|
+
it under `tools.browser`, with `invoke` precise enough that an agent that has never seen this
|
|
114
|
+
project can use it without a search, and put anything it would trip over in `notes`.
|
|
115
|
+
|
|
116
|
+
When nothing is found, record `"browser": null` and tell the user which tool to install; the
|
|
117
|
+
making and reviewing steps then work from source alone and say so.
|
|
118
|
+
|
|
119
|
+
## Step 4: Wrap up or resume
|
|
120
|
+
|
|
121
|
+
Write `.ezdesign/config.json`, creating the directory if needed. Say something to the user only
|
|
122
|
+
when they have to act on it - no browser tool was found, and here is how to install one. The rest
|
|
123
|
+
of this step is the harness's own business. Then end, or resume the request that invoked this.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Open a review
|
|
2
|
+
|
|
3
|
+
ezdesign turns a locally running dev server into an annotatable review surface. Start the session
|
|
4
|
+
with an ACP agent, then let that agent own the feedback loop. It receives each annotation batch,
|
|
5
|
+
edits the source, and streams its work, questions, permission prompts, and replies into the review
|
|
6
|
+
shell while the app's HMR updates the page in place.
|
|
7
|
+
|
|
8
|
+
You do not need ezdesign installed globally. Invoke it as `npx -y @cychien/ezdesign@latest ...`.
|
|
9
|
+
|
|
10
|
+
## When to use
|
|
11
|
+
|
|
12
|
+
- You just generated or significantly changed a page or component and the user should review it visually
|
|
13
|
+
- The user wants to give feedback by pointing at things instead of describing them in chat
|
|
14
|
+
- You are iterating on visual, copy, or layout details where prose descriptions are lossy
|
|
15
|
+
|
|
16
|
+
## Agent profile
|
|
17
|
+
|
|
18
|
+
Use the ACP profile matching the current coding-agent environment unless the user explicitly names
|
|
19
|
+
another ACP agent or command:
|
|
20
|
+
|
|
21
|
+
- Claude Code: `claude`
|
|
22
|
+
- Codex: `codex`
|
|
23
|
+
- Gemini CLI: `gemini`
|
|
24
|
+
|
|
25
|
+
Every skill invocation uses ACP mode. Never omit `--agent`, never run `ezdesign poll`, and never fall
|
|
26
|
+
back to Poll mode after an ACP failure. If the environment does not identify a supported profile and
|
|
27
|
+
the user did not provide an ACP command, ask them which ACP agent to use.
|
|
28
|
+
|
|
29
|
+
## Workflow
|
|
30
|
+
|
|
31
|
+
1. Make sure the dev server is running. Never start a second instance when one is already up. If
|
|
32
|
+
the user only wants to explore designs on a canvas, there may be no dev server: run the command
|
|
33
|
+
below without the url and the review opens on the project's canvases.
|
|
34
|
+
2. From the project root, run:
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
npx -y @cychien/ezdesign@latest <url> --agent <profile>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Use the full URL of the page to review, for example:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
npx -y @cychien/ezdesign@latest http://localhost:5173/pricing --agent codex
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The working directory matters because the session and spawned agent are scoped to that project.
|
|
47
|
+
If the CLI says the user previously ended the session, do not pass `--reopen` unless the user
|
|
48
|
+
explicitly asked to review again.
|
|
49
|
+
3. Once the CLI reports the session and agent, tell the user the review shell is ready. Do not start
|
|
50
|
+
a parallel feedback loop or shadow the ACP agent's edits. The daemon delivers queued and future
|
|
51
|
+
batches automatically, one turn at a time. The shell owns the turn: the user can stop one that is
|
|
52
|
+
heading the wrong way (the Stop button, or Cmd/Ctrl+.), `/new` in the note box clears the agent's
|
|
53
|
+
context and carries the review on in a fresh session, and a batch sent mid-turn queues rather
|
|
54
|
+
than interrupting - so do not tell the user to wait for a turn to finish before annotating.
|
|
55
|
+
4. The user ends the review from the shell. Do not end or stop a live session unless they ask.
|
|
56
|
+
|
|
57
|
+
## Failures and switching agents
|
|
58
|
+
|
|
59
|
+
- If the ACP command is missing, unauthenticated, or exits, report that error and help fix that ACP
|
|
60
|
+
setup. Do not substitute Poll mode.
|
|
61
|
+
- A live session can own only one ACP agent. If the CLI refuses a different agent, do not stop the
|
|
62
|
+
daemon automatically because it may hold other sessions. Ask the user whether to end the current
|
|
63
|
+
session or stop the daemon before switching.
|
|
64
|
+
- Do not retry a failing launch indefinitely. After one retry for a transient failure, report the
|
|
65
|
+
blocker and preserve the session state.
|
|
66
|
+
|
|
67
|
+
## Source anchors
|
|
68
|
+
|
|
69
|
+
Projects using Vite get exact `file:line` anchors by adding the bundled plugin:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
// vite.config.ts
|
|
73
|
+
import { ezdesignSource } from '@cychien/ezdesign/vite'
|
|
74
|
+
// plugins: [ezdesignSource(), ...]
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Suggest this dev-only plugin when feedback repeatedly arrives without exact source locations. It has
|
|
78
|
+
no production-build impact.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export interface Png {
|
|
2
|
+
width: number
|
|
3
|
+
height: number
|
|
4
|
+
channels: 3 | 4
|
|
5
|
+
pixels: Buffer
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export type Diff =
|
|
9
|
+
| { same: true }
|
|
10
|
+
| {
|
|
11
|
+
same: false
|
|
12
|
+
changed: number
|
|
13
|
+
box: { left: number; top: number; right: number; bottom: number }
|
|
14
|
+
}
|
|
15
|
+
| { same: false; size: { before: string; after: string } }
|
|
16
|
+
|
|
17
|
+
export type Comparison = { name: string } & (Diff | { same: false; missing: 'before' | 'after' })
|
|
18
|
+
|
|
19
|
+
export const DESKTOP: { width: number; height: number }
|
|
20
|
+
export const MOBILE: { width: number; height: number }
|
|
21
|
+
export function shotName(path: string): string
|
|
22
|
+
export function decodePng(buffer: Buffer): Png
|
|
23
|
+
export function diffPngs(before: Buffer, after: Buffer): Diff
|
|
24
|
+
export function compare(beforeDir: string, afterDir: string): Comparison[]
|
|
25
|
+
export function describe(result: Comparison): string
|
|
26
|
+
export function findChrome(env?: Record<string, string | undefined>, platform?: string): string
|
|
27
|
+
export type ShotState = 'hover' | 'focus' | 'active'
|
|
28
|
+
export const STATES: ShotState[]
|
|
29
|
+
export function shotFiles(
|
|
30
|
+
file: string,
|
|
31
|
+
sizes: { width: number; height: number }[],
|
|
32
|
+
page: boolean,
|
|
33
|
+
states?: { state: ShotState; target: string }[],
|
|
34
|
+
): {
|
|
35
|
+
size: { width: number; height: number }
|
|
36
|
+
file: string
|
|
37
|
+
kind: 'piece' | 'page' | ShotState
|
|
38
|
+
target?: string
|
|
39
|
+
}[]
|
|
40
|
+
export function statesFrom(
|
|
41
|
+
named: Record<string, string | true>,
|
|
42
|
+
around: string | null,
|
|
43
|
+
): { state: ShotState; target: string }[]
|
|
44
|
+
export function parseViewport(raw: string | undefined): { width: number; height: number }
|
|
45
|
+
export function flags(args: string[]): { named: Record<string, string | true>; rest: string[] }
|
|
46
|
+
|
|
47
|
+
export interface StorageEntry {
|
|
48
|
+
name: string
|
|
49
|
+
value: string
|
|
50
|
+
}
|
|
51
|
+
export interface SignIn {
|
|
52
|
+
cookies: { name: string; value: string; domain: string; path: string }[]
|
|
53
|
+
storage: {
|
|
54
|
+
localStorage: StorageEntry[]
|
|
55
|
+
sessionStorage: StorageEntry[]
|
|
56
|
+
indexedDB: unknown[]
|
|
57
|
+
} | null
|
|
58
|
+
}
|
|
59
|
+
export function sameOrigin(a: string, b: string): boolean
|
|
60
|
+
export function signInFor(origin: string, state: unknown): SignIn | null
|
|
61
|
+
export function landedElsewhere(requested: string, landed: string): boolean
|