@decantr/cli 1.5.0 → 1.5.2

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.
@@ -298,11 +298,11 @@ function generateTokensCSS(themeData, mode) {
298
298
  "--d-radius-lg": "0.75rem",
299
299
  "--d-radius-xl": "1rem",
300
300
  "--d-radius-full": "9999px",
301
- // Shadows
302
- "--d-shadow-sm": "0 1px 2px rgba(0,0,0,0.05)",
303
- "--d-shadow": "0 1px 3px rgba(0,0,0,0.1)",
304
- "--d-shadow-md": "0 4px 6px rgba(0,0,0,0.1)",
305
- "--d-shadow-lg": "0 10px 15px rgba(0,0,0,0.1)",
301
+ // Shadows — dark mode needs higher opacity to be visible on dark backgrounds
302
+ "--d-shadow-sm": tokenMode === "light" ? "0 1px 2px rgba(0,0,0,0.05)" : "0 1px 2px rgba(0,0,0,0.2)",
303
+ "--d-shadow": tokenMode === "light" ? "0 1px 3px rgba(0,0,0,0.1)" : "0 1px 3px rgba(0,0,0,0.25)",
304
+ "--d-shadow-md": tokenMode === "light" ? "0 4px 6px rgba(0,0,0,0.1)" : "0 4px 6px rgba(0,0,0,0.3)",
305
+ "--d-shadow-lg": tokenMode === "light" ? "0 10px 15px rgba(0,0,0,0.1)" : "0 10px 15px rgba(0,0,0,0.4)",
306
306
  // Status colors
307
307
  "--d-success": themeData.tokens?.base?.success || "#22c55e",
308
308
  "--d-error": themeData.tokens?.base?.danger || "#ef4444",
@@ -319,7 +319,19 @@ ${lines}
319
319
  `;
320
320
  if (mode === "auto") {
321
321
  const lightTokens = buildTokens("light");
322
- const paletteKeys = ["--d-bg", "--d-surface", "--d-surface-raised", "--d-border", "--d-text", "--d-text-muted", "--d-primary-hover"];
322
+ const paletteKeys = [
323
+ "--d-bg",
324
+ "--d-surface",
325
+ "--d-surface-raised",
326
+ "--d-border",
327
+ "--d-text",
328
+ "--d-text-muted",
329
+ "--d-primary-hover",
330
+ "--d-shadow-sm",
331
+ "--d-shadow",
332
+ "--d-shadow-md",
333
+ "--d-shadow-lg"
334
+ ];
323
335
  const lightLines = Object.entries(lightTokens).filter(([key]) => paletteKeys.includes(key)).map(([key, value]) => ` ${key}: ${value};`).join("\n");
324
336
  css += `
325
337
  @media (prefers-color-scheme: light) {
@@ -357,6 +369,65 @@ function generateDecoratorsCSS(recipeData, themeName) {
357
369
  `);
358
370
  return css.join("\n");
359
371
  }
372
+ function generateGlobalCSS(personality) {
373
+ const personalityText = personality.join(" ").toLowerCase();
374
+ let fontBody = "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif";
375
+ if (personalityText.includes("inter")) {
376
+ fontBody = `'Inter', ${fontBody}`;
377
+ } else if (personalityText.includes("geist")) {
378
+ fontBody = `'Geist', ${fontBody}`;
379
+ }
380
+ return `/* Generated by @decantr/cli \u2014 global reset + body styles */
381
+
382
+ *, *::before, *::after {
383
+ box-sizing: border-box;
384
+ margin: 0;
385
+ padding: 0;
386
+ }
387
+
388
+ html {
389
+ color-scheme: dark;
390
+ -webkit-font-smoothing: antialiased;
391
+ -moz-osx-font-smoothing: grayscale;
392
+ text-rendering: optimizeLegibility;
393
+ }
394
+
395
+ body {
396
+ font-family: ${fontBody};
397
+ background: var(--d-bg);
398
+ color: var(--d-text);
399
+ line-height: 1.6;
400
+ min-height: 100dvh;
401
+ }
402
+
403
+ img, picture, video, canvas, svg {
404
+ display: block;
405
+ max-width: 100%;
406
+ }
407
+
408
+ input, button, textarea, select {
409
+ font: inherit;
410
+ color: inherit;
411
+ }
412
+
413
+ :focus-visible {
414
+ outline: 2px solid var(--d-primary);
415
+ outline-offset: 2px;
416
+ }
417
+
418
+ .sr-only {
419
+ position: absolute;
420
+ width: 1px;
421
+ height: 1px;
422
+ padding: 0;
423
+ margin: -1px;
424
+ overflow: hidden;
425
+ clip: rect(0, 0, 0, 0);
426
+ white-space: nowrap;
427
+ border-width: 0;
428
+ }
429
+ `;
430
+ }
360
431
  function generateDecoratorsContext(recipeData, recipeName) {
361
432
  const lines = [];
362
433
  lines.push(`# Recipe Decorators: ${recipeName}`);
@@ -390,6 +461,12 @@ function generateDecoratorsContext(recipeData, recipeName) {
390
461
  function generateDecoratorRule(name, description) {
391
462
  const rules = [];
392
463
  const descLower = description.toLowerCase();
464
+ const nameLower = name.toLowerCase();
465
+ const isCard = descLower.includes("card") || descLower.includes("panel") || nameLower.includes("card") || nameLower.includes("panel");
466
+ const isInput = descLower.includes("input") || descLower.includes("field") || descLower.includes("textarea") || nameLower.includes("input") || nameLower.includes("textarea");
467
+ const isGlass = descLower.includes("glassmorphic") || descLower.includes("glass") || nameLower.includes("glass");
468
+ const isInteractive = isCard || isInput || isGlass;
469
+ const isNonInteractive = descLower.includes("divider") || descLower.includes("skeleton") || descLower.includes("keyframe") || descLower.includes("canvas") || nameLower.includes("divider") || nameLower.includes("skeleton") || nameLower.includes("canvas");
393
470
  if (descLower.includes("monospace") || descLower.includes("mono font")) {
394
471
  rules.push("font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, 'DejaVu Sans Mono', monospace");
395
472
  }
@@ -402,6 +479,16 @@ function generateDecoratorRule(name, description) {
402
479
  } else if (descLower.includes("primary-tinted") || descLower.includes("primary background")) {
403
480
  rules.push("background: color-mix(in srgb, var(--d-primary) 15%, var(--d-surface))");
404
481
  }
482
+ if (isInput) {
483
+ if (!rules.some((r) => r.startsWith("background"))) {
484
+ rules.push("background: var(--d-surface)");
485
+ }
486
+ rules.push("color: var(--d-text)");
487
+ rules.push("padding: 0.5rem 0.75rem");
488
+ rules.push("border-radius: var(--d-radius)");
489
+ rules.push("width: 100%");
490
+ rules.push("outline: none");
491
+ }
405
492
  const leftBorderMatch = descLower.match(/(\d+)px\s+left\s+border/);
406
493
  if (leftBorderMatch) {
407
494
  rules.push(`border-left: ${leftBorderMatch[1]}px solid var(--d-primary)`);
@@ -415,23 +502,40 @@ function generateDecoratorRule(name, description) {
415
502
  const radiusMatch = descLower.match(/(\d+)px radius/);
416
503
  if (radiusMatch) {
417
504
  rules.push(`border-radius: ${radiusMatch[1]}px`);
418
- } else if (descLower.includes("radius") || descLower.includes("rounded")) {
505
+ } else if ((descLower.includes("radius") || descLower.includes("rounded")) && !isInput) {
419
506
  rules.push("border-radius: var(--d-radius)");
420
507
  }
421
- if (descLower.includes("hover shadow") || descLower.includes("shadow transition")) {
422
- rules.push("transition: box-shadow 0.15s ease");
423
- }
424
508
  if (descLower.includes("elevation") || descLower.includes("shadow")) {
425
509
  rules.push("box-shadow: var(--d-shadow)");
426
510
  }
511
+ if (isInteractive && !isNonInteractive) {
512
+ rules.push("transition: border-color 0.15s ease, box-shadow 0.15s ease, background 0.15s ease");
513
+ }
427
514
  if (descLower.includes("entrance animation") || descLower.includes("fade")) {
428
515
  rules.push("animation: decantr-fade-in 0.2s ease-out");
429
516
  }
430
517
  if (descLower.includes("pulse animation") || descLower.includes("skeleton")) {
431
518
  rules.push("animation: decantr-pulse 1.5s ease-in-out infinite");
432
519
  }
433
- if (descLower.includes("blur") || descLower.includes("glass")) {
520
+ const blurMatch = descLower.match(/blur\((\d+)px\)/);
521
+ if (blurMatch) {
522
+ rules.push(`backdrop-filter: blur(${blurMatch[1]}px)`);
523
+ rules.push(`-webkit-backdrop-filter: blur(${blurMatch[1]}px)`);
524
+ } else if (isGlass) {
525
+ rules.push("backdrop-filter: blur(12px)");
526
+ rules.push("-webkit-backdrop-filter: blur(12px)");
527
+ } else if (descLower.includes("blur")) {
434
528
  rules.push("backdrop-filter: blur(8px)");
529
+ rules.push("-webkit-backdrop-filter: blur(8px)");
530
+ }
531
+ if (descLower.includes("semi-transparent") || descLower.includes("glassmorphic")) {
532
+ const bgIdx = rules.findIndex((r) => r.startsWith("background:"));
533
+ const rgbaBg = "background: rgba(31, 31, 35, 0.8)";
534
+ if (bgIdx !== -1) {
535
+ rules[bgIdx] = rgbaBg;
536
+ } else {
537
+ rules.push(rgbaBg);
538
+ }
435
539
  }
436
540
  if (descLower.includes("right-aligned")) {
437
541
  rules.push("margin-left: auto");
@@ -455,9 +559,33 @@ function generateDecoratorRule(name, description) {
455
559
  if (rules.length === 0) {
456
560
  return `/* .${name}: ${description} */`;
457
561
  }
458
- return `.${name} {
562
+ let css = `.${name} {
459
563
  ${rules.join(";\n ")};
460
564
  }`;
565
+ if (isInteractive && !isNonInteractive) {
566
+ const stateRules = [];
567
+ if (isCard || isGlass) {
568
+ stateRules.push(`.${name}:hover {
569
+ border-color: var(--d-primary-hover, var(--d-border));
570
+ box-shadow: var(--d-shadow-md);
571
+ }`);
572
+ }
573
+ if (isInput) {
574
+ stateRules.push(`.${name}:focus {
575
+ border-color: var(--d-primary);
576
+ box-shadow: 0 0 0 3px color-mix(in srgb, var(--d-primary) 25%, transparent);
577
+ }`);
578
+ stateRules.push(`.${name}::placeholder {
579
+ color: var(--d-text-muted);
580
+ }`);
581
+ stateRules.push(`.${name}:disabled {
582
+ opacity: 0.5;
583
+ cursor: not-allowed;
584
+ }`);
585
+ }
586
+ css += "\n\n" + stateRules.join("\n\n");
587
+ }
588
+ return css;
461
589
  }
462
590
  function serializeLayoutItem(item) {
463
591
  if (typeof item === "string") {
@@ -1006,33 +1134,29 @@ function generateTaskContext(templateName, essence) {
1006
1134
  };
1007
1135
  return renderTemplate(template, vars);
1008
1136
  }
1009
- function generateEssenceSummary(essence) {
1010
- const template = loadTemplate("essence-summary.md.template");
1011
- const pagesTable = `| Page | Shell | Layout |
1012
- |------|-------|--------|
1013
- ${essence.structure.map((p) => `| ${p.id} | ${p.shell} | ${p.layout.map(serializeLayoutItem).join(", ") || "none"} |`).join("\n")}`;
1014
- const featuresList = essence.features.length > 0 ? essence.features.map((f) => `- ${f}`).join("\n") : "- No features specified";
1015
- const dnaEnforcement = essence.guard.enforce_style ? "error" : "off";
1016
- const blueprintEnforcement = essence.guard.enforce_recipe ? "warn" : "off";
1137
+ function generateTaskContextV3(templateName, essence) {
1138
+ const template = loadTemplate(templateName);
1139
+ const pages = essence.blueprint.sections && essence.blueprint.sections.length > 0 ? essence.blueprint.sections.flatMap((s) => s.pages) : essence.blueprint.pages || [];
1140
+ const defaultShell = essence.blueprint.sections?.[0]?.shell || essence.blueprint.shell || "sidebar-main";
1141
+ const layout = pages[0]?.layout?.map(serializeLayoutItem).join(", ") || "none";
1142
+ const scaffoldStructure = pages.map((p) => {
1143
+ const patterns = p.layout.length > 0 ? `
1144
+ - Patterns: ${p.layout.map(serializeLayoutItem).join(", ")}` : "";
1145
+ return `- **${p.id}** (${defaultShell})${patterns}`;
1146
+ }).join("\n");
1147
+ const densityLevel = essence.dna.spacing?.density || "comfortable";
1148
+ const contentGap = essence.dna.spacing?.content_gap || "_gap4";
1017
1149
  const vars = {
1018
- ARCHETYPE: essence.archetype || "custom",
1019
- BLUEPRINT: essence.blueprint || "none",
1020
- PERSONALITY: essence.personality.join(", "),
1021
- TARGET: essence.target,
1022
- THEME_STYLE: essence.theme.style,
1023
- THEME_MODE: essence.theme.mode,
1024
- THEME_RECIPE: essence.theme.recipe,
1025
- SHAPE: essence.theme.shape,
1026
- PAGES_TABLE: pagesTable,
1027
- FEATURES_LIST: featuresList,
1028
- GUARD_MODE: essence.guard.mode,
1029
- ENFORCE_STYLE: String(essence.guard.enforce_style),
1030
- ENFORCE_RECIPE: String(essence.guard.enforce_recipe),
1031
- DNA_ENFORCEMENT: dnaEnforcement,
1032
- BLUEPRINT_ENFORCEMENT: blueprintEnforcement,
1033
- DENSITY: essence.density.level,
1034
- CONTENT_GAP: essence.density.content_gap,
1035
- LAST_UPDATED: (/* @__PURE__ */ new Date()).toISOString()
1150
+ TARGET: essence.meta.target || "react",
1151
+ THEME_STYLE: essence.dna.theme.style,
1152
+ THEME_MODE: essence.dna.theme.mode,
1153
+ THEME_RECIPE: essence.dna.theme.recipe || essence.dna.theme.style,
1154
+ DEFAULT_SHELL: defaultShell,
1155
+ GUARD_MODE: essence.meta.guard.mode,
1156
+ LAYOUT: layout,
1157
+ DENSITY: densityLevel,
1158
+ CONTENT_GAP: contentGap,
1159
+ SCAFFOLD_STRUCTURE: scaffoldStructure
1036
1160
  };
1037
1161
  return renderTemplate(template, vars);
1038
1162
  }
@@ -1116,15 +1240,6 @@ async function scaffoldProject(projectRoot, options, detected, registry, archety
1116
1240
  const scaffoldPath = join(contextDir, "task-scaffold.md");
1117
1241
  writeFileSync(scaffoldPath, generateTaskContext("task-scaffold.md.template", essence));
1118
1242
  contextFiles.push(scaffoldPath);
1119
- const addPagePath = join(contextDir, "task-add-page.md");
1120
- writeFileSync(addPagePath, generateTaskContext("task-add-page.md.template", essence));
1121
- contextFiles.push(addPagePath);
1122
- const modifyPath = join(contextDir, "task-modify.md");
1123
- writeFileSync(modifyPath, generateTaskContext("task-modify.md.template", essence));
1124
- contextFiles.push(modifyPath);
1125
- const summaryPath = join(contextDir, "essence-summary.md");
1126
- writeFileSync(summaryPath, generateEssenceSummary(essence));
1127
- contextFiles.push(summaryPath);
1128
1243
  if (composedSections) {
1129
1244
  essenceV3.version = "3.1.0";
1130
1245
  essenceV3.blueprint = {
@@ -1133,7 +1248,7 @@ async function scaffoldProject(projectRoot, options, detected, registry, archety
1133
1248
  routes: routeMap || {}
1134
1249
  };
1135
1250
  if (blueprintData?.personality?.length) {
1136
- essenceV3.dna.personality = blueprintData.personality;
1251
+ essenceV3.dna.personality = typeof blueprintData.personality === "string" ? [blueprintData.personality] : blueprintData.personality;
1137
1252
  }
1138
1253
  if (blueprintData?.design_constraints) {
1139
1254
  essenceV3.dna.constraints = blueprintData.design_constraints;
@@ -1146,7 +1261,7 @@ async function scaffoldProject(projectRoot, options, detected, registry, archety
1146
1261
  }
1147
1262
  writeFileSync(essencePath, JSON.stringify(essenceV3, null, 2) + "\n");
1148
1263
  }
1149
- const refreshResult = await refreshDerivedFiles(projectRoot, essenceV3, registry, themeData, recipeData);
1264
+ const refreshResult = await refreshDerivedFiles(projectRoot, essenceV3, registry, themeData, recipeData, { isInitialScaffold: true });
1150
1265
  contextFiles.push(...refreshResult.contextFiles);
1151
1266
  const gitignoreUpdated = updateGitignore(projectRoot);
1152
1267
  return {
@@ -1348,7 +1463,7 @@ When available, use these tools:
1348
1463
  gitignoreUpdated
1349
1464
  };
1350
1465
  }
1351
- async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThemeData, prefetchedRecipeData) {
1466
+ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThemeData, prefetchedRecipeData, options) {
1352
1467
  const decantrDir = join(projectRoot, ".decantr");
1353
1468
  const contextDir = join(decantrDir, "context");
1354
1469
  mkdirSync(contextDir, { recursive: true });
@@ -1452,14 +1567,30 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1452
1567
  if (hasRealRecipeData || !existsSync(decoratorsPath)) {
1453
1568
  writeFileSync(decoratorsPath, generateDecoratorsCSS(recipeData, themeName));
1454
1569
  }
1455
- const cssFiles = [tokensPath, decoratorsPath];
1570
+ const globalPath = join(stylesDir, "global.css");
1571
+ if (!existsSync(globalPath)) {
1572
+ writeFileSync(globalPath, generateGlobalCSS(personality));
1573
+ }
1574
+ const cssFiles = [tokensPath, decoratorsPath, globalPath];
1456
1575
  const decoratorsMdPath = join(contextDir, "decorators.md");
1457
1576
  writeFileSync(decoratorsMdPath, generateDecoratorsContext(recipeData, recipeName));
1458
1577
  const decantrMdPath = join(projectRoot, "DECANTR.md");
1459
1578
  writeFileSync(decantrMdPath, generateDecantrMdV31(guardMode, CSS_APPROACH_CONTENT));
1460
- const summaryPath = join(contextDir, "essence-summary.md");
1461
- writeFileSync(summaryPath, generateEssenceSummaryV3(essence));
1462
- const contextFiles = [decoratorsMdPath, summaryPath];
1579
+ const hasSections = essence.blueprint.sections && essence.blueprint.sections.length > 0;
1580
+ const contextFiles = [decoratorsMdPath];
1581
+ if (!hasSections) {
1582
+ const summaryPath = join(contextDir, "essence-summary.md");
1583
+ writeFileSync(summaryPath, generateEssenceSummaryV3(essence));
1584
+ contextFiles.push(summaryPath);
1585
+ }
1586
+ if (!options?.isInitialScaffold) {
1587
+ const addPagePath = join(contextDir, "task-add-page.md");
1588
+ writeFileSync(addPagePath, generateTaskContextV3("task-add-page.md.template", essence));
1589
+ contextFiles.push(addPagePath);
1590
+ const modifyPath = join(contextDir, "task-modify.md");
1591
+ writeFileSync(modifyPath, generateTaskContextV3("task-modify.md.template", essence));
1592
+ contextFiles.push(modifyPath);
1593
+ }
1463
1594
  const blueprint = essence.blueprint;
1464
1595
  const sections = blueprint.sections && blueprint.sections.length > 0 ? blueprint.sections : [];
1465
1596
  if (sections.length > 0) {
@@ -1492,21 +1623,28 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1492
1623
  slots = synthetic;
1493
1624
  }
1494
1625
  }
1495
- patternSpecs[name] = {
1626
+ const spec = {
1496
1627
  description: inner.description || "",
1497
1628
  components: inner.components || [],
1498
1629
  slots
1499
1630
  };
1631
+ if (!spec.components || spec.components.length === 0) {
1632
+ const syntheticComps = generateSyntheticComponents(name, spec.description);
1633
+ if (syntheticComps.length > 0) spec.components = syntheticComps;
1634
+ }
1635
+ patternSpecs[name] = spec;
1500
1636
  } else {
1501
1637
  const synthetic = generateSyntheticSlots(name, "");
1502
- if (Object.keys(synthetic).length > 0) {
1503
- patternSpecs[name] = { description: "", components: [], slots: synthetic };
1638
+ const syntheticComps = generateSyntheticComponents(name, "");
1639
+ if (Object.keys(synthetic).length > 0 || syntheticComps.length > 0) {
1640
+ patternSpecs[name] = { description: "", components: syntheticComps, slots: synthetic };
1504
1641
  }
1505
1642
  }
1506
1643
  } catch {
1507
1644
  const synthetic = generateSyntheticSlots(name, "");
1508
- if (Object.keys(synthetic).length > 0) {
1509
- patternSpecs[name] = { description: "", components: [], slots: synthetic };
1645
+ const syntheticComps = generateSyntheticComponents(name, "");
1646
+ if (Object.keys(synthetic).length > 0 || syntheticComps.length > 0) {
1647
+ patternSpecs[name] = { description: "", components: syntheticComps, slots: synthetic };
1510
1648
  }
1511
1649
  }
1512
1650
  }
@@ -1666,21 +1804,28 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
1666
1804
  slots = synthetic;
1667
1805
  }
1668
1806
  }
1669
- patternSpecs[name] = {
1807
+ const spec = {
1670
1808
  description: inner.description || "",
1671
1809
  components: inner.components || [],
1672
1810
  slots
1673
1811
  };
1812
+ if (!spec.components || spec.components.length === 0) {
1813
+ const syntheticComps = generateSyntheticComponents(name, spec.description);
1814
+ if (syntheticComps.length > 0) spec.components = syntheticComps;
1815
+ }
1816
+ patternSpecs[name] = spec;
1674
1817
  } else {
1675
1818
  const synthetic = generateSyntheticSlots(name, "");
1676
- if (Object.keys(synthetic).length > 0) {
1677
- patternSpecs[name] = { description: "", components: [], slots: synthetic };
1819
+ const syntheticComps = generateSyntheticComponents(name, "");
1820
+ if (Object.keys(synthetic).length > 0 || syntheticComps.length > 0) {
1821
+ patternSpecs[name] = { description: "", components: syntheticComps, slots: synthetic };
1678
1822
  }
1679
1823
  }
1680
1824
  } catch {
1681
1825
  const synthetic = generateSyntheticSlots(name, "");
1682
- if (Object.keys(synthetic).length > 0) {
1683
- patternSpecs[name] = { description: "", components: [], slots: synthetic };
1826
+ const syntheticComps = generateSyntheticComponents(name, "");
1827
+ if (Object.keys(synthetic).length > 0 || syntheticComps.length > 0) {
1828
+ patternSpecs[name] = { description: "", components: syntheticComps, slots: synthetic };
1684
1829
  }
1685
1830
  }
1686
1831
  }
@@ -1810,6 +1955,27 @@ function generateSyntheticSlots(patternId, description) {
1810
1955
  }
1811
1956
  return syntheticSlots;
1812
1957
  }
1958
+ function generateSyntheticComponents(patternId, description) {
1959
+ const desc = description.toLowerCase();
1960
+ const syntheticComponents = [];
1961
+ if (patternId.includes("hero")) syntheticComponents.push("Button", "Icon", "Image");
1962
+ if (patternId.includes("feature")) syntheticComponents.push("Card", "Icon", "Text");
1963
+ if (patternId.includes("pricing")) syntheticComponents.push("Card", "Button", "Badge");
1964
+ if (patternId.includes("testimonial")) syntheticComponents.push("Card", "Avatar", "Text");
1965
+ if (patternId.includes("cta")) syntheticComponents.push("Button", "Text");
1966
+ if (patternId.includes("form") || patternId.includes("contact")) syntheticComponents.push("Input", "Textarea", "Button", "Label");
1967
+ if (patternId.includes("team")) syntheticComponents.push("Card", "Avatar", "Text");
1968
+ if (patternId.includes("settings") || patternId.includes("security")) syntheticComponents.push("Card", "Toggle", "Input", "Button");
1969
+ if (patternId.includes("message") || patternId.includes("chat")) syntheticComponents.push("Avatar", "Text", "CodeBlock");
1970
+ if (patternId.includes("input") && desc.includes("chat")) syntheticComponents.push("Textarea", "Button", "Icon");
1971
+ if (patternId.includes("header") && desc.includes("chat")) syntheticComponents.push("Button", "Icon", "Text");
1972
+ if (patternId.includes("content") || patternId.includes("legal")) syntheticComponents.push("Heading", "Text", "List");
1973
+ if (patternId.includes("how-it-works") || patternId.includes("steps")) syntheticComponents.push("Card", "Icon", "Text", "Badge");
1974
+ if (patternId.includes("values")) syntheticComponents.push("Card", "Icon", "Text");
1975
+ if (patternId.includes("story") || patternId.includes("about")) syntheticComponents.push("Text", "Image");
1976
+ if (patternId.includes("empty") || patternId.includes("new")) syntheticComponents.push("Icon", "Text", "Button");
1977
+ return [...new Set(syntheticComponents)];
1978
+ }
1813
1979
  function generateSectionContext(input) {
1814
1980
  const { section, decorators, guardConfig, personality, themeName, recipeName, zoneContext, patternSpecs, recipeHints, constraints, shellInfo } = input;
1815
1981
  const lines = [];
@@ -1837,8 +2003,9 @@ function generateSectionContext(input) {
1837
2003
  lines.push(`**Theme tokens:** see \`src/styles/tokens.css\` \u2014 use \`var(--d-primary)\`, \`var(--d-bg)\`, etc.`);
1838
2004
  lines.push("");
1839
2005
  if (decorators.length > 0) {
1840
- const names = decorators.map((d) => d.name).join(", ");
1841
- lines.push(`**Decorators:** see \`src/styles/decorators.css\` \u2014 available classes: ${names}`);
2006
+ const names = decorators.map((d) => `\`${d.name}\``).join(", ");
2007
+ lines.push(`**Decorators:** ${names} (see \`src/styles/decorators.css\`)`);
2008
+ lines.push("Usage: `className={css('_flex _col') + ' carbon-card'}` \u2014 atoms via css(), decorators as plain class strings.");
1842
2009
  } else {
1843
2010
  lines.push("**Decorators:** none defined.");
1844
2011
  }
@@ -1871,11 +2038,7 @@ function generateSectionContext(input) {
1871
2038
  lines.push("");
1872
2039
  }
1873
2040
  if (personality.length > 0) {
1874
- lines.push("## Personality");
1875
- lines.push("");
1876
- lines.push(personality.join(", "));
1877
- lines.push("");
1878
- lines.push("---");
2041
+ lines.push("**Personality:** See scaffold.md for personality and visual direction.");
1879
2042
  lines.push("");
1880
2043
  }
1881
2044
  if (constraints && Object.keys(constraints).length > 0) {
@@ -1888,6 +2051,34 @@ function generateSectionContext(input) {
1888
2051
  lines.push("---");
1889
2052
  lines.push("");
1890
2053
  }
2054
+ const uniquePatterns = /* @__PURE__ */ new Map();
2055
+ for (const page of section.pages) {
2056
+ const patternNames = page.layout.flatMap(extractPatternNames);
2057
+ for (const name of patternNames) {
2058
+ if (patternSpecs[name] && !uniquePatterns.has(name)) {
2059
+ uniquePatterns.set(name, patternSpecs[name]);
2060
+ }
2061
+ }
2062
+ }
2063
+ if (uniquePatterns.size > 0) {
2064
+ lines.push("## Pattern Reference");
2065
+ lines.push("");
2066
+ for (const [patternName, spec] of uniquePatterns) {
2067
+ lines.push(`### ${patternName}`);
2068
+ lines.push("");
2069
+ lines.push(spec.description);
2070
+ lines.push("");
2071
+ lines.push(`**Components:** ${spec.components.join(", ")}`);
2072
+ lines.push("");
2073
+ lines.push("**Layout slots:**");
2074
+ for (const [slot, desc] of Object.entries(spec.slots)) {
2075
+ lines.push(`- \`${slot}\`: ${desc}`);
2076
+ }
2077
+ lines.push("");
2078
+ }
2079
+ lines.push("---");
2080
+ lines.push("");
2081
+ }
1891
2082
  lines.push("## Pages");
1892
2083
  lines.push("");
1893
2084
  for (const page of section.pages) {
@@ -1908,22 +2099,6 @@ function generateSectionContext(input) {
1908
2099
  }
1909
2100
  lines.push("");
1910
2101
  }
1911
- const patternNames = page.layout.flatMap(extractPatternNames);
1912
- for (const patternName of patternNames) {
1913
- const spec = patternSpecs[patternName];
1914
- if (!spec) continue;
1915
- lines.push(`#### Pattern: ${patternName}`);
1916
- lines.push("");
1917
- lines.push(spec.description);
1918
- lines.push("");
1919
- lines.push(`**Components:** ${spec.components.join(", ")}`);
1920
- lines.push("");
1921
- lines.push("**Layout slots:**");
1922
- for (const [slot, desc] of Object.entries(spec.slots)) {
1923
- lines.push(`- \`${slot}\`: ${desc}`);
1924
- }
1925
- lines.push("");
1926
- }
1927
2102
  }
1928
2103
  return lines.join("\n");
1929
2104
  }