transitions-refine 0.1.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/refine-live/SKILL.md +96 -12
- package/README.md +12 -2
- package/demo.html +515 -264
- package/package.json +1 -1
- package/server/inject.mjs +3 -5
- package/server/relay.mjs +69 -7
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: refine-live
|
|
3
|
-
description: Become the live "Refine" agent for the Timeline Inspector. Use when the user runs `/refine live`, asks to "refine live", "go live", "answer refine jobs", or wants the timeline panel's Refine button (LLM mode)
|
|
3
|
+
description: Become the live "Refine" agent for the Timeline Inspector. Use when the user runs `/refine live`, asks to "refine live", "go live", "answer refine jobs", or wants the timeline panel's Refine button (LLM mode), Accept button, or grouped scan to be backed by a real agent. Long-polls the local refine relay, reasons about each CSS transition with the transitions-dev skill, posts suggestions back to the browser panel, for "scan" jobs groups the page's transitions into components with open/close phases by reading the source, and for "apply" jobs writes the accepted timing changes into the user's source code.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Refine Live
|
|
@@ -54,6 +54,10 @@ never has to re-run `/refine live`.
|
|
|
54
54
|
}
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
+
- **If `request.kind === "scan"`** this is not a suggestion job — the panel is
|
|
58
|
+
asking you to group the page's transitions by reading the source. Jump to
|
|
59
|
+
[`## Scan jobs`](#scan-jobs-group-from-source) and return `groups` instead of
|
|
60
|
+
suggestions.
|
|
57
61
|
- **If `request.kind === "apply"`** this is not a suggestion job — the user
|
|
58
62
|
pressed **Accept** to write changes to their code. Jump to
|
|
59
63
|
[`## Apply jobs`](#apply-jobs-write-to-source) and edit the source instead of
|
|
@@ -176,6 +180,78 @@ never has to re-run `/refine live`.
|
|
|
176
180
|
stop, tell them the LLM tab will go unavailable and how to restart
|
|
177
181
|
(`/refine live`).
|
|
178
182
|
|
|
183
|
+
## Scan jobs (group from source)
|
|
184
|
+
|
|
185
|
+
When a claimed job has `request.kind === "scan"`, the panel wants you to turn a
|
|
186
|
+
flat list of DOM-detected transitions into **components with phases**. A naive
|
|
187
|
+
DOM scan only sees each element's *current* computed transition — it can't tell
|
|
188
|
+
open from close, and lists related elements (panel, backdrop, staggered items)
|
|
189
|
+
separately. You fix that by reading the source. The request looks like:
|
|
190
|
+
|
|
191
|
+
```json
|
|
192
|
+
{
|
|
193
|
+
"id": "uuid",
|
|
194
|
+
"request": {
|
|
195
|
+
"kind": "scan",
|
|
196
|
+
"url": "http://localhost:5173/",
|
|
197
|
+
"raw": [
|
|
198
|
+
{ "label": "div.dropdown-panel", "selector": ".dropdown-panel",
|
|
199
|
+
"properties": ["opacity","transform"],
|
|
200
|
+
"timings": [{ "property": "opacity", "durationMs": 200, "delayMs": 0, "easing": "ease-out" }] }
|
|
201
|
+
]
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Do this:
|
|
207
|
+
|
|
208
|
+
1. **Identify each animated component** the raw entries belong to (dropdown,
|
|
209
|
+
modal, tooltip, accordion, drawer, toast…). Use the selectors/labels as hints,
|
|
210
|
+
then read the source — plain CSS / CSS Modules, styled-components/emotion,
|
|
211
|
+
Tailwind, inline styles, or Motion/Framer variants.
|
|
212
|
+
2. **Split each component into phases** — usually `open` and `close` (a hover-only
|
|
213
|
+
component can be a single phase). Open and close often live on different
|
|
214
|
+
selectors (`.is-open` vs `.is-closing`) with different timings; report **both**
|
|
215
|
+
even though only one is in the DOM right now.
|
|
216
|
+
3. **List each phase's members** — the elements that animate in that phase. Give
|
|
217
|
+
each a stable `id`, a human `label`, a live-resolvable CSS `selector`, an
|
|
218
|
+
optional `toState` hint (the class/attribute that drives the phase, e.g.
|
|
219
|
+
`.is-open`), and its real `propertyTimings`. **Quote the real timings from the
|
|
220
|
+
source — never invent.**
|
|
221
|
+
4. **Post the groups** (this completes the job):
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
curl -s -X POST http://localhost:7331/jobs/<id>/result \
|
|
225
|
+
-H 'Content-Type: application/json' \
|
|
226
|
+
-d '{
|
|
227
|
+
"summary": "Grouped Dropdown into Open/Close.",
|
|
228
|
+
"groups": [
|
|
229
|
+
{ "id": "dropdown", "label": "Dropdown", "component": "src/Dropdown.tsx",
|
|
230
|
+
"phases": [
|
|
231
|
+
{ "id": "dropdown:open", "phase": "open", "label": "Open", "members": [
|
|
232
|
+
{ "id": "panel", "label": "Panel", "selector": ".dropdown-panel", "toState": ".is-open",
|
|
233
|
+
"propertyTimings": [
|
|
234
|
+
{ "property": "opacity", "durationMs": 200, "delayMs": 0, "easing": "ease-out" },
|
|
235
|
+
{ "property": "transform", "durationMs": 200, "delayMs": 0, "easing": "cubic-bezier(0.22, 1, 0.36, 1)" }
|
|
236
|
+
] }
|
|
237
|
+
] },
|
|
238
|
+
{ "id": "dropdown:close", "phase": "close", "label": "Close", "members": [
|
|
239
|
+
{ "id": "panel", "label": "Panel", "selector": ".dropdown-panel", "toState": ".is-closing",
|
|
240
|
+
"propertyTimings": [
|
|
241
|
+
{ "property": "opacity", "durationMs": 150, "delayMs": 0, "easing": "ease-in" }
|
|
242
|
+
] }
|
|
243
|
+
] }
|
|
244
|
+
] }
|
|
245
|
+
]
|
|
246
|
+
}'
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
If you can't confidently group anything, post `{"groups":[],"summary":"…"}` —
|
|
250
|
+
the panel keeps its flat DOM scan. Reserve `/jobs/<id>/error` for unexpected
|
|
251
|
+
failures.
|
|
252
|
+
|
|
253
|
+
Then go back to step 1 of the loop.
|
|
254
|
+
|
|
179
255
|
## Apply jobs (write to source)
|
|
180
256
|
|
|
181
257
|
When a claimed job has `request.kind === "apply"`, the user accepted their current
|
|
@@ -186,10 +262,14 @@ timeline values and wants them written to the codebase. The request looks like:
|
|
|
186
262
|
"id": "uuid",
|
|
187
263
|
"request": {
|
|
188
264
|
"kind": "apply",
|
|
189
|
-
"label": "
|
|
190
|
-
"selector": "
|
|
265
|
+
"label": "Dropdown · Close",
|
|
266
|
+
"selector": ".dropdown-panel",
|
|
267
|
+
"component": "src/Dropdown.tsx",
|
|
268
|
+
"group": "Dropdown",
|
|
269
|
+
"phase": "close",
|
|
191
270
|
"changes": [
|
|
192
|
-
{ "property": "opacity", "
|
|
271
|
+
{ "property": "opacity", "member": "Panel", "selector": ".dropdown-panel",
|
|
272
|
+
"from": { "durationMs": 300, "delayMs": 0, "easing": "ease" },
|
|
193
273
|
"to": { "durationMs": 150, "delayMs": 0, "easing": "cubic-bezier(0.4, 0, 1, 1)" } }
|
|
194
274
|
]
|
|
195
275
|
}
|
|
@@ -199,15 +279,19 @@ timeline values and wants them written to the codebase. The request looks like:
|
|
|
199
279
|
Do this:
|
|
200
280
|
|
|
201
281
|
1. **Locate the real declaration in the source.** The `selector` is a DOM-path
|
|
202
|
-
*hint*, not necessarily the source selector.
|
|
203
|
-
handle whatever the project uses: plain CSS / CSS
|
|
204
|
-
emotion template literals, Tailwind utilities
|
|
205
|
-
`[transition-duration:300ms]`, or the
|
|
206
|
-
`style={{ transition: … }}` objects
|
|
282
|
+
*hint*, not necessarily the source selector. Use the `component` hint and search
|
|
283
|
+
by the label/class names; handle whatever the project uses: plain CSS / CSS
|
|
284
|
+
Modules, styled-components or emotion template literals, Tailwind utilities
|
|
285
|
+
(`duration-300`, arbitrary `[transition-duration:300ms]`, or the
|
|
286
|
+
`tailwind.config` theme), inline `style={{ transition: … }}` objects, and
|
|
287
|
+
Motion/Framer variants. Match by the `from` values to disambiguate.
|
|
288
|
+
- **If `phase` is set** (e.g. `"open"`/`"close"`), edit only that state's rule
|
|
289
|
+
(the `.is-open` rule for open, the `.is-closing`/base rule for close) — not
|
|
290
|
+
the other phase. Each change's `member` + `selector` says which element.
|
|
207
291
|
2. **Edit each change's property** to its `to` values (`durationMs` ms, `easing`,
|
|
208
|
-
`delayMs` ms). Keep the file's existing unit/format
|
|
209
|
-
touch only that property's timing. If a CSS variable /
|
|
210
|
-
value, update it at the single most sensible place.
|
|
292
|
+
`delayMs` ms) on the right member + phase. Keep the file's existing unit/format
|
|
293
|
+
(`0.25s` vs `250ms`) and touch only that property's timing. If a CSS variable /
|
|
294
|
+
design token backs the value, update it at the single most sensible place.
|
|
211
295
|
3. **Minimal edit** — no reformatting or unrelated changes.
|
|
212
296
|
4. **Post the outcome** (this completes the job):
|
|
213
297
|
|
package/README.md
CHANGED
|
@@ -4,6 +4,10 @@ A live, agent-driven **Refine** panel for CSS and [Motion](https://motion.dev) t
|
|
|
4
4
|
|
|
5
5
|
The feedback shows up **in a panel that slides in from the right** — not in your chat — and you pick which suggestions to apply. Applied suggestions are **live overrides** (instant preview, reversible) — the same path as dragging the timeline bars. When you're happy, **Accept** writes those values back into your source via the agent.
|
|
6
6
|
|
|
7
|
+
There's **no play button or scrubber** — the running component *is* the preview. Any edit (a dragged bar, an inspector tweak, or an applied suggestion) is written straight onto the live element as an inline `transition`, so you see it the next time you trigger the transition (open the dropdown, hover the card, …). Reset reverts the element to its source.
|
|
8
|
+
|
|
9
|
+
Real components rarely live in one CSS rule. A dropdown has an **Open** and a **Close** phase, each animating several elements (panel, backdrop, staggered items) with different timings — and the close phase usually isn't even in the DOM while the panel is open. So when the panel opens it also asks the agent to **read your source and group** the page's transitions into components → phases → member elements. You then pick a whole phase (e.g. *Dropdown · Open*) and see every sub-transition as a labeled lane on one shared timeline. If no agent is live, the panel falls back to the flat DOM scan with no regression.
|
|
10
|
+
|
|
7
11
|
Inspired by the [impeccable.style](https://impeccable.style/live-mode/) "live" pattern: the browser drops a job in a tiny local relay, and the relay answers it with **one agent run per click**. No standing loop, nothing to start per click — you just keep the relay running.
|
|
8
12
|
|
|
9
13
|
```
|
|
@@ -54,6 +58,12 @@ REFINE_AGENT_CMD='cursor-agent -p' npm run relay # or: codex exec - | claude
|
|
|
54
58
|
|
|
55
59
|
The CLI must have the `transitions-dev` skill available (the prompt tells it to read the skill).
|
|
56
60
|
|
|
61
|
+
## Grouped scan — Open / Close phases
|
|
62
|
+
|
|
63
|
+
When the panel opens it posts a **scan job** to the relay; the agent reads your source and returns the page's animated components, each split into phases (`open`, `close`, …) with their **member elements** and the *real* per-state timings — including the close transition the DOM can't show you. The picker then groups by component, you select a phase, and the timeline renders one lane per member-property (each lane labeled with its member) on a single time axis so stagger and delays line up. Editing any lane applies **live** as an inline `transition` on that member element, so triggering the component yourself (open/close it) previews the whole phase with your values; **Accept** writes back to the correct state rule (`.is-open` vs `.is-closing`) per member.
|
|
64
|
+
|
|
65
|
+
Grouping needs the agent (`/refine live`, `--llm`, or `REFINE_AGENT_CMD`); with no agent the panel just shows the flat DOM scan as before.
|
|
66
|
+
|
|
57
67
|
## Refine modes
|
|
58
68
|
|
|
59
69
|
- **Small refinements** — keeps the transition, suggests motion-token tweaks (duration/easing), and may add a whole-transition replacement when one clearly fits better.
|
|
@@ -63,7 +73,7 @@ The CLI must have the `transitions-dev` skill available (the prompt tells it to
|
|
|
63
73
|
|
|
64
74
|
The **Accept** button (next to Refine) is enabled whenever the selected transition has unsaved changes — whether you edited the bars/easing by hand or applied a Refine suggestion. Pressing it sends an **apply job** to the relay: the agent finds where that transition is declared in your source (plain CSS, CSS Modules, styled-components/emotion, Tailwind, or inline styles), edits only the changed timings, and reports back. The button shows a spinner while saving and flips to **Done** on success.
|
|
65
75
|
|
|
66
|
-
Like Replace, Accept needs the agent — run `/refine live` (or `--llm` / `REFINE_AGENT_CMD`). The deterministic answerer can't edit files.
|
|
76
|
+
Like Replace, Accept needs the agent — run `/refine live` (or `--llm` / `REFINE_AGENT_CMD`). The deterministic answerer can't edit files.
|
|
67
77
|
|
|
68
78
|
## Pieces
|
|
69
79
|
|
|
@@ -86,7 +96,7 @@ Like Replace, Accept needs the agent — run `/refine live` (or `--llm` / `REFIN
|
|
|
86
96
|
| `REFINE_AUTO=0` | — | disable auto-answer and wait for an external poller |
|
|
87
97
|
| `window.REFINE_RELAY_URL` | injected origin | browser override for the relay URL |
|
|
88
98
|
|
|
89
|
-
Endpoints: `POST /jobs` (refine or `kind: "
|
|
99
|
+
Endpoints: `POST /jobs` (refine, `kind: "apply"`, or `kind: "scan"`), `GET /jobs/:id` (browser). In `REFINE_AUTO=0` mode an external poller also uses `GET /jobs/next` and `POST /jobs/:id/{status,result,error}` (the result body accepts `suggestions`, `groups`, or `applied`).
|
|
90
100
|
|
|
91
101
|
Refine suggestions stay as live overrides until you press **Accept**, which is the explicit step that writes them into your source.
|
|
92
102
|
|