transitions-refine 0.3.24 → 0.3.26
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/demo.html +57 -11
- package/package.json +1 -1
- package/server/motion-tokens.mjs +34 -0
- package/server/relay.mjs +23 -4
package/demo.html
CHANGED
|
@@ -2891,10 +2891,16 @@
|
|
|
2891
2891
|
// Token-faithful tweaks for one lane list (mirrors refineTimings()).
|
|
2892
2892
|
function _txTokenTweaks(timings, ctx){
|
|
2893
2893
|
const out=[]; const c=ctx||{};
|
|
2894
|
+
const blurByMember=new Map(); // memberKey -> {member,hint,sawBlur,varName}
|
|
2894
2895
|
for(const t of (timings||[])){
|
|
2895
2896
|
const prop=t.property||"all";
|
|
2896
2897
|
const member=t.member||null;
|
|
2897
2898
|
const hint=[c.label,c.selector,c.phase,t.property,t.member].filter(Boolean).join(" ");
|
|
2899
|
+
const memberKey=member||"__all__";
|
|
2900
|
+
if(!blurByMember.has(memberKey))blurByMember.set(memberKey,{member,hint,sawBlur:false,varName:null});
|
|
2901
|
+
const bm=blurByMember.get(memberKey);
|
|
2902
|
+
bm.hint=[bm.hint,hint].filter(Boolean).join(" ");
|
|
2903
|
+
if(t.varName&&(!bm.varName||prop==="filter"))bm.varName=t.varName;
|
|
2898
2904
|
const mp=(rest)=> member ? {member, ...rest} : rest;
|
|
2899
2905
|
if(Number.isFinite(t.durationMs)){const {token,delta}=_txNearest(_TX_DUR_TOKENS,t.durationMs);
|
|
2900
2906
|
if(delta>10)out.push({id:"mock-"+prop+"-duration",kind:"duration",property:prop,member,
|
|
@@ -2911,11 +2917,23 @@
|
|
|
2911
2917
|
patch:mp({property:prop,scale:token.v,...(t.varName?{varName:t.varName}:{})}),
|
|
2912
2918
|
reason:token.name+" ("+token.v+") is the transitions.dev scale token for "+token.usage+". "+t.scale+" is "+(usage?"off the usage token":"off-grid")+"."});}
|
|
2913
2919
|
if(Number.isFinite(t.blur)&&t.blur>1e-4){const usage=_txBlurUsage(hint);const {token:near,delta}=_txNearest(_TX_BLUR_TOKENS,t.blur);const token=usage||near;
|
|
2920
|
+
bm.sawBlur=true;
|
|
2914
2921
|
if(token&&(usage||delta>0.5)&&Math.abs(token.v-t.blur)>1e-4)out.push({id:"mock-"+prop+"-blur",kind:"blur",property:prop,member,
|
|
2915
2922
|
title:"Blur → "+token.name,from:t.blur+"px",to:token.v+"px",
|
|
2916
2923
|
patch:mp({property:prop,blur:token.v,...(t.varName?{varName:t.varName}:{})}),
|
|
2917
2924
|
reason:token.name+" ("+token.v+"px) is the transitions.dev blur token for "+token.usage+". "+t.blur+"px is "+(usage?"off the usage token":"off-grid")+"."});}
|
|
2918
2925
|
}
|
|
2926
|
+
// Missing-blur detection (demo parity with server token engine).
|
|
2927
|
+
for(const [memberKey,info] of blurByMember){
|
|
2928
|
+
if(info.sawBlur)continue;
|
|
2929
|
+
const token=_txBlurUsage(info.hint);
|
|
2930
|
+
if(!token)continue;
|
|
2931
|
+
const member=info.member||null;
|
|
2932
|
+
out.push({id:"mock-"+memberKey+"-blur-add",kind:"blur",property:"filter",member,
|
|
2933
|
+
title:"Blur → "+token.name,from:"0px",to:token.v+"px",
|
|
2934
|
+
patch:{property:"filter",blur:token.v,...(member?{member}:{}),...(info.varName?{varName:info.varName}:{})},
|
|
2935
|
+
reason:token.name+" ("+token.v+"px) is the transitions.dev blur token for "+token.usage+". This transition currently has no blur."});
|
|
2936
|
+
}
|
|
2919
2937
|
return out;
|
|
2920
2938
|
}
|
|
2921
2939
|
// refine → token-faithful suggestions (same verdict the shipped agent reaches)
|
|
@@ -2938,15 +2956,20 @@
|
|
|
2938
2956
|
// property and needs the var name, which only the scanned timings know.
|
|
2939
2957
|
const reqPhases = (request && Array.isArray(request.phases)) ? request.phases.filter(p=>p&&p.phase) : [];
|
|
2940
2958
|
const recipeScale = ph => /close/i.test(ph)?0.99:0.97;
|
|
2959
|
+
const recipeBlur = ph => /close/i.test(ph)?2:2;
|
|
2941
2960
|
const recipeDur = ph => /close/i.test(ph)?150:250;
|
|
2942
2961
|
// one scale patch per transform/filter lane that has a backing var, so the
|
|
2943
2962
|
// applied override carries {scale, varName} and the live var gets written.
|
|
2944
2963
|
const scalePatchesFor = (phase, timings) => (timings||[])
|
|
2945
2964
|
.filter(tt=>tt.varName && tt.scale!=null)
|
|
2946
2965
|
.map(tt=>({phase, property:tt.property, member:tt.member||null, varName:tt.varName, scale:recipeScale(phase)}));
|
|
2966
|
+
const blurPatchesFor = (phase, timings) => (timings||[])
|
|
2967
|
+
.filter(tt=>tt.varName && tt.blur!=null)
|
|
2968
|
+
.map(tt=>({phase, property:tt.property, member:tt.member||null, varName:tt.varName, blur:recipeBlur(phase)}));
|
|
2947
2969
|
const phasePatches = (phase, timings) => [
|
|
2948
2970
|
{phase, property:"all", durationMs:recipeDur(phase), easing:_TX_EASE},
|
|
2949
|
-
...scalePatchesFor(phase, timings)
|
|
2971
|
+
...scalePatchesFor(phase, timings),
|
|
2972
|
+
...blurPatchesFor(phase, timings)];
|
|
2950
2973
|
let replacePatches = reqPhases.length>1
|
|
2951
2974
|
? reqPhases.flatMap(p=>phasePatches(p.phase, p.timings))
|
|
2952
2975
|
: phasePatches((request&&request.phase)||null, t);
|
|
@@ -4797,15 +4820,10 @@
|
|
|
4797
4820
|
const groupId=active&&active.groupId;
|
|
4798
4821
|
for(const p of patches){
|
|
4799
4822
|
if(!p.property)continue;
|
|
4800
|
-
const
|
|
4801
|
-
if(p.durationMs!=null)
|
|
4802
|
-
if(p.delayMs!=null)
|
|
4803
|
-
if(p.easing!=null)
|
|
4804
|
-
// value edits: keep the new value, the backing var, and the from-value
|
|
4805
|
-
// (parsed off the suggestion) so computeChanges can write source on Accept.
|
|
4806
|
-
if(p.scale!=null){o.scale=p.scale;o.scaleFrom=(s.from!=null?parseFloat(s.from):undefined);}
|
|
4807
|
-
if(p.blur!=null){o.blur=p.blur;o.blurFrom=(s.from!=null?parseFloat(s.from):undefined);}
|
|
4808
|
-
if(p.varName!=null)o.varName=p.varName;
|
|
4823
|
+
const timingOverride={};
|
|
4824
|
+
if(p.durationMs!=null)timingOverride.durationMs=p.durationMs;
|
|
4825
|
+
if(p.delayMs!=null)timingOverride.delayMs=p.delayMs;
|
|
4826
|
+
if(p.easing!=null)timingOverride.easing=p.easing;
|
|
4809
4827
|
// resolve the phase entry this patch targets (its sibling open/close
|
|
4810
4828
|
// lives in the same group); default to the active entry.
|
|
4811
4829
|
let target=active;
|
|
@@ -4820,7 +4838,35 @@
|
|
|
4820
4838
|
let lanes=p.property==="all"?et:et.filter(t=>t.property===p.property);
|
|
4821
4839
|
if(p.member)lanes=lanes.filter(t=>(t.member===p.member||t.memberId===p.member));
|
|
4822
4840
|
const ids=lanes.length?lanes.map(t=>t.laneId):[p.property];
|
|
4823
|
-
|
|
4841
|
+
if(Object.keys(timingOverride).length){
|
|
4842
|
+
for(const id of ids)registry.setPropOverride(target.id,id,timingOverride);
|
|
4843
|
+
}
|
|
4844
|
+
|
|
4845
|
+
// Value edits (scale/blur) must land on concrete lanes so live preview
|
|
4846
|
+
// can write the right backing var. For coarse recipe patches that use
|
|
4847
|
+
// property:"all" and omit varName, fan out by lane type.
|
|
4848
|
+
if(p.scale!=null){
|
|
4849
|
+
let scaleLanes=lanes.filter(t=>t.property==="transform"||t.scale!=null);
|
|
4850
|
+
if(!scaleLanes.length&&p.property==="all")scaleLanes=et.filter(t=>t.property==="transform"||t.scale!=null);
|
|
4851
|
+
const scaleFrom=(s.from!=null?parseFloat(s.from):undefined);
|
|
4852
|
+
for(const lane of scaleLanes){
|
|
4853
|
+
const ov={scale:p.scale,scaleFrom};
|
|
4854
|
+
const vn=p.varName??lane.varName;
|
|
4855
|
+
if(vn!=null)ov.varName=vn;
|
|
4856
|
+
registry.setPropOverride(target.id,lane.laneId,ov);
|
|
4857
|
+
}
|
|
4858
|
+
}
|
|
4859
|
+
if(p.blur!=null){
|
|
4860
|
+
let blurLanes=lanes.filter(t=>t.property==="filter"||t.blur!=null);
|
|
4861
|
+
if(!blurLanes.length&&p.property==="all")blurLanes=et.filter(t=>t.property==="filter"||t.blur!=null);
|
|
4862
|
+
const blurFrom=(s.from!=null?parseFloat(s.from):undefined);
|
|
4863
|
+
for(const lane of blurLanes){
|
|
4864
|
+
const ov={blur:p.blur,blurFrom};
|
|
4865
|
+
const vn=p.varName??lane.varName;
|
|
4866
|
+
if(vn!=null)ov.varName=vn;
|
|
4867
|
+
registry.setPropOverride(target.id,lane.laneId,ov);
|
|
4868
|
+
}
|
|
4869
|
+
}
|
|
4824
4870
|
}
|
|
4825
4871
|
setAppliedIds(prev=>({...prev,[s.id]:true}));
|
|
4826
4872
|
},[registry,active,entries]);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "transitions-refine",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.26",
|
|
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": {
|
package/server/motion-tokens.mjs
CHANGED
|
@@ -133,6 +133,7 @@ export function refineTimings(timings, ctx) {
|
|
|
133
133
|
const suggestions = [];
|
|
134
134
|
if (!Array.isArray(timings)) return suggestions;
|
|
135
135
|
const c = ctx || {};
|
|
136
|
+
const blurByMember = new Map(); // memberKey -> {member,hint,sawBlur,varName}
|
|
136
137
|
|
|
137
138
|
for (const t of timings) {
|
|
138
139
|
const prop = t.property || "all";
|
|
@@ -140,6 +141,13 @@ export function refineTimings(timings, ctx) {
|
|
|
140
141
|
// with this lane's own property/member so component words (dropdown, modal,
|
|
141
142
|
// page slide…) are visible to the usage matcher.
|
|
142
143
|
const hint = [c.label, c.selector, c.phase, t.hint, t.property, t.member].filter(Boolean).join(" ");
|
|
144
|
+
const memberKey = t.member || "__all__";
|
|
145
|
+
if (!blurByMember.has(memberKey)) {
|
|
146
|
+
blurByMember.set(memberKey, { member: t.member || null, hint, sawBlur: false, varName: null });
|
|
147
|
+
}
|
|
148
|
+
const bm = blurByMember.get(memberKey);
|
|
149
|
+
bm.hint = [bm.hint, hint].filter(Boolean).join(" ");
|
|
150
|
+
if (t.varName && (!bm.varName || prop === "filter")) bm.varName = t.varName;
|
|
143
151
|
|
|
144
152
|
// Duration → nearest token (skip if already on-grid or within 10ms).
|
|
145
153
|
if (Number.isFinite(t.durationMs)) {
|
|
@@ -199,6 +207,7 @@ export function refineTimings(timings, ctx) {
|
|
|
199
207
|
// Blur → a transitions.dev blur token (usage-first, nearest fallback). The
|
|
200
208
|
// captured value is the non-resting "pre" blur (settles to 0); skip 0/none.
|
|
201
209
|
if (Number.isFinite(t.blur) && t.blur > 1e-4) {
|
|
210
|
+
bm.sawBlur = true;
|
|
202
211
|
const usage = pickBlurByUsage(hint);
|
|
203
212
|
const { token: near, delta } = nearestBlur(t.blur);
|
|
204
213
|
const token = usage || near;
|
|
@@ -218,5 +227,30 @@ export function refineTimings(timings, ctx) {
|
|
|
218
227
|
}
|
|
219
228
|
}
|
|
220
229
|
|
|
230
|
+
// Missing-blur detection: when usage clearly maps to a blur token but no blur
|
|
231
|
+
// lane is present, suggest adding one so the motion isn't opacity-only.
|
|
232
|
+
for (const [memberKey, info] of blurByMember) {
|
|
233
|
+
if (info.sawBlur) continue;
|
|
234
|
+
const token = pickBlurByUsage(info.hint);
|
|
235
|
+
if (!token) continue;
|
|
236
|
+
const member = info.member;
|
|
237
|
+
suggestions.push({
|
|
238
|
+
id: `${memberKey}-blur-add`,
|
|
239
|
+
kind: "blur",
|
|
240
|
+
property: "filter",
|
|
241
|
+
member,
|
|
242
|
+
title: `Blur → ${token.name}`,
|
|
243
|
+
from: "0px",
|
|
244
|
+
to: `${token.px}px`,
|
|
245
|
+
patch: {
|
|
246
|
+
property: "filter",
|
|
247
|
+
blur: token.px,
|
|
248
|
+
...(info.varName ? { varName: info.varName } : {}),
|
|
249
|
+
...(member ? { member } : {}),
|
|
250
|
+
},
|
|
251
|
+
reason: `${token.name} (${token.px}px) is the transitions.dev blur token for ${token.usage}. This transition currently has no blur.`,
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
221
255
|
return suggestions;
|
|
222
256
|
}
|
package/server/relay.mjs
CHANGED
|
@@ -265,9 +265,26 @@ const RECIPES_BLOCK = [
|
|
|
265
265
|
"- Card hover tilt: 3D tilt toward the pointer (19-card-tilt.md)",
|
|
266
266
|
"- Plus to menu morph: a circular trigger becomes the surface it opens (20-plus-menu-morph.md)",
|
|
267
267
|
"- Accordion expand: a collapsible body grows/shrinks in height (21-accordion.md)",
|
|
268
|
+
"Recipe non-resting values — the pre-blur/pre-scale the recipe animates FROM (settling to blur 0 / scale 1). When you pick one of these recipes you MUST also emit a lane-level value patch for it, EVEN IF the input has no such lane (add the lane so the element gains the recipe's blur/scale):",
|
|
269
|
+
" - Panel reveal → filter blur 2px (Small)",
|
|
270
|
+
" - Page side-by-side → filter blur 3px (Medium) on the sliding page",
|
|
271
|
+
" - Icon swap → filter blur 2px (Small)",
|
|
272
|
+
" - Skeleton loader and reveal → filter blur 2px (Small)",
|
|
273
|
+
" - Texts reveal → filter blur 3px (Medium)",
|
|
274
|
+
" - Success check → filter blur 8px (Large)",
|
|
275
|
+
" - Modal open/close → transform scale 0.96 (Large)",
|
|
276
|
+
" - Menu dropdown → transform scale 0.97 (Medium) open, 0.99 (Tiny) close",
|
|
277
|
+
" - Tooltip open/close → transform scale 0.98 (Small)",
|
|
268
278
|
"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.",
|
|
269
279
|
].join("\n");
|
|
270
280
|
|
|
281
|
+
// Shared guidance for the token-tweak passes (small + both). Mirrors the
|
|
282
|
+
// deterministic engine's "missing-blur" path: some usages animate a non-resting
|
|
283
|
+
// pre-blur that a bare opacity/transform capture won't show, so the agent must
|
|
284
|
+
// ADD it rather than only tweak blur lanes that already exist.
|
|
285
|
+
const MISSING_BLUR_NOTE =
|
|
286
|
+
"MISSING BLUR: blur can be ABSENT, not just off-token. When the inferred usage clearly implies a non-resting pre-blur per the Blur tokens above (e.g. a page slide / page side-by-side panel → 3px Medium; an icon swap, text swap, panel reveal, skeleton reveal or number pop-in → 2px Small; a text reveal → 3px Medium; a success check → 8px Large) but the input timings carry NO `filter` lane, ADD one: emit a `blur` suggestion with `property:\"filter\"`, `from:\"0px\"`, `to` the usage token (e.g. \"3px\"), echoing the `member` (and `varName` if present) of the sibling lanes so the sliding/swapping element gains the recipe's blur. Only add blur when the usage genuinely calls for it — never on motion that is intentionally opacity-only (a plain fade, a colour/theme change).";
|
|
287
|
+
|
|
271
288
|
function buildPrompt(job) {
|
|
272
289
|
const r = job.request || {};
|
|
273
290
|
const rawType = r.refineType || "small";
|
|
@@ -300,7 +317,7 @@ function buildPrompt(job) {
|
|
|
300
317
|
);
|
|
301
318
|
if (multiPhase) {
|
|
302
319
|
lines.push(
|
|
303
|
-
"RELATED PHASES: `phases` lists this transition's related states (e.g. open AND close). They are ONE motion — a recipe swap must update them TOGETHER. For the replace suggestion emit a `patches` array with ONE entry per phase in `phases`,
|
|
320
|
+
"RELATED PHASES: `phases` lists this transition's related states (e.g. open AND close). They are ONE motion — a recipe swap must update them TOGETHER. For the replace suggestion emit a `patches` array with ONE timing entry per phase in `phases`, shaped `{\"phase\":<phase>,\"property\":\"all\",\"durationMs\":...,\"easing\":...}`. If the recipe uses transform pre-scale or filter pre-blur (see \"Recipe non-resting values\" above), add EXTRA per-lane value patches for those lanes (not property:\"all\"), e.g. `{\"phase\":\"open\",\"property\":\"transform\",\"member\":\"Panel\",\"varName\":\"--dropdown-pre-scale\",\"scale\":0.97}` and `{\"phase\":\"open\",\"property\":\"filter\",\"member\":\"Panel\",\"varName\":\"--panel-blur\",\"blur\":2}`. Copy each lane's `member` + `varName` from the input timings when that lane exists; when the recipe adds a lane the input LACKS (e.g. Page side-by-side prescribes a 3px blur but the captured page has no `filter` lane), STILL emit the blur patch — reuse the sibling lane's `member` so it targets the right element and omit `varName`. Take each phase duration from MOTION TOKENS (open usually slower than close, e.g. 250ms/150ms). Also include a single `patch` for live preview (first timing patch is fine). Apply will write every phase from `patches`.",
|
|
304
321
|
"",
|
|
305
322
|
);
|
|
306
323
|
}
|
|
@@ -310,21 +327,23 @@ function buildPrompt(job) {
|
|
|
310
327
|
"refineType is \"replace\": suggest a WHOLE-TRANSITION replacement ONLY — do NOT propose motion-token tweaks (no kind \"duration\"/\"delay\"/\"easing\").",
|
|
311
328
|
multiPhase
|
|
312
329
|
? "Pick the SINGLE best-fit recipe from the list above. Emit ONE suggestion with kind \"replace\": include the `patches` array (one entry per related phase, as described above) AND a single `patch` (the first phase) for the live preview, 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."
|
|
313
|
-
: "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.",
|
|
330
|
+
: "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 the recipe prescribes a non-resting pre-blur/pre-scale (see \"Recipe non-resting values\" above), put that value on the `patch` too (add `blur` for a filter-blur recipe like Page side-by-side → 3px, `scale` for a scale recipe) so the sliding/scaling element gains it even when the captured transition had no such lane. If no recipe genuinely fits the usage, return an empty suggestions array.",
|
|
314
331
|
"Answer in ONE response — do NOT read or search files.",
|
|
315
332
|
);
|
|
316
333
|
} else if (refineType === "both") {
|
|
317
334
|
lines.push(
|
|
318
335
|
"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.",
|
|
319
336
|
"(1) Motion-token tweaks (kind \"duration\"/\"delay\"/\"easing\"/\"scale\"/\"blur\"): for each declaration, propose the token value only where it DIFFERS from the current one. Use \"scale\" for an off-token transform pre-scale and \"blur\" for an off-token filter pre-blur, picked by usage.",
|
|
337
|
+
MISSING_BLUR_NOTE,
|
|
320
338
|
multiPhase
|
|
321
339
|
? "(2) Whole-transition replacement (kind \"replace\"): ALWAYS evaluate one — pick the SINGLE best-fit recipe. Emit at most ONE \"replace\" suggestion with the `patches` array (one entry per related phase) AND a single `patch` (the first phase), a `reference` field, and the recipe named in `title`/`reason`. If no recipe genuinely fits, omit the replace suggestion."
|
|
322
|
-
: "(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\")
|
|
340
|
+
: "(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\"); if the recipe prescribes a non-resting pre-blur/pre-scale (see \"Recipe non-resting values\" above) put that `blur`/`scale` on the `patch` too so e.g. Page side-by-side gains its 3px blur. Add a `reference` field with the reference filename, and name the recipe in `title`/`reason`. If no recipe genuinely fits, omit the replace suggestion.",
|
|
323
341
|
"Answer in ONE response — do NOT read or search files.",
|
|
324
342
|
);
|
|
325
343
|
} else {
|
|
326
344
|
lines.push(
|
|
327
345
|
"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\"/\"scale\"/\"blur\"). Use \"scale\" for an off-token transform pre-scale and \"blur\" for an off-token filter pre-blur, picked by usage. Do NOT propose a whole-transition replacement (no kind \"replace\") — the Replace tab requests that separately.",
|
|
346
|
+
MISSING_BLUR_NOTE,
|
|
328
347
|
"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.",
|
|
329
348
|
);
|
|
330
349
|
}
|
|
@@ -335,7 +354,7 @@ function buildPrompt(job) {
|
|
|
335
354
|
'A scale tweak looks like {"id":"transform-scale","kind":"scale","property":"transform","member":"Menu","title":"Scale → Medium","from":"0.8","to":"0.97","patch":{"property":"transform","member":"Menu","scale":0.97},"reason":"…"}; a blur tweak like {"id":"filter-blur","kind":"blur","property":"filter","member":"Panel","title":"Blur → Small","from":"8px","to":"2px","patch":{"property":"filter","member":"Panel","blur":2},"reason":"…"}.',
|
|
336
355
|
"CRITICAL — `member`: every suggestion and its `patch` MUST echo the `member` of the input lane it came from, copied VERBATIM from that lane in the data above. This is REQUIRED whenever any input lane carries a `member` — most importantly when several lanes share the same `property` (e.g. a dropdown where a caret does `transform: rotate` AND a panel does `transform: scale`): the `member` is the ONLY way to tell which lane a `transform` tweak targets. Omitting it mislabels the suggestion onto the wrong element. Omit `member` only for lanes that genuinely have none.",
|
|
337
356
|
multiPhase
|
|
338
|
-
? 'A multi-phase replace suggestion ALSO carries a `patches` array
|
|
357
|
+
? 'A multi-phase replace suggestion ALSO carries a `patches` array. Put timing in phase-level entries (`property:"all"`, duration/easing), and put scale/blur in lane-level entries (`property:"transform"`/`"filter"` plus the lane `member` and `varName` from input timings). In each `patch`/`patches` entry include only changed fields (durationMs, delayMs, easing, scale, blur — plus `member` copied from input lane and `varName` when present); `property` must match an input property or "all".'
|
|
339
358
|
: 'In each `patch` include only the changed fields (durationMs, delayMs, easing, scale, blur — plus `member` copied from the input lane, and `varName` if the input lane had one); `property` must match an input property or "all". If nothing should change, return an empty suggestions array.',
|
|
340
359
|
);
|
|
341
360
|
return lines.join("\n");
|