transitions-refine 0.3.8 → 0.3.10
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 +72 -49
- package/demo.html +834 -191
- package/package.json +2 -1
- package/server/relay.mjs +70 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "transitions-refine",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.10",
|
|
4
4
|
"description": "Live, agent-driven Refine panel for CSS/Motion transitions — injects a timeline + Refine UI and runs transitions.dev suggestions via your coding agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
],
|
|
15
15
|
"scripts": {
|
|
16
16
|
"prepack": "node scripts/sync-skill.mjs",
|
|
17
|
+
"build:website-demo": "node scripts/build-website-demo.mjs",
|
|
17
18
|
"live": "node bin/cli.mjs live",
|
|
18
19
|
"relay": "node server/relay.mjs"
|
|
19
20
|
},
|
package/server/relay.mjs
CHANGED
|
@@ -28,7 +28,7 @@ import { spawn } from "node:child_process";
|
|
|
28
28
|
import { existsSync } from "node:fs";
|
|
29
29
|
import { homedir } from "node:os";
|
|
30
30
|
import { delimiter, join } from "node:path";
|
|
31
|
-
import { refineTimings } from "./motion-tokens.mjs";
|
|
31
|
+
import { refineTimings, DURATION_TOKENS, SMOOTH_OUT } from "./motion-tokens.mjs";
|
|
32
32
|
import { buildInjectModule } from "./inject.mjs";
|
|
33
33
|
|
|
34
34
|
const PORT = Number(process.env.REFINE_RELAY_PORT) || 7331;
|
|
@@ -55,6 +55,13 @@ const AGENT_CMD = augmentAgentCmd(process.env.REFINE_AGENT_CMD || null);
|
|
|
55
55
|
// (Opus / GPT-5.5) — forcing a fast model here keeps the initial scan snappy.
|
|
56
56
|
// Override with REFINE_SCAN_MODEL="" to fall back to the agent's default.
|
|
57
57
|
const SCAN_MODEL = process.env.REFINE_SCAN_MODEL ?? "composer-2.5-fast";
|
|
58
|
+
// Pin a fast model for refine (suggestion) jobs too. The motion-token vocabulary
|
|
59
|
+
// is inlined into the prompt (see MOTION_TOKENS_BLOCK) so a fast model has every
|
|
60
|
+
// fact it needs for the common token-tweak path — keeping suggestions snappy
|
|
61
|
+
// without falling back to the user's (possibly heavy/slow) default model.
|
|
62
|
+
// Override with REFINE_MODEL="" to use the agent's default model if you ever find
|
|
63
|
+
// the fast model regresses a tricky `replace`/recipe judgement. cursor-agent only.
|
|
64
|
+
const REFINE_MODEL = process.env.REFINE_MODEL ?? "composer-2.5-fast";
|
|
58
65
|
const AGENT_TIMEOUT_MS = Number(process.env.REFINE_AGENT_TIMEOUT_MS) || 120000;
|
|
59
66
|
|
|
60
67
|
// Inject `--model <m>` into a `cursor-agent …` command (after the binary).
|
|
@@ -144,13 +151,57 @@ function nextPendingLlm() {
|
|
|
144
151
|
|
|
145
152
|
// ── answering a job (one run per job) ────────────────────────────────────────
|
|
146
153
|
|
|
154
|
+
// Inlined transitions.dev motion-token vocabulary (mirrors motion-tokens.mjs and
|
|
155
|
+
// the transitions-dev skill's "## Motion tokens"). Embedding it in the refine
|
|
156
|
+
// prompt means the common token-tweak path needs ZERO file reads — only recipe
|
|
157
|
+
// selection (replace/both) still opens the skill + a reference file.
|
|
158
|
+
const MOTION_TOKENS_BLOCK = [
|
|
159
|
+
"Motion tokens (transitions.dev) — match on USAGE intent, not the nearest number:",
|
|
160
|
+
"Durations:",
|
|
161
|
+
...DURATION_TOKENS.map((t) => ` - ${t.ms}ms ${t.name}: ${t.usage}`),
|
|
162
|
+
`Default easing "Smooth ease out": ${SMOOTH_OUT}.`,
|
|
163
|
+
"Token easings (already on-grid — leave unchanged): ease-out, ease-in-out, linear, cubic-bezier(0.34, 1.36, 0.64, 1) (bouncy overshoot, badge pop), cubic-bezier(0.34, 3.85, 0.64, 1) (strong overshoot, avatar return).",
|
|
164
|
+
'Generic curves to nudge toward "Smooth ease out": "ease", "ease-in", or any hand-rolled cubic-bezier()/linear() that is not a token above.',
|
|
165
|
+
].join("\n");
|
|
166
|
+
|
|
167
|
+
// Inlined recipe catalog + decision hints (mirrors the transitions-dev skill's
|
|
168
|
+
// "## Quick reference" + "## Decision rules"). Embedding it lets the `replace`
|
|
169
|
+
// path pick a recipe WITHOUT reading SKILL.md — at most it opens the ONE chosen
|
|
170
|
+
// reference file for exact structure/timings.
|
|
171
|
+
const RECIPES_BLOCK = [
|
|
172
|
+
"transitions.dev recipes — match the inferred USAGE to ONE recipe (reference file in parens):",
|
|
173
|
+
"- Card resize: a container changes width/height on a layout change (01-card-resize.md)",
|
|
174
|
+
"- Number pop-in: a number/digit updates (02-number-pop-in.md)",
|
|
175
|
+
"- Notification badge: a small dot/badge appears on a trigger (03-notification-badge.md)",
|
|
176
|
+
"- Text states swap: text content changes in place (04-text-states-swap.md)",
|
|
177
|
+
"- Menu dropdown: an anchored surface grows from its trigger (05-menu-dropdown.md)",
|
|
178
|
+
"- Modal open/close: a centered dialog scales up, softer scale-down on close (06-modal.md)",
|
|
179
|
+
"- Panel reveal: a surface slides into a region with a cross-blur (07-panel-reveal.md)",
|
|
180
|
+
"- Page side-by-side: slide between list<->detail or step1<->step2 (08-page-side-by-side.md)",
|
|
181
|
+
"- Icon swap: two icons cross-fade in the same slot (09-icon-swap.md)",
|
|
182
|
+
"- Success check: a checkmark celebration, fade+rotate+bob+stroke-draw (10-success-check.md)",
|
|
183
|
+
"- Avatar group hover: hover lifts an item in a horizontal stack (11-avatar-group-hover.md)",
|
|
184
|
+
"- Error state shake: invalid-input shake (12-error-state-shake.md)",
|
|
185
|
+
"- Input clear with dissolve: clearing a text field (13-input-clear-dissolve.md)",
|
|
186
|
+
"- Skeleton loader and reveal: placeholder pulses then swaps to real content (14-skeleton-reveal.md)",
|
|
187
|
+
"- Shimmer text: in-progress/'thinking' text shimmer (15-shimmer-text.md)",
|
|
188
|
+
"- Tabs sliding: a moving highlight across segmented options (16-tabs-sliding.md)",
|
|
189
|
+
"- Tooltip open/close: delayed fade+scale in, instant out (17-tooltip.md)",
|
|
190
|
+
"- Texts reveal: staggered blurred rise of stacked text lines (18-texts-reveal.md)",
|
|
191
|
+
"- Card hover tilt: 3D tilt toward the pointer (19-card-tilt.md)",
|
|
192
|
+
"- Plus to menu morph: a circular trigger becomes the surface it opens (20-plus-menu-morph.md)",
|
|
193
|
+
"- Accordion expand: a collapsible body grows/shrinks in height (21-accordion.md)",
|
|
194
|
+
"Tie-break: prefer the lower-overhead recipe (card resize over panel reveal, dropdown over modal). If no recipe genuinely fits, return an empty suggestions array.",
|
|
195
|
+
].join("\n");
|
|
196
|
+
|
|
147
197
|
function buildPrompt(job) {
|
|
148
198
|
const r = job.request || {};
|
|
149
199
|
const rawType = r.refineType || "small";
|
|
150
200
|
const refineType = rawType === "replace" ? "replace" : rawType === "both" ? "both" : "small";
|
|
201
|
+
const needsRecipe = refineType === "replace" || refineType === "both";
|
|
151
202
|
const lines = [
|
|
152
203
|
"You are refining ONE CSS transition against the transitions.dev library and motion tokens.",
|
|
153
|
-
|
|
204
|
+
MOTION_TOKENS_BLOCK,
|
|
154
205
|
"",
|
|
155
206
|
"Transition context (JSON):",
|
|
156
207
|
JSON.stringify({ label: r.label, selector: r.selector, refineType, timings: r.timings }, null, 2),
|
|
@@ -158,21 +209,31 @@ function buildPrompt(job) {
|
|
|
158
209
|
"Infer each declaration's USAGE (modal close, dropdown open, tooltip, badge, resize, color/theme change…) from the label/selector. Match on usage intent, not the nearest number.",
|
|
159
210
|
"",
|
|
160
211
|
];
|
|
212
|
+
if (needsRecipe) {
|
|
213
|
+
lines.push(
|
|
214
|
+
"To pick a whole-transition replacement, match the inferred USAGE against the recipe list below (the skill's decision rules are summarised here — do NOT read SKILL.md or any reference file). Derive the `patch`'s duration/easing from the MOTION TOKENS above for the recipe's phase (open vs close), and put the recipe's reference filename in `reference` so the user can paste the full recipe themselves. The patch only drives the live preview; exact keyframes/structure come from that pasted file — so you never need to open it.",
|
|
215
|
+
"",
|
|
216
|
+
RECIPES_BLOCK,
|
|
217
|
+
"",
|
|
218
|
+
);
|
|
219
|
+
}
|
|
161
220
|
if (refineType === "replace") {
|
|
162
221
|
lines.push(
|
|
163
222
|
"refineType is \"replace\": suggest a WHOLE-TRANSITION replacement ONLY — do NOT propose motion-token tweaks (no kind \"duration\"/\"delay\"/\"easing\").",
|
|
164
|
-
"
|
|
223
|
+
"Pick the SINGLE best-fit recipe from the list above. Emit ONE suggestion with kind \"replace\": set its `patch` to the motion-token duration/easing for the recipe's phase on the property that already transitions (or \"all\") so Apply works live, add a `reference` field with the reference filename, and name the recipe in `title`/`reason`. If no recipe genuinely fits the usage, return an empty suggestions array.",
|
|
224
|
+
"Answer in ONE response — do NOT read or search files.",
|
|
165
225
|
);
|
|
166
226
|
} else if (refineType === "both") {
|
|
167
227
|
lines.push(
|
|
168
228
|
"refineType is \"both\": produce TWO independent groups in the SAME suggestions array — the UI shows them in separate tabs, so include each group whenever it applies.",
|
|
169
229
|
"(1) Motion-token tweaks (kind \"duration\"/\"delay\"/\"easing\"): for each declaration, propose the token value only where it DIFFERS from the current one.",
|
|
170
|
-
"(2) Whole-transition replacement (kind \"replace\"): ALWAYS evaluate one —
|
|
230
|
+
"(2) Whole-transition replacement (kind \"replace\"): ALWAYS evaluate one — pick the SINGLE best-fit recipe from the list above. Emit at most ONE \"replace\" suggestion: set its `patch` to the motion-token duration/easing for the recipe's phase on the property that already transitions (or \"all\"), add a `reference` field with the reference filename, and name the recipe in `title`/`reason`. If no recipe genuinely fits, omit the replace suggestion.",
|
|
231
|
+
"Answer in ONE response — do NOT read or search files.",
|
|
171
232
|
);
|
|
172
233
|
} else {
|
|
173
234
|
lines.push(
|
|
174
|
-
"refineType is \"small\":
|
|
175
|
-
"
|
|
235
|
+
"refineType is \"small\": suggest motion-token tweaks ONLY — for each declaration, propose the token value only where it DIFFERS from the current one (kind \"duration\"/\"delay\"/\"easing\"). Do NOT propose a whole-transition replacement (no kind \"replace\") — the Replace tab requests that separately.",
|
|
236
|
+
"Answer in ONE response using ONLY the data above. Do NOT read or search files, run tools, spawn subagents, or explore the codebase — the motion tokens contain everything you need. This is a quick judgement, not a coding task.",
|
|
176
237
|
);
|
|
177
238
|
}
|
|
178
239
|
lines.push(
|
|
@@ -372,7 +433,9 @@ async function answerJob(job) {
|
|
|
372
433
|
);
|
|
373
434
|
}
|
|
374
435
|
job.statusLog.push({ message: "Asking your agent…", at: now() });
|
|
375
|
-
|
|
436
|
+
const t0 = now();
|
|
437
|
+
result = await runAgentCmd(withModel(AGENT_CMD, REFINE_MODEL), buildPrompt(job));
|
|
438
|
+
console.log(` ⏱ refine agent ${job.id.slice(0, 8)} (${job.request?.refineType || "small"}) ${now() - t0}ms`);
|
|
376
439
|
} else {
|
|
377
440
|
job.statusLog.push({ message: "Matching values to the motion tokens…", at: now() });
|
|
378
441
|
result = refineDeterministic(job);
|