@decantr/cli 1.4.0 → 1.5.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/dist/bin.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import "./chunk-
|
|
3
|
-
import "./chunk-
|
|
2
|
+
import "./chunk-SUNMRG3P.js";
|
|
3
|
+
import "./chunk-6K6ZPDT4.js";
|
|
@@ -120,6 +120,12 @@ function composeSections(composeEntries, archetypeResults, overrides) {
|
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
}
|
|
123
|
+
const primaryShell = sections.find((s) => s.role === "primary")?.shell || defaultShell;
|
|
124
|
+
for (const section of sections) {
|
|
125
|
+
if (section.shell === "inherit") {
|
|
126
|
+
section.shell = primaryShell;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
123
129
|
let features = [...new Set(allFeatures)];
|
|
124
130
|
if (overrides?.features_add) {
|
|
125
131
|
for (const f of overrides.features_add) {
|
|
@@ -351,17 +357,57 @@ function generateDecoratorsCSS(recipeData, themeName) {
|
|
|
351
357
|
`);
|
|
352
358
|
return css.join("\n");
|
|
353
359
|
}
|
|
360
|
+
function generateDecoratorsContext(recipeData, recipeName) {
|
|
361
|
+
const lines = [];
|
|
362
|
+
lines.push(`# Recipe Decorators: ${recipeName}`);
|
|
363
|
+
lines.push("");
|
|
364
|
+
lines.push("## Available Classes");
|
|
365
|
+
lines.push("");
|
|
366
|
+
if (recipeData?.decorators && Object.keys(recipeData.decorators).length > 0) {
|
|
367
|
+
lines.push("| Decorator | Description |");
|
|
368
|
+
lines.push("|-----------|-------------|");
|
|
369
|
+
for (const [name, description] of Object.entries(recipeData.decorators)) {
|
|
370
|
+
lines.push(`| ${name} | ${description} |`);
|
|
371
|
+
}
|
|
372
|
+
} else {
|
|
373
|
+
lines.push("No decorators defined.");
|
|
374
|
+
}
|
|
375
|
+
lines.push("");
|
|
376
|
+
lines.push("## Usage");
|
|
377
|
+
lines.push("");
|
|
378
|
+
lines.push("Decorators are plain CSS class names from `src/styles/decorators.css`. Combine with atoms:");
|
|
379
|
+
lines.push("");
|
|
380
|
+
lines.push("```tsx");
|
|
381
|
+
lines.push("<div className={css('_flex _col _gap4') + ' " + recipeName + "-card'}>");
|
|
382
|
+
lines.push(" <pre className={css('_p3') + ' " + recipeName + "-code'}>{code}</pre>");
|
|
383
|
+
lines.push("</div>");
|
|
384
|
+
lines.push("```");
|
|
385
|
+
lines.push("");
|
|
386
|
+
lines.push("Atoms use `css()` function. Decorators are plain class strings. Combined via string concatenation.");
|
|
387
|
+
lines.push("");
|
|
388
|
+
return lines.join("\n");
|
|
389
|
+
}
|
|
354
390
|
function generateDecoratorRule(name, description) {
|
|
355
391
|
const rules = [];
|
|
356
392
|
const descLower = description.toLowerCase();
|
|
357
|
-
if (descLower.includes("
|
|
393
|
+
if (descLower.includes("monospace") || descLower.includes("mono font")) {
|
|
394
|
+
rules.push("font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, 'DejaVu Sans Mono', monospace");
|
|
395
|
+
}
|
|
396
|
+
if (descLower.includes("surface-raised") || descLower.includes("surface raised")) {
|
|
397
|
+
rules.push("background: var(--d-surface-raised)");
|
|
398
|
+
} else if (descLower.includes("surface background") || descLower.includes("surface elevation")) {
|
|
358
399
|
rules.push("background: var(--d-surface)");
|
|
359
400
|
} else if (descLower.includes("background") && descLower.includes("theme")) {
|
|
360
401
|
rules.push("background: var(--d-bg)");
|
|
361
402
|
} else if (descLower.includes("primary-tinted") || descLower.includes("primary background")) {
|
|
362
403
|
rules.push("background: color-mix(in srgb, var(--d-primary) 15%, var(--d-surface))");
|
|
363
404
|
}
|
|
364
|
-
|
|
405
|
+
const leftBorderMatch = descLower.match(/(\d+)px\s+left\s+border/);
|
|
406
|
+
if (leftBorderMatch) {
|
|
407
|
+
rules.push(`border-left: ${leftBorderMatch[1]}px solid var(--d-primary)`);
|
|
408
|
+
} else if (descLower.includes("left border")) {
|
|
409
|
+
rules.push("border-left: 3px solid var(--d-primary)");
|
|
410
|
+
} else if (descLower.includes("1px border") || descLower.includes("subtle border")) {
|
|
365
411
|
rules.push("border: 1px solid var(--d-border)");
|
|
366
412
|
} else if (descLower.includes("border") && !descLower.includes("radius")) {
|
|
367
413
|
rules.push("border: 1px solid var(--d-border)");
|
|
@@ -397,6 +443,15 @@ function generateDecoratorRule(name, description) {
|
|
|
397
443
|
rules.push("border-radius: var(--d-radius-lg)");
|
|
398
444
|
rules.push("max-width: 80%");
|
|
399
445
|
}
|
|
446
|
+
if (descLower.includes("monospace") || descLower.includes("code")) {
|
|
447
|
+
if (!rules.some((r) => r.startsWith("padding"))) {
|
|
448
|
+
rules.push("padding: 0.75rem 1rem");
|
|
449
|
+
}
|
|
450
|
+
if (!rules.some((r) => r.startsWith("border-radius"))) {
|
|
451
|
+
rules.push("border-radius: var(--d-radius-sm)");
|
|
452
|
+
}
|
|
453
|
+
rules.push("overflow-x: auto");
|
|
454
|
+
}
|
|
400
455
|
if (rules.length === 0) {
|
|
401
456
|
return `/* .${name}: ${description} */`;
|
|
402
457
|
}
|
|
@@ -833,6 +888,32 @@ The \`css()\` function processes atom strings and injects CSS at runtime:
|
|
|
833
888
|
| \`_trans\` | \`transition:all 0.15s ease\` |
|
|
834
889
|
| \`_visible\`, \`_invisible\` | visibility |
|
|
835
890
|
|
|
891
|
+
### Using Recipe Decorators
|
|
892
|
+
|
|
893
|
+
Recipe decorators (from \`src/styles/decorators.css\`) are regular CSS class names, NOT atoms. They are applied directly as class names and combined with atoms using string concatenation:
|
|
894
|
+
|
|
895
|
+
\`\`\`tsx
|
|
896
|
+
// Atoms use css() function, decorators are plain class names
|
|
897
|
+
<div className={css('_flex _col _gap4') + ' carbon-card'}>
|
|
898
|
+
<div className={css('_p4') + ' carbon-glass'}>
|
|
899
|
+
<pre className={css('_p3') + ' carbon-code'}>{code}</pre>
|
|
900
|
+
</div>
|
|
901
|
+
</div>
|
|
902
|
+
\`\`\`
|
|
903
|
+
|
|
904
|
+
**Key difference:**
|
|
905
|
+
- Atoms: \`css('_flex _col _gap4')\` \u2014 processed by @decantr/css runtime
|
|
906
|
+
- Decorators: \`'carbon-card'\`, \`'carbon-glass'\` \u2014 plain CSS classes from decorators.css
|
|
907
|
+
- Combined: \`css('_flex _col') + ' carbon-card'\`
|
|
908
|
+
|
|
909
|
+
### Routing
|
|
910
|
+
|
|
911
|
+
Check \`decantr.essence.json\` \u2192 \`meta.platform.routing\` for the routing strategy:
|
|
912
|
+
- \`"hash"\` \u2192 use \`HashRouter\` (e.g., for static hosting, GitHub Pages)
|
|
913
|
+
- \`"history"\` \u2192 use \`BrowserRouter\` (e.g., for server-rendered apps)
|
|
914
|
+
|
|
915
|
+
Routes are defined in \`decantr.essence.json\` \u2192 \`blueprint.routes\` and listed in \`.decantr/context/scaffold.md\`.
|
|
916
|
+
|
|
836
917
|
### CSS Architecture
|
|
837
918
|
|
|
838
919
|
The CSS is organized into two parts:
|
|
@@ -1065,7 +1146,7 @@ async function scaffoldProject(projectRoot, options, detected, registry, archety
|
|
|
1065
1146
|
}
|
|
1066
1147
|
writeFileSync(essencePath, JSON.stringify(essenceV3, null, 2) + "\n");
|
|
1067
1148
|
}
|
|
1068
|
-
const refreshResult = await refreshDerivedFiles(projectRoot, essenceV3, registry);
|
|
1149
|
+
const refreshResult = await refreshDerivedFiles(projectRoot, essenceV3, registry, themeData, recipeData);
|
|
1069
1150
|
contextFiles.push(...refreshResult.contextFiles);
|
|
1070
1151
|
const gitignoreUpdated = updateGitignore(projectRoot);
|
|
1071
1152
|
return {
|
|
@@ -1267,7 +1348,7 @@ When available, use these tools:
|
|
|
1267
1348
|
gitignoreUpdated
|
|
1268
1349
|
};
|
|
1269
1350
|
}
|
|
1270
|
-
async function refreshDerivedFiles(projectRoot, essence, registry) {
|
|
1351
|
+
async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThemeData, prefetchedRecipeData) {
|
|
1271
1352
|
const decantrDir = join(projectRoot, ".decantr");
|
|
1272
1353
|
const contextDir = join(decantrDir, "context");
|
|
1273
1354
|
mkdirSync(contextDir, { recursive: true });
|
|
@@ -1281,12 +1362,13 @@ async function refreshDerivedFiles(projectRoot, essence, registry) {
|
|
|
1281
1362
|
blueprint_enforcement: essence.meta.guard.blueprint_enforcement || "warn"
|
|
1282
1363
|
};
|
|
1283
1364
|
const personality = essence.dna.personality || [];
|
|
1284
|
-
let themeData;
|
|
1285
|
-
let recipeData;
|
|
1286
|
-
try {
|
|
1365
|
+
let themeData = prefetchedThemeData;
|
|
1366
|
+
let recipeData = prefetchedRecipeData;
|
|
1367
|
+
if (!themeData) try {
|
|
1287
1368
|
const themeResult = await registry.fetchTheme(themeName);
|
|
1288
1369
|
if (themeResult?.data) {
|
|
1289
|
-
const
|
|
1370
|
+
const raw = themeResult.data;
|
|
1371
|
+
const t = raw.data ?? raw;
|
|
1290
1372
|
themeData = {
|
|
1291
1373
|
seed: t.seed,
|
|
1292
1374
|
palette: t.palette,
|
|
@@ -1298,18 +1380,66 @@ async function refreshDerivedFiles(projectRoot, essence, registry) {
|
|
|
1298
1380
|
}
|
|
1299
1381
|
} catch {
|
|
1300
1382
|
}
|
|
1301
|
-
try {
|
|
1383
|
+
if (!recipeData) try {
|
|
1302
1384
|
const recipeResult = await registry.fetchRecipe(recipeName);
|
|
1303
1385
|
if (recipeResult?.data) {
|
|
1304
|
-
const
|
|
1386
|
+
const raw = recipeResult.data;
|
|
1387
|
+
const r = raw.data ?? raw;
|
|
1305
1388
|
recipeData = {
|
|
1306
1389
|
decorators: r.decorators,
|
|
1307
1390
|
spatial_hints: r.spatial_hints,
|
|
1308
1391
|
radius_hints: r.radius_hints
|
|
1309
1392
|
};
|
|
1393
|
+
if (!recipeData.decorators && raw.data) {
|
|
1394
|
+
const inner = raw.data;
|
|
1395
|
+
if (inner.decorators) {
|
|
1396
|
+
recipeData.decorators = inner.decorators;
|
|
1397
|
+
recipeData.spatial_hints = inner.spatial_hints;
|
|
1398
|
+
recipeData.radius_hints = inner.radius_hints;
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1310
1401
|
}
|
|
1311
1402
|
} catch {
|
|
1312
1403
|
}
|
|
1404
|
+
if (!recipeData?.decorators || Object.keys(recipeData.decorators).length === 0) {
|
|
1405
|
+
try {
|
|
1406
|
+
const apiUrl = registry.apiUrl || "https://api.decantr.ai/v1";
|
|
1407
|
+
const resp = await fetch(`${apiUrl}/recipes/@official/${recipeName}`);
|
|
1408
|
+
if (resp.ok) {
|
|
1409
|
+
const apiData = await resp.json();
|
|
1410
|
+
const inner = apiData.data ?? apiData;
|
|
1411
|
+
if (inner.decorators && Object.keys(inner.decorators).length > 0) {
|
|
1412
|
+
recipeData = {
|
|
1413
|
+
decorators: inner.decorators,
|
|
1414
|
+
spatial_hints: inner.spatial_hints,
|
|
1415
|
+
radius_hints: inner.radius_hints
|
|
1416
|
+
};
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
} catch {
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1422
|
+
if (!themeData?.seed?.primary) {
|
|
1423
|
+
try {
|
|
1424
|
+
const apiUrl = registry.apiUrl || "https://api.decantr.ai/v1";
|
|
1425
|
+
const resp = await fetch(`${apiUrl}/themes/@official/${themeName}`);
|
|
1426
|
+
if (resp.ok) {
|
|
1427
|
+
const apiData = await resp.json();
|
|
1428
|
+
const inner = apiData.data ?? apiData;
|
|
1429
|
+
if (inner.seed) {
|
|
1430
|
+
themeData = {
|
|
1431
|
+
seed: inner.seed,
|
|
1432
|
+
palette: inner.palette,
|
|
1433
|
+
cvd_support: inner.cvd_support,
|
|
1434
|
+
tokens: inner.tokens,
|
|
1435
|
+
typography_hints: inner.typography_hints,
|
|
1436
|
+
motion_hints: inner.motion_hints
|
|
1437
|
+
};
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
} catch {
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1313
1443
|
const stylesDir = join(projectRoot, "src", "styles");
|
|
1314
1444
|
mkdirSync(stylesDir, { recursive: true });
|
|
1315
1445
|
const tokensPath = join(stylesDir, "tokens.css");
|
|
@@ -1323,14 +1453,22 @@ async function refreshDerivedFiles(projectRoot, essence, registry) {
|
|
|
1323
1453
|
writeFileSync(decoratorsPath, generateDecoratorsCSS(recipeData, themeName));
|
|
1324
1454
|
}
|
|
1325
1455
|
const cssFiles = [tokensPath, decoratorsPath];
|
|
1456
|
+
const decoratorsMdPath = join(contextDir, "decorators.md");
|
|
1457
|
+
writeFileSync(decoratorsMdPath, generateDecoratorsContext(recipeData, recipeName));
|
|
1326
1458
|
const decantrMdPath = join(projectRoot, "DECANTR.md");
|
|
1327
1459
|
writeFileSync(decantrMdPath, generateDecantrMdV31(guardMode, CSS_APPROACH_CONTENT));
|
|
1328
1460
|
const summaryPath = join(contextDir, "essence-summary.md");
|
|
1329
1461
|
writeFileSync(summaryPath, generateEssenceSummaryV3(essence));
|
|
1330
|
-
const contextFiles = [summaryPath];
|
|
1462
|
+
const contextFiles = [decoratorsMdPath, summaryPath];
|
|
1331
1463
|
const blueprint = essence.blueprint;
|
|
1332
1464
|
const sections = blueprint.sections && blueprint.sections.length > 0 ? blueprint.sections : [];
|
|
1333
1465
|
if (sections.length > 0) {
|
|
1466
|
+
const primarySectionShell = sections.find((s) => s.role === "primary")?.shell || "sidebar-main";
|
|
1467
|
+
for (const section of sections) {
|
|
1468
|
+
if (section.shell === "inherit") {
|
|
1469
|
+
section.shell = primarySectionShell;
|
|
1470
|
+
}
|
|
1471
|
+
}
|
|
1334
1472
|
const patternSpecs = {};
|
|
1335
1473
|
const seenPatterns = /* @__PURE__ */ new Set();
|
|
1336
1474
|
for (const section of sections) {
|
|
@@ -1347,13 +1485,29 @@ async function refreshDerivedFiles(projectRoot, essence, registry) {
|
|
|
1347
1485
|
const inner = raw.data ?? raw;
|
|
1348
1486
|
const defaultPreset = inner.default_preset || "standard";
|
|
1349
1487
|
const preset = inner.presets?.[defaultPreset];
|
|
1488
|
+
let slots = preset?.layout?.slots || {};
|
|
1489
|
+
if (Object.keys(slots).length === 0) {
|
|
1490
|
+
const synthetic = generateSyntheticSlots(name, inner.description || "");
|
|
1491
|
+
if (Object.keys(synthetic).length > 0) {
|
|
1492
|
+
slots = synthetic;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1350
1495
|
patternSpecs[name] = {
|
|
1351
1496
|
description: inner.description || "",
|
|
1352
1497
|
components: inner.components || [],
|
|
1353
|
-
slots
|
|
1498
|
+
slots
|
|
1354
1499
|
};
|
|
1500
|
+
} else {
|
|
1501
|
+
const synthetic = generateSyntheticSlots(name, "");
|
|
1502
|
+
if (Object.keys(synthetic).length > 0) {
|
|
1503
|
+
patternSpecs[name] = { description: "", components: [], slots: synthetic };
|
|
1504
|
+
}
|
|
1355
1505
|
}
|
|
1356
1506
|
} catch {
|
|
1507
|
+
const synthetic = generateSyntheticSlots(name, "");
|
|
1508
|
+
if (Object.keys(synthetic).length > 0) {
|
|
1509
|
+
patternSpecs[name] = { description: "", components: [], slots: synthetic };
|
|
1510
|
+
}
|
|
1357
1511
|
}
|
|
1358
1512
|
}
|
|
1359
1513
|
}
|
|
@@ -1423,8 +1577,17 @@ async function refreshDerivedFiles(projectRoot, essence, registry) {
|
|
|
1423
1577
|
}
|
|
1424
1578
|
}
|
|
1425
1579
|
for (const section of sections) {
|
|
1426
|
-
const zoneLabel = section.role === "primary" || section.role === "auxiliary" ? "App" : section.role
|
|
1427
|
-
|
|
1580
|
+
const zoneLabel = section.role === "primary" || section.role === "auxiliary" ? "App" : section.role === "gateway" ? "Gateway" : "Public";
|
|
1581
|
+
let zoneContext = `**Zone:** ${zoneLabel} (${section.role}) \u2014 ${section.shell} shell`;
|
|
1582
|
+
if (section.role === "gateway") {
|
|
1583
|
+
zoneContext += "\nAuth success \u2192 enters App zone. Sign out returns here.";
|
|
1584
|
+
} else if (section.role === "primary") {
|
|
1585
|
+
zoneContext += "\nAuthenticated users land here. Sign out \u2192 Gateway (/login).";
|
|
1586
|
+
} else if (section.role === "public") {
|
|
1587
|
+
zoneContext += "\nAnonymous visitors. CTAs lead to Gateway (/login, /register).";
|
|
1588
|
+
} else if (section.role === "auxiliary") {
|
|
1589
|
+
zoneContext += "\nSupporting section within App zone. Shares navigation with primary.";
|
|
1590
|
+
}
|
|
1428
1591
|
const sectionPatterns = {};
|
|
1429
1592
|
for (const page of section.pages) {
|
|
1430
1593
|
for (const item of page.layout) {
|
|
@@ -1496,13 +1659,29 @@ async function refreshDerivedFiles(projectRoot, essence, registry) {
|
|
|
1496
1659
|
const inner = raw.data ?? raw;
|
|
1497
1660
|
const defaultPreset = inner.default_preset || "standard";
|
|
1498
1661
|
const preset = inner.presets?.[defaultPreset];
|
|
1662
|
+
let slots = preset?.layout?.slots || {};
|
|
1663
|
+
if (Object.keys(slots).length === 0) {
|
|
1664
|
+
const synthetic = generateSyntheticSlots(name, inner.description || "");
|
|
1665
|
+
if (Object.keys(synthetic).length > 0) {
|
|
1666
|
+
slots = synthetic;
|
|
1667
|
+
}
|
|
1668
|
+
}
|
|
1499
1669
|
patternSpecs[name] = {
|
|
1500
1670
|
description: inner.description || "",
|
|
1501
1671
|
components: inner.components || [],
|
|
1502
|
-
slots
|
|
1672
|
+
slots
|
|
1503
1673
|
};
|
|
1674
|
+
} else {
|
|
1675
|
+
const synthetic = generateSyntheticSlots(name, "");
|
|
1676
|
+
if (Object.keys(synthetic).length > 0) {
|
|
1677
|
+
patternSpecs[name] = { description: "", components: [], slots: synthetic };
|
|
1678
|
+
}
|
|
1504
1679
|
}
|
|
1505
1680
|
} catch {
|
|
1681
|
+
const synthetic = generateSyntheticSlots(name, "");
|
|
1682
|
+
if (Object.keys(synthetic).length > 0) {
|
|
1683
|
+
patternSpecs[name] = { description: "", components: [], slots: synthetic };
|
|
1684
|
+
}
|
|
1506
1685
|
}
|
|
1507
1686
|
}
|
|
1508
1687
|
}
|
|
@@ -1565,8 +1744,74 @@ async function refreshDerivedFiles(projectRoot, essence, registry) {
|
|
|
1565
1744
|
cssFiles
|
|
1566
1745
|
};
|
|
1567
1746
|
}
|
|
1747
|
+
function generateSyntheticSlots(patternId, description) {
|
|
1748
|
+
const desc = description.toLowerCase();
|
|
1749
|
+
const syntheticSlots = {};
|
|
1750
|
+
if (patternId.includes("feature") || desc.includes("feature")) {
|
|
1751
|
+
syntheticSlots["grid"] = "Grid of feature cards (icon + title + description)";
|
|
1752
|
+
syntheticSlots["feature-card"] = "Individual feature with icon, heading, and description text";
|
|
1753
|
+
}
|
|
1754
|
+
if (patternId.includes("pricing") || desc.includes("pricing")) {
|
|
1755
|
+
syntheticSlots["tiers"] = "Pricing tier cards (name, price, features list, CTA button)";
|
|
1756
|
+
syntheticSlots["toggle"] = "Monthly/annual billing toggle (optional)";
|
|
1757
|
+
}
|
|
1758
|
+
if (patternId.includes("testimonial") || desc.includes("testimonial")) {
|
|
1759
|
+
syntheticSlots["quotes"] = "Testimonial cards (quote text, author name, role, avatar)";
|
|
1760
|
+
}
|
|
1761
|
+
if (patternId.includes("cta") || desc.includes("call-to-action") || desc.includes("call to action")) {
|
|
1762
|
+
syntheticSlots["headline"] = "CTA headline text";
|
|
1763
|
+
syntheticSlots["description"] = "Supporting description text";
|
|
1764
|
+
syntheticSlots["actions"] = "CTA button(s)";
|
|
1765
|
+
}
|
|
1766
|
+
if (patternId.includes("how-it-works") || desc.includes("how it works") || desc.includes("timeline") || desc.includes("steps")) {
|
|
1767
|
+
syntheticSlots["steps"] = "Numbered steps (step number, title, description)";
|
|
1768
|
+
}
|
|
1769
|
+
if (patternId.includes("team") || desc.includes("team")) {
|
|
1770
|
+
syntheticSlots["members"] = "Team member cards (avatar, name, role)";
|
|
1771
|
+
}
|
|
1772
|
+
if (patternId.includes("story") || desc.includes("story") || desc.includes("about")) {
|
|
1773
|
+
syntheticSlots["content"] = "Story/about narrative text content";
|
|
1774
|
+
}
|
|
1775
|
+
if (patternId.includes("values") || desc.includes("values")) {
|
|
1776
|
+
syntheticSlots["values"] = "Value cards (icon/emoji, title, description)";
|
|
1777
|
+
}
|
|
1778
|
+
if (patternId.includes("form") || desc.includes("form") || desc.includes("contact")) {
|
|
1779
|
+
syntheticSlots["fields"] = "Form fields (name, email, message, etc.)";
|
|
1780
|
+
syntheticSlots["submit"] = "Submit button";
|
|
1781
|
+
}
|
|
1782
|
+
if (patternId.includes("content") || desc.includes("legal") || desc.includes("privacy") || desc.includes("policy")) {
|
|
1783
|
+
syntheticSlots["body"] = "Long-form text content with headings and paragraphs";
|
|
1784
|
+
syntheticSlots["toc"] = "Table of contents sidebar (optional)";
|
|
1785
|
+
}
|
|
1786
|
+
if (patternId.includes("settings") || desc.includes("settings") || desc.includes("preferences")) {
|
|
1787
|
+
syntheticSlots["sections"] = "Settings sections (label, description, input/toggle)";
|
|
1788
|
+
}
|
|
1789
|
+
if (patternId.includes("security") || desc.includes("security") || desc.includes("password")) {
|
|
1790
|
+
syntheticSlots["sections"] = "Security sections (password change, MFA toggle, session list)";
|
|
1791
|
+
}
|
|
1792
|
+
if (patternId.includes("session") || desc.includes("session")) {
|
|
1793
|
+
syntheticSlots["list"] = "Active sessions list (device, location, last active, revoke button)";
|
|
1794
|
+
}
|
|
1795
|
+
if (patternId.includes("message") || desc.includes("message") || desc.includes("chat")) {
|
|
1796
|
+
syntheticSlots["messages"] = "Message bubbles (user/assistant, content, timestamp)";
|
|
1797
|
+
}
|
|
1798
|
+
if (patternId.includes("input") && desc.includes("chat")) {
|
|
1799
|
+
syntheticSlots["textarea"] = "Auto-expanding message input";
|
|
1800
|
+
syntheticSlots["actions"] = "Attach file button, send button";
|
|
1801
|
+
}
|
|
1802
|
+
if (patternId.includes("empty") || desc.includes("empty")) {
|
|
1803
|
+
syntheticSlots["illustration"] = "Empty state illustration or icon";
|
|
1804
|
+
syntheticSlots["message"] = "Welcome/empty state message";
|
|
1805
|
+
syntheticSlots["suggestions"] = "Suggested actions or prompts";
|
|
1806
|
+
}
|
|
1807
|
+
if (patternId.includes("header") && desc.includes("chat")) {
|
|
1808
|
+
syntheticSlots["title"] = "Conversation title or model name";
|
|
1809
|
+
syntheticSlots["actions"] = "Header action buttons (new chat, settings)";
|
|
1810
|
+
}
|
|
1811
|
+
return syntheticSlots;
|
|
1812
|
+
}
|
|
1568
1813
|
function generateSectionContext(input) {
|
|
1569
|
-
const { section,
|
|
1814
|
+
const { section, decorators, guardConfig, personality, themeName, recipeName, zoneContext, patternSpecs, recipeHints, constraints, shellInfo } = input;
|
|
1570
1815
|
const lines = [];
|
|
1571
1816
|
lines.push(`# Section: ${section.id}`);
|
|
1572
1817
|
lines.push("");
|
|
@@ -1587,40 +1832,15 @@ function generateSectionContext(input) {
|
|
|
1587
1832
|
lines.push("");
|
|
1588
1833
|
lines.push("---");
|
|
1589
1834
|
lines.push("");
|
|
1590
|
-
lines.push(
|
|
1591
|
-
lines.push("");
|
|
1592
|
-
lines.push(`| Rule | Scope | Severity | Description |`);
|
|
1593
|
-
lines.push(`|------|-------|----------|-------------|`);
|
|
1594
|
-
lines.push(`| Style guard | DNA | ${guardConfig.dna_enforcement} | Code must use the ${themeName} theme |`);
|
|
1595
|
-
lines.push(`| Recipe guard | DNA | ${guardConfig.dna_enforcement} | Visual recipe must match ${recipeName} |`);
|
|
1596
|
-
lines.push(`| Density guard | DNA | ${guardConfig.dna_enforcement} | Content gap must match essence density |`);
|
|
1597
|
-
lines.push(`| Accessibility guard | DNA | ${guardConfig.dna_enforcement} | Must meet WCAG level from essence |`);
|
|
1598
|
-
lines.push(`| Structure guard | Blueprint | ${guardConfig.blueprint_enforcement} | Pages must exist in essence structure |`);
|
|
1599
|
-
lines.push(`| Layout guard | Blueprint | ${guardConfig.blueprint_enforcement} | Pattern order must match essence layout |`);
|
|
1600
|
-
lines.push(`| Pattern existence | Blueprint | ${guardConfig.blueprint_enforcement} | All patterns must exist in registry |`);
|
|
1601
|
-
lines.push("");
|
|
1602
|
-
lines.push(`**Guard mode:** ${guardConfig.mode}`);
|
|
1603
|
-
lines.push("");
|
|
1604
|
-
lines.push("---");
|
|
1605
|
-
lines.push("");
|
|
1606
|
-
lines.push(`## Theme: ${themeName}`);
|
|
1607
|
-
lines.push("");
|
|
1608
|
-
lines.push("```css");
|
|
1609
|
-
lines.push(themeTokens);
|
|
1610
|
-
lines.push("```");
|
|
1611
|
-
lines.push("");
|
|
1612
|
-
lines.push("---");
|
|
1835
|
+
lines.push(`**Guard:** ${guardConfig.mode} mode | DNA violations = ${guardConfig.dna_enforcement} | Blueprint violations = ${guardConfig.blueprint_enforcement}`);
|
|
1613
1836
|
lines.push("");
|
|
1614
|
-
lines.push(
|
|
1837
|
+
lines.push(`**Theme tokens:** see \`src/styles/tokens.css\` \u2014 use \`var(--d-primary)\`, \`var(--d-bg)\`, etc.`);
|
|
1615
1838
|
lines.push("");
|
|
1616
1839
|
if (decorators.length > 0) {
|
|
1617
|
-
|
|
1618
|
-
lines.push(
|
|
1619
|
-
for (const dec of decorators) {
|
|
1620
|
-
lines.push(`| ${dec.name} | ${dec.description} |`);
|
|
1621
|
-
}
|
|
1840
|
+
const names = decorators.map((d) => d.name).join(", ");
|
|
1841
|
+
lines.push(`**Decorators:** see \`src/styles/decorators.css\` \u2014 available classes: ${names}`);
|
|
1622
1842
|
} else {
|
|
1623
|
-
lines.push("
|
|
1843
|
+
lines.push("**Decorators:** none defined.");
|
|
1624
1844
|
}
|
|
1625
1845
|
lines.push("");
|
|
1626
1846
|
if (recipeHints) {
|
|
@@ -1638,11 +1858,8 @@ function generateSectionContext(input) {
|
|
|
1638
1858
|
lines.push("---");
|
|
1639
1859
|
lines.push("");
|
|
1640
1860
|
if (zoneContext) {
|
|
1641
|
-
lines.push("## Zone Context");
|
|
1642
|
-
lines.push("");
|
|
1643
1861
|
lines.push(zoneContext);
|
|
1644
|
-
lines.push("");
|
|
1645
|
-
lines.push("---");
|
|
1862
|
+
lines.push("For full app topology, see `.decantr/context/scaffold.md`");
|
|
1646
1863
|
lines.push("");
|
|
1647
1864
|
}
|
|
1648
1865
|
if (section.features.length > 0) {
|
|
@@ -1925,12 +2142,22 @@ var RegistryClient = class {
|
|
|
1925
2142
|
if (customResult) return customResult;
|
|
1926
2143
|
if (id.startsWith("custom:")) return null;
|
|
1927
2144
|
if (!this.offline) {
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
2145
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
2146
|
+
try {
|
|
2147
|
+
const data = await this.apiClient.getContent(contentType, namespace, id);
|
|
2148
|
+
saveToCache(this.cacheDir, contentType, id, data, namespace);
|
|
2149
|
+
return { data, source: { type: "api", url: this.apiUrl } };
|
|
2150
|
+
} catch (e) {
|
|
2151
|
+
if (process.env.DECANTR_DEBUG) {
|
|
2152
|
+
console.error(` [debug] API fetch ${attempt === 0 ? "failed" : "retry failed"} for ${contentType}/${namespace}/${id}: ${e.message}`);
|
|
2153
|
+
}
|
|
2154
|
+
if (attempt === 0) {
|
|
2155
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
2156
|
+
}
|
|
2157
|
+
}
|
|
1933
2158
|
}
|
|
2159
|
+
} else if (process.env.DECANTR_DEBUG) {
|
|
2160
|
+
console.error(` [debug] Skipping API (offline mode) for ${contentType}/${namespace}/${id}`);
|
|
1934
2161
|
}
|
|
1935
2162
|
return loadFromCache(this.cacheDir, contentType, id, namespace);
|
|
1936
2163
|
}
|
|
@@ -1992,9 +2219,12 @@ async function syncRegistry(cacheDir, apiUrl = DEFAULT_API_URL) {
|
|
|
1992
2219
|
const result = await apiClient.listContent(type, { namespace: "@official" });
|
|
1993
2220
|
saveToCache(cacheDir, type, null, result, "@official");
|
|
1994
2221
|
for (const item of result.items) {
|
|
1995
|
-
const
|
|
1996
|
-
|
|
1997
|
-
|
|
2222
|
+
const slug = item.slug;
|
|
2223
|
+
const data = item.data;
|
|
2224
|
+
const innerSlug = data?.id || data?.slug;
|
|
2225
|
+
const cacheKey = slug || innerSlug || item.id;
|
|
2226
|
+
if (cacheKey) {
|
|
2227
|
+
saveToCache(cacheDir, type, cacheKey, item, "@official");
|
|
1998
2228
|
}
|
|
1999
2229
|
}
|
|
2000
2230
|
synced.push(type);
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
scaffoldMinimal,
|
|
10
10
|
scaffoldProject,
|
|
11
11
|
syncRegistry
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-6K6ZPDT4.js";
|
|
13
13
|
|
|
14
14
|
// src/index.ts
|
|
15
15
|
import { readFileSync as readFileSync14, existsSync as existsSync19, readdirSync as readdirSync6 } from "fs";
|
|
@@ -2578,11 +2578,9 @@ ${YELLOW6}You're offline. Scaffolding Decantr default.${RESET9}`);
|
|
|
2578
2578
|
);
|
|
2579
2579
|
selectedBlueprint = selected || "default";
|
|
2580
2580
|
}
|
|
2581
|
-
const
|
|
2582
|
-
|
|
2583
|
-
|
|
2584
|
-
registryClient.fetchThemes()
|
|
2585
|
-
]);
|
|
2581
|
+
const archetypesResult = await registryClient.fetchArchetypes();
|
|
2582
|
+
const blueprintsResult = await registryClient.fetchBlueprints();
|
|
2583
|
+
const themesResult = await registryClient.fetchThemes();
|
|
2586
2584
|
if (archetypesResult.source.type === "api") {
|
|
2587
2585
|
registrySource = "api";
|
|
2588
2586
|
}
|
|
@@ -2626,15 +2624,14 @@ ${YELLOW6}You're offline. Scaffolding Decantr default.${RESET9}`);
|
|
|
2626
2624
|
blueprintRecipeName = blueprint.theme?.recipe;
|
|
2627
2625
|
if (blueprint.compose && blueprint.compose.length > 0) {
|
|
2628
2626
|
const entries = blueprint.compose;
|
|
2629
|
-
const
|
|
2627
|
+
const results = [];
|
|
2628
|
+
for (const entry of entries) {
|
|
2630
2629
|
const id = typeof entry === "string" ? entry : entry.archetype;
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
});
|
|
2637
|
-
const results = await Promise.all(fetchPromises);
|
|
2630
|
+
const r = await registryClient.fetchArchetype(id);
|
|
2631
|
+
const raw = r?.data;
|
|
2632
|
+
const inner = raw?.data ?? raw;
|
|
2633
|
+
results.push([id, inner]);
|
|
2634
|
+
}
|
|
2638
2635
|
const archetypeMap = new Map(results.map(([id, data]) => [id, data || null]));
|
|
2639
2636
|
const composed = composeArchetypes(entries, archetypeMap);
|
|
2640
2637
|
const primaryId = typeof entries[0] === "string" ? entries[0] : entries[0].archetype;
|
|
@@ -2677,8 +2674,7 @@ ${YELLOW6}You're offline. Scaffolding Decantr default.${RESET9}`);
|
|
|
2677
2674
|
}
|
|
2678
2675
|
patternSpecs = {};
|
|
2679
2676
|
if (allPatternIds.size > 0) {
|
|
2680
|
-
const
|
|
2681
|
-
const fetches = [...allPatternIds].map(async (pid) => {
|
|
2677
|
+
for (const pid of allPatternIds) {
|
|
2682
2678
|
try {
|
|
2683
2679
|
const result2 = await registryClient.fetchPattern(pid);
|
|
2684
2680
|
if (result2) {
|
|
@@ -2686,17 +2682,15 @@ ${YELLOW6}You're offline. Scaffolding Decantr default.${RESET9}`);
|
|
|
2686
2682
|
const inner = raw.data ?? raw;
|
|
2687
2683
|
const defaultPreset = inner.default_preset || "standard";
|
|
2688
2684
|
const preset = inner.presets?.[defaultPreset];
|
|
2689
|
-
|
|
2685
|
+
patternSpecs[pid] = {
|
|
2690
2686
|
description: inner.description || "",
|
|
2691
2687
|
components: inner.components || [],
|
|
2692
|
-
slots: preset?.layout?.slots || {}
|
|
2693
|
-
code: preset?.code?.example || inner.code?.example || ""
|
|
2688
|
+
slots: preset?.layout?.slots || {}
|
|
2694
2689
|
};
|
|
2695
2690
|
}
|
|
2696
2691
|
} catch {
|
|
2697
2692
|
}
|
|
2698
|
-
}
|
|
2699
|
-
await Promise.all(fetches);
|
|
2693
|
+
}
|
|
2700
2694
|
}
|
|
2701
2695
|
const zoneInputs = [];
|
|
2702
2696
|
for (const entry of entries) {
|
|
@@ -2766,6 +2760,10 @@ ${YELLOW6}You're offline. Scaffolding Decantr default.${RESET9}`);
|
|
|
2766
2760
|
}
|
|
2767
2761
|
const recipeName = blueprintRecipeName || options.theme;
|
|
2768
2762
|
const recipeResult = await registryClient.fetchRecipe(recipeName);
|
|
2763
|
+
if (process.env.DECANTR_DEBUG && recipeResult) {
|
|
2764
|
+
const dbg = recipeResult.data;
|
|
2765
|
+
console.error(` [debug] recipe source: ${recipeResult.source.type}, keys: ${Object.keys(dbg).join(",")}, has .data: ${"data" in dbg}, has .decorators: ${"decorators" in dbg}, inner.decorators: ${!!dbg.data?.decorators}`);
|
|
2766
|
+
}
|
|
2769
2767
|
if (recipeResult) {
|
|
2770
2768
|
const rawRecipe = recipeResult.data;
|
|
2771
2769
|
const recipe = rawRecipe.data ?? rawRecipe;
|
|
@@ -2816,18 +2814,21 @@ ${YELLOW6}You're offline. Scaffolding Decantr default.${RESET9}`);
|
|
|
2816
2814
|
console.log(` ${cyan("decantr migrate")} Migrate v2 essence to v3`);
|
|
2817
2815
|
const essenceContent = readFileSync14(result.essencePath, "utf-8");
|
|
2818
2816
|
const essence = JSON.parse(essenceContent);
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2817
|
+
if (essence.version !== "3.1.0") {
|
|
2818
|
+
const validation = validateEssence2(essence);
|
|
2819
|
+
if (!validation.valid) {
|
|
2820
|
+
console.log(error(`
|
|
2822
2821
|
Validation warnings: ${validation.errors.join(", ")}`));
|
|
2822
|
+
}
|
|
2823
2823
|
}
|
|
2824
2824
|
console.log("");
|
|
2825
2825
|
let promptPages;
|
|
2826
2826
|
if (isV36(essence)) {
|
|
2827
|
-
|
|
2827
|
+
const allPages = essence.blueprint.sections ? essence.blueprint.sections.flatMap((s) => s.pages.map((p) => ({ ...p, _shell: s.shell }))) : essence.blueprint.pages || [];
|
|
2828
|
+
promptPages = allPages.map((p) => ({
|
|
2828
2829
|
id: p.id,
|
|
2829
|
-
shell: p.shell_override ?? essence.blueprint.shell,
|
|
2830
|
-
layout: p.layout.map((item) => typeof item === "string" ? item : extractPatternName(item))
|
|
2830
|
+
shell: p.shell_override ?? p._shell ?? essence.blueprint.shell,
|
|
2831
|
+
layout: (p.layout || []).map((item) => typeof item === "string" ? item : extractPatternName(item))
|
|
2831
2832
|
}));
|
|
2832
2833
|
} else {
|
|
2833
2834
|
promptPages = essence.structure || [{ id: "home", shell: options.shell, layout: ["hero"] }];
|
|
@@ -3241,7 +3242,7 @@ async function main() {
|
|
|
3241
3242
|
break;
|
|
3242
3243
|
}
|
|
3243
3244
|
case "upgrade": {
|
|
3244
|
-
const { cmdUpgrade } = await import("./upgrade-
|
|
3245
|
+
const { cmdUpgrade } = await import("./upgrade-I2RUTNAT.js");
|
|
3245
3246
|
const applyFlag = args.includes("--apply");
|
|
3246
3247
|
await cmdUpgrade(process.cwd(), { apply: applyFlag });
|
|
3247
3248
|
break;
|
|
@@ -3496,5 +3497,6 @@ async function main() {
|
|
|
3496
3497
|
}
|
|
3497
3498
|
main().catch((e) => {
|
|
3498
3499
|
console.error(error(e.message));
|
|
3500
|
+
if (e.stack) console.error(e.stack);
|
|
3499
3501
|
process.exitCode = 1;
|
|
3500
3502
|
});
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import "./chunk-
|
|
2
|
-
import "./chunk-
|
|
1
|
+
import "./chunk-SUNMRG3P.js";
|
|
2
|
+
import "./chunk-6K6ZPDT4.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decantr/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "Decantr CLI — search the registry, validate essence files, and access design intelligence from the terminal",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"access": "public"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@decantr/essence-spec": "1.0.0-beta.
|
|
27
|
-
"@decantr/registry": "1.0.0-beta.
|
|
26
|
+
"@decantr/essence-spec": "1.0.0-beta.8",
|
|
27
|
+
"@decantr/registry": "1.0.0-beta.8"
|
|
28
28
|
},
|
|
29
29
|
"scripts": {
|
|
30
30
|
"build": "tsup",
|