synthesisui 0.16.27 → 0.16.28

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.
@@ -69,8 +69,14 @@ const FONT = /font-family\s*:\s*([^;}\n]+)/g;
69
69
  * time; the value normalizer makes `.3s` and `300ms` one literal, so the
70
70
  * system's own duration token gets NAMED, not just flagged.
71
71
  */
72
- const MOTION_PROP = /\b(?:transition|animation)(?:-(?:duration|delay|timing-function)|Duration|Delay|TimingFunction)?\s*:\s*["'`]?([^;}"'`\n]*)/g;
73
- const TIME_LITERAL = /(\d*\.?\d+)(ms|s)\b/g;
72
+ // `]` ends the value too: Tailwind's arbitrary syntax (`[animation:...]`)
73
+ // closes with it, and without the stop the "value" swallowed the rest of the
74
+ // className - first real report blamed classes that sat beside the animation.
75
+ const MOTION_PROP = /\b(?:transition|animation)(?:-(?:duration|delay|timing-function)|Duration|Delay|TimingFunction)?\s*:\s*["'`]?([^;}\]"'`\n]*)/g;
76
+ // Trailing guard instead of `\b`: in arbitrary syntax spaces are `_`, which is
77
+ // a word char, so `200ms_ease` failed `\b` and the hand-written clock passed
78
+ // unseen (found on the generated button.tsx, first field run of the family).
79
+ const TIME_LITERAL = /(\d*\.?\d+)(ms|s)(?![a-zA-Z0-9%])/g;
74
80
  const BEZIER = /cubic-bezier\([^)]*\)/g;
75
81
  /** `duration-300`, `duration-[350ms]`, `delay-75` - Tailwind's stock clock. */
76
82
  const TW_TIME = /\b(?:duration|delay)-(?:\[([^\]]+)\]|(\d+)\b)/g;
@@ -344,8 +350,15 @@ export function scanSource(file, source, table) {
344
350
  }
345
351
  // A `ds-*` animation name that the installed css never declares renders
346
352
  // as NOTHING, silently - the keyframe cousin of a phantom token.
353
+ //
354
+ // Two guards, both from the first field run (soft-test, 29/07): the
355
+ // lookbehind keeps `var(--ds-motion-durations-base)` from reading as a
356
+ // keyframe named after the token (`-` before `ds-` means it is a custom
357
+ // property, not a name); and the name must END on a word - the greedy
358
+ // run used to stop at Tailwind's `_` separator and report a truncated
359
+ // `ds-soft-test-` as phantom while the real keyframe existed.
347
360
  if (table.keyframes.size > 0) {
348
- for (const w of value.matchAll(/\b(ds-[a-z0-9-]+)\b/g)) {
361
+ for (const w of value.matchAll(/(?<![\w-])(ds-[a-z0-9]+(?:-[a-z0-9]+)*)/g)) {
349
362
  if (!table.keyframes.has(w[1]))
350
363
  phantoms.push({ name: `@keyframes ${w[1]}`, line: at });
351
364
  }
package/dist/guide.js CHANGED
@@ -147,10 +147,18 @@ ${depLines.join("\n")}
147
147
  loop: "ambient loop · infinite - decorative, use sparingly",
148
148
  attention: "attention · one shot, only when asked",
149
149
  };
150
+ // Promise only what the ARTIFACTS actually contain, not what this CLI
151
+ // version knows how to describe. First field run (soft-test, 29/07): a
152
+ // newer CLI wrote "already compiled as ready utilities" against a theme.css
153
+ // an older server had compiled without --animate-* keys - the agent
154
+ // verified `animate-rise` emitted nothing and filed the GUIDE's own promise
155
+ // as a gap. The artifacts travel in this payload; reading them is free.
156
+ const hasAnimateKeys = /--animate-/.test(payload.artifacts["theme.css"] ?? "");
157
+ const hasReducedMotionFloor = Object.values(payload.artifacts).some((css) => /prefers-reduced-motion/.test(css ?? ""));
150
158
  const keyframeLines = Object.entries(motion.keyframes).map(([kname, frames]) => {
151
159
  const kf = frames;
152
160
  const intent = classifyKeyframe(kname, kf);
153
- const usage = hasTailwind
161
+ const usage = hasTailwind && hasAnimateKeys
154
162
  ? `\`animate-${kebab(kname)}\``
155
163
  : `\`animation: ${animationShorthand(`ds-${slug}-${kebab(kname)}`, intent, isFullRotation(kf))}\``;
156
164
  return `- ${usage} - ${INTENT_LINE[intent]} (${describeKeyframe(kf)})`;
@@ -398,7 +406,7 @@ ${keyframeLines.length > 0
398
406
 
399
407
  The base is QUIET: nothing moves until someone asks. When they do, animation is **selection
400
408
  from this list**, not improvisation - these are the system's own animations, already compiled
401
- into ${hasTailwind ? "`theme.css` as ready utilities" : `\`tokens.css\` as \`@keyframes ds-${slug}-<name>\``}, each on the clock its intent calls for:
409
+ into ${hasTailwind && hasAnimateKeys ? "`theme.css` as ready utilities" : `\`tokens.css\` as \`@keyframes ds-${slug}-<name>\``}, each on the clock its intent calls for:
402
410
 
403
411
  ${keyframeLines.join("\n")}
404
412
  ${patternLines.length > 0
@@ -409,8 +417,9 @@ ${patternLines.join("\n")}
409
417
  `
410
418
  : ""}
411
419
  Rules: entrances run once, never on scroll-loop; ambient loops are decoration, one per view is
412
- plenty; \`prefers-reduced-motion\` is already honored in the shipped CSS (a floor collapses all
413
- animations) - don't undo it. If a request needs a motion this vocabulary lacks, do NOT hand-roll
420
+ plenty; ${hasReducedMotionFloor
421
+ ? "`prefers-reduced-motion` is already honored in the shipped CSS (a floor collapses all\nanimations) - don't undo it"
422
+ : "honor `prefers-reduced-motion` yourself (wrap animations in `motion-safe:` or a media query) - this build of the CSS ships no floor"}. If a request needs a motion this vocabulary lacks, do NOT hand-roll
414
423
  \`@keyframes\` or raw durations: file it (\`request_token\` via MCP, or \`synthesisui request\`) and
415
424
  tell the person what you chose from the vocabulary instead.`
416
425
  : ""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "synthesisui",
3
- "version": "0.16.27",
3
+ "version": "0.16.28",
4
4
  "description": "Bring SynthesisUI design systems into any project - tokens, typed components, whole pages and an agent-ready CLAUDE.md manifest.",
5
5
  "type": "module",
6
6
  "bin": {