@decantr/cli 1.7.1 → 1.7.5
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/README.md +84 -0
- package/dist/bin.js +3 -2
- package/dist/{chunk-X4RHL7WQ.js → chunk-H4H3IQJK.js} +616 -123
- package/dist/{chunk-4KL2DCYC.js → chunk-KAEQTVAM.js} +1249 -360
- package/dist/chunk-KUDAVJOR.js +46 -0
- package/dist/{heal-VYEGIUAS.js → heal-MURR3RVQ.js} +7 -3
- package/dist/index.js +3 -2
- package/dist/{upgrade-2KX7BKEK.js → upgrade-XNUAON3G.js} +9 -13
- package/package.json +15 -9
- package/src/templates/DECANTR.md.template +31 -3
- package/src/templates/essence-summary.md.template +1 -2
- package/src/templates/task-add-page.md.template +3 -3
- package/src/templates/task-modify.md.template +7 -7
- package/LICENSE +0 -21
|
@@ -3,6 +3,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync, appendFileSync } fr
|
|
|
3
3
|
import { join, dirname } from "path";
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
5
5
|
import { computeSpatialTokens } from "@decantr/essence-spec";
|
|
6
|
+
import { compileExecutionPackBundle } from "@decantr/core";
|
|
6
7
|
|
|
7
8
|
// src/treatments.ts
|
|
8
9
|
function generateTreatmentCSS(spatialTokens, treatmentOverrides, themeDecorators, themeName) {
|
|
@@ -49,7 +50,7 @@ ${themeBody}
|
|
|
49
50
|
["display", "inline-flex"],
|
|
50
51
|
["align-items", "center"],
|
|
51
52
|
["gap", "0.5em"],
|
|
52
|
-
["padding", "var(--d-interactive-py) var(--d-interactive-px)"],
|
|
53
|
+
["padding", "calc(var(--d-interactive-py) * var(--d-density-scale, 1)) var(--d-interactive-px)"],
|
|
53
54
|
["border", "1px solid var(--d-border)"],
|
|
54
55
|
["border-radius", "var(--d-radius)"],
|
|
55
56
|
["background", "transparent"],
|
|
@@ -98,7 +99,7 @@ ${themeBody}
|
|
|
98
99
|
["border", "1px solid var(--d-border)"],
|
|
99
100
|
["border-radius", "var(--d-radius)"],
|
|
100
101
|
["box-shadow", "var(--d-shadow)"],
|
|
101
|
-
["padding", "var(--d-surface-p)"]
|
|
102
|
+
["padding", "calc(var(--d-surface-p) * var(--d-density-scale, 1))"]
|
|
102
103
|
]);
|
|
103
104
|
emitRule('.d-surface[data-elevation="raised"]', [
|
|
104
105
|
["background", "var(--d-surface-raised)"],
|
|
@@ -124,7 +125,7 @@ ${themeBody}
|
|
|
124
125
|
["font-size", "0.875rem"]
|
|
125
126
|
]);
|
|
126
127
|
emitRule(".d-data-header", [
|
|
127
|
-
["padding", "var(--d-data-py) var(--d-content-gap)"],
|
|
128
|
+
["padding", "calc(var(--d-data-py) * var(--d-density-scale, 1)) var(--d-content-gap)"],
|
|
128
129
|
["font-weight", "500"],
|
|
129
130
|
["color", "var(--d-text-muted)"],
|
|
130
131
|
["border-bottom", "1px solid var(--d-border)"],
|
|
@@ -140,13 +141,13 @@ ${themeBody}
|
|
|
140
141
|
["background", "var(--d-surface)"]
|
|
141
142
|
]);
|
|
142
143
|
emitRule(".d-data-cell", [
|
|
143
|
-
["padding", "var(--d-data-py) var(--d-content-gap)"],
|
|
144
|
+
["padding", "calc(var(--d-data-py) * var(--d-density-scale, 1)) var(--d-content-gap)"],
|
|
144
145
|
["vertical-align", "middle"]
|
|
145
146
|
]);
|
|
146
147
|
emitRule(".d-control", [
|
|
147
148
|
["background", "var(--d-surface)"],
|
|
148
149
|
["color", "var(--d-text)"],
|
|
149
|
-
["padding", "var(--d-control-py) 0.75rem"],
|
|
150
|
+
["padding", "calc(var(--d-control-py) * var(--d-density-scale, 1)) 0.75rem"],
|
|
150
151
|
["border-radius", "var(--d-radius)"],
|
|
151
152
|
["border", "1px solid var(--d-border)"],
|
|
152
153
|
["width", "100%"],
|
|
@@ -170,16 +171,21 @@ ${themeBody}
|
|
|
170
171
|
["box-shadow", "0 0 0 3px color-mix(in srgb, var(--d-error) 15%, transparent)"]
|
|
171
172
|
]);
|
|
172
173
|
emitRule(".d-section", [
|
|
173
|
-
["
|
|
174
|
+
["--d-density-scale", "1"],
|
|
175
|
+
["padding", "calc(var(--d-section-py) * var(--d-density-scale)) 0"]
|
|
174
176
|
]);
|
|
177
|
+
lines.push('.d-section[data-density="compact"] {');
|
|
178
|
+
lines.push(" --d-density-scale: 0.65;");
|
|
179
|
+
lines.push("}");
|
|
180
|
+
lines.push("");
|
|
181
|
+
lines.push('.d-section[data-density="spacious"] {');
|
|
182
|
+
lines.push(" --d-density-scale: 1.4;");
|
|
183
|
+
lines.push("}");
|
|
184
|
+
lines.push("");
|
|
175
185
|
lines.push(".d-section + .d-section {");
|
|
176
186
|
lines.push(" border-top: 1px solid transparent;");
|
|
177
187
|
lines.push(" border-image: linear-gradient(to right, transparent, var(--d-border), transparent) 1;");
|
|
178
|
-
lines.push(" margin-top: var(--d-gap-
|
|
179
|
-
lines.push("}");
|
|
180
|
-
lines.push("");
|
|
181
|
-
lines.push('.d-section[data-density="compact"] {');
|
|
182
|
-
lines.push(" padding: calc(var(--d-section-py) * 0.5) 0;");
|
|
188
|
+
lines.push(" margin-top: calc(var(--d-section-gap) * var(--d-density-scale, 1));");
|
|
183
189
|
lines.push("}");
|
|
184
190
|
lines.push("");
|
|
185
191
|
emitRule(".d-annotation", [
|
|
@@ -189,6 +195,7 @@ ${themeBody}
|
|
|
189
195
|
["font-size", "0.75rem"],
|
|
190
196
|
["font-weight", "500"],
|
|
191
197
|
["padding", "0.125rem 0.5rem"],
|
|
198
|
+
["margin-top", "calc(var(--d-annotation-mt) * var(--d-density-scale, 1))"],
|
|
192
199
|
["border-radius", "var(--d-radius-full)"],
|
|
193
200
|
["background", "var(--d-surface)"],
|
|
194
201
|
["color", "var(--d-text-muted)"],
|
|
@@ -216,7 +223,13 @@ ${themeBody}
|
|
|
216
223
|
["text-transform", "uppercase"],
|
|
217
224
|
["letter-spacing", "0.08em"],
|
|
218
225
|
["color", "var(--d-text-muted)"],
|
|
219
|
-
["font-family", "var(--d-font-mono, ui-monospace, monospace)"]
|
|
226
|
+
["font-family", "var(--d-font-mono, ui-monospace, monospace)"],
|
|
227
|
+
["display", "block"],
|
|
228
|
+
["margin-bottom", "calc(var(--d-label-mb) * var(--d-density-scale, 1))"]
|
|
229
|
+
]);
|
|
230
|
+
emitRule(".d-label[data-anchor]", [
|
|
231
|
+
["padding-left", "var(--d-label-px)"],
|
|
232
|
+
["border-left", "2px solid var(--d-accent)"]
|
|
220
233
|
]);
|
|
221
234
|
if (themeOverrideRules.length > 0) {
|
|
222
235
|
lines.push("/* \u2500\u2500 Theme-scoped Treatment Overrides \u2500\u2500 */");
|
|
@@ -275,7 +288,62 @@ function generatePersonalityCSS(personality, themeData) {
|
|
|
275
288
|
}
|
|
276
289
|
|
|
277
290
|
// src/scaffold.ts
|
|
291
|
+
import { API_CONTENT_TYPES } from "@decantr/registry";
|
|
278
292
|
var __dirname = dirname(fileURLToPath(import.meta.url));
|
|
293
|
+
function getPlatformMeta(target) {
|
|
294
|
+
const normalized = (target || "react").toLowerCase();
|
|
295
|
+
const routing = normalized === "nextjs" ? "pathname" : "hash";
|
|
296
|
+
return {
|
|
297
|
+
type: "spa",
|
|
298
|
+
routing
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
function getLegacyBlueprintId(meta) {
|
|
302
|
+
return meta.blueprint;
|
|
303
|
+
}
|
|
304
|
+
function isRecord(value) {
|
|
305
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
306
|
+
}
|
|
307
|
+
function getStringArray(value) {
|
|
308
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
309
|
+
}
|
|
310
|
+
function toPatternReferenceObject(ref) {
|
|
311
|
+
return typeof ref === "string" ? { pattern: ref } : ref;
|
|
312
|
+
}
|
|
313
|
+
function collectPatternIdsFromValue(value, ids) {
|
|
314
|
+
if (typeof value === "string") {
|
|
315
|
+
ids.add(value);
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
if (!value || typeof value !== "object") return;
|
|
319
|
+
if ("pattern" in value && typeof value.pattern === "string") {
|
|
320
|
+
ids.add(value.pattern);
|
|
321
|
+
}
|
|
322
|
+
if ("cols" in value && Array.isArray(value.cols)) {
|
|
323
|
+
for (const col of value.cols) collectPatternIdsFromValue(col, ids);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
function collectPatternIdsFromItems(items) {
|
|
327
|
+
const ids = /* @__PURE__ */ new Set();
|
|
328
|
+
for (const item of items) collectPatternIdsFromValue(item, ids);
|
|
329
|
+
return [...ids];
|
|
330
|
+
}
|
|
331
|
+
function mapRegistryArchetypeToArchetypeData(archetype) {
|
|
332
|
+
return {
|
|
333
|
+
id: archetype.id,
|
|
334
|
+
name: archetype.name,
|
|
335
|
+
role: archetype.role,
|
|
336
|
+
description: archetype.description,
|
|
337
|
+
pages: archetype.pages?.map((page) => ({
|
|
338
|
+
id: page.id,
|
|
339
|
+
shell: page.shell,
|
|
340
|
+
default_layout: page.default_layout?.length ? page.default_layout : ["hero"],
|
|
341
|
+
patterns: page.patterns?.map(toPatternReferenceObject)
|
|
342
|
+
})),
|
|
343
|
+
features: archetype.features,
|
|
344
|
+
seo_hints: archetype.seo_hints
|
|
345
|
+
};
|
|
346
|
+
}
|
|
279
347
|
function composeArchetypes(composeEntries, archetypeResults) {
|
|
280
348
|
if (composeEntries.length === 0) {
|
|
281
349
|
return {
|
|
@@ -298,7 +366,7 @@ function composeArchetypes(composeEntries, archetypeResults) {
|
|
|
298
366
|
for (const page of data.pages) {
|
|
299
367
|
allPages.push({
|
|
300
368
|
id: page.id,
|
|
301
|
-
layout: page.default_layout?.length ? page.default_layout : ["hero"],
|
|
369
|
+
layout: (page.default_layout?.length ? page.default_layout : ["hero"]).map((item) => resolvePatternAlias(item, page.patterns)),
|
|
302
370
|
...page.shell !== defaultShell ? { shell_override: page.shell } : {}
|
|
303
371
|
});
|
|
304
372
|
}
|
|
@@ -307,7 +375,7 @@ function composeArchetypes(composeEntries, archetypeResults) {
|
|
|
307
375
|
for (const page of data.pages) {
|
|
308
376
|
allPages.push({
|
|
309
377
|
id: `${prefix}-${page.id}`,
|
|
310
|
-
layout: page.default_layout?.length ? page.default_layout : ["hero"],
|
|
378
|
+
layout: (page.default_layout?.length ? page.default_layout : ["hero"]).map((item) => resolvePatternAlias(item, page.patterns)),
|
|
311
379
|
...page.shell !== defaultShell ? { shell_override: page.shell } : {}
|
|
312
380
|
});
|
|
313
381
|
}
|
|
@@ -359,7 +427,7 @@ function composeSections(composeEntries, archetypeResults, overrides) {
|
|
|
359
427
|
const overriddenPage = overrides?.pages?.[page.id];
|
|
360
428
|
pages.push({
|
|
361
429
|
id: page.id,
|
|
362
|
-
layout: page.default_layout?.length ? page.default_layout : ["hero"],
|
|
430
|
+
layout: (page.default_layout?.length ? page.default_layout : ["hero"]).map((item) => resolvePatternAlias(item, page.patterns)),
|
|
363
431
|
...overriddenPage
|
|
364
432
|
});
|
|
365
433
|
}
|
|
@@ -521,6 +589,24 @@ function generateTopologySection(data, personality) {
|
|
|
521
589
|
return lines.join("\n");
|
|
522
590
|
}
|
|
523
591
|
var CLI_VERSION = "1.0.0";
|
|
592
|
+
function mapRegistryThemeToThemeData(theme) {
|
|
593
|
+
return {
|
|
594
|
+
seed: theme.seed,
|
|
595
|
+
palette: theme.palette,
|
|
596
|
+
tokens: theme.tokens,
|
|
597
|
+
cvd_support: theme.cvd_support,
|
|
598
|
+
typography: theme.typography,
|
|
599
|
+
motion: theme.motion,
|
|
600
|
+
decorators: theme.decorators,
|
|
601
|
+
treatments: theme.treatments,
|
|
602
|
+
spatial: theme.spatial,
|
|
603
|
+
radius: theme.radius,
|
|
604
|
+
shell: theme.shell,
|
|
605
|
+
effects: theme.effects,
|
|
606
|
+
compositions: theme.compositions,
|
|
607
|
+
pattern_preferences: theme.pattern_preferences
|
|
608
|
+
};
|
|
609
|
+
}
|
|
524
610
|
function generateTokensCSS(themeData, mode, spatialTokens) {
|
|
525
611
|
if (!themeData) {
|
|
526
612
|
const spatialLines2 = spatialTokens ? "\n" + Object.entries(spatialTokens).map(([k, v]) => ` ${k}: ${v};`).join("\n") : "";
|
|
@@ -671,6 +757,26 @@ body {
|
|
|
671
757
|
min-height: 100dvh;
|
|
672
758
|
}
|
|
673
759
|
|
|
760
|
+
.skip-link {
|
|
761
|
+
position: absolute;
|
|
762
|
+
top: 0.75rem;
|
|
763
|
+
left: 0.75rem;
|
|
764
|
+
z-index: 100;
|
|
765
|
+
padding: 0.5rem 0.75rem;
|
|
766
|
+
border-radius: var(--d-radius);
|
|
767
|
+
background: var(--d-surface-raised);
|
|
768
|
+
color: var(--d-text);
|
|
769
|
+
text-decoration: none;
|
|
770
|
+
border: 1px solid var(--d-border);
|
|
771
|
+
transform: translateY(-150%);
|
|
772
|
+
transition: transform 0.15s ease;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
.skip-link:focus,
|
|
776
|
+
.skip-link:focus-visible {
|
|
777
|
+
transform: translateY(0);
|
|
778
|
+
}
|
|
779
|
+
|
|
674
780
|
img, picture, video, canvas, svg {
|
|
675
781
|
display: block;
|
|
676
782
|
max-width: 100%;
|
|
@@ -698,6 +804,15 @@ input, button, textarea, select {
|
|
|
698
804
|
border-width: 0;
|
|
699
805
|
}
|
|
700
806
|
|
|
807
|
+
@media (prefers-reduced-motion: reduce) {
|
|
808
|
+
*, *::before, *::after {
|
|
809
|
+
animation-duration: 0.01ms !important;
|
|
810
|
+
animation-iteration-count: 1 !important;
|
|
811
|
+
transition-duration: 0.01ms !important;
|
|
812
|
+
scroll-behavior: auto !important;
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
|
|
701
816
|
}
|
|
702
817
|
`;
|
|
703
818
|
}
|
|
@@ -861,10 +976,7 @@ function buildEssenceV3(options, archetypeData, themeHints) {
|
|
|
861
976
|
const meta = {
|
|
862
977
|
archetype: options.archetype || "custom",
|
|
863
978
|
target: options.target,
|
|
864
|
-
platform:
|
|
865
|
-
type: "spa",
|
|
866
|
-
routing: "hash"
|
|
867
|
-
},
|
|
979
|
+
platform: getPlatformMeta(options.target),
|
|
868
980
|
guard: guardModeMap[options.guard] || guardModeMap.guided
|
|
869
981
|
};
|
|
870
982
|
return {
|
|
@@ -894,6 +1006,14 @@ import './styles/treatments.css'; // Treatments + theme decorators
|
|
|
894
1006
|
import './styles/global.css'; // Resets
|
|
895
1007
|
\`\`\`
|
|
896
1008
|
|
|
1009
|
+
### Runtime Rules
|
|
1010
|
+
|
|
1011
|
+
- Use the real \`@decantr/css\` runtime for atoms. If \`package.json\` does not already depend on \`@decantr/css\`, add it before building.
|
|
1012
|
+
- Do **not** create local atom-runtime substitutes such as \`src/lib/css.js\`, \`src/lib/css.ts\`, or hand-written \`src/styles/atoms.css\` files unless the task explicitly asks for a fallback runtime.
|
|
1013
|
+
- Keep atoms in \`css(...)\`, treatments as semantic classes, and theme decorators as additive classes. Do not blur those roles together.
|
|
1014
|
+
- Use \`d-control\` as the default semantic treatment for inputs, selects, and textareas. Theme decorators such as \`carbon-input\` are additive and should only layer on when the section or theme contract explicitly calls for them.
|
|
1015
|
+
- Use loading decorators such as \`carbon-skeleton\` as optional enhancement on top of a structurally correct loading state \u2014 they do not replace the need for a real loading/skeleton branch.
|
|
1016
|
+
|
|
897
1017
|
### Visual Treatments
|
|
898
1018
|
|
|
899
1019
|
Six base treatment classes provide semantic styling. Combine with atoms for layout:
|
|
@@ -1032,16 +1152,26 @@ css('hover:_opacity80')
|
|
|
1032
1152
|
| Atom | CSS |
|
|
1033
1153
|
|------|-----|
|
|
1034
1154
|
| \`_bgprimary\` | \`background:var(--d-primary)\` |
|
|
1155
|
+
| \`_bgaccent\` | \`background:var(--d-accent)\` |
|
|
1156
|
+
| \`_bgsecondary\` | \`background:var(--d-secondary)\` |
|
|
1035
1157
|
| \`_bgsurface\` | \`background:var(--d-surface)\` |
|
|
1036
1158
|
| \`_bgsurface0\`-\`_bgsurface2\` | surface elevation layers |
|
|
1037
1159
|
| \`_bgmuted\` | \`background:var(--d-muted)\` |
|
|
1038
1160
|
| \`_bgbg\` | \`background:var(--d-bg)\` |
|
|
1161
|
+
| \`_bgtransparent\` | \`background:transparent\` |
|
|
1039
1162
|
| \`_bgsuccess\`, \`_bgerror\`, \`_bgwarning\`, \`_bginfo\` | status backgrounds |
|
|
1040
1163
|
| \`_fgprimary\` | \`color:var(--d-primary)\` |
|
|
1164
|
+
| \`_fgaccent\` | \`color:var(--d-accent)\` |
|
|
1165
|
+
| \`_fgsecondary\` | \`color:var(--d-secondary)\` |
|
|
1041
1166
|
| \`_fgtext\` | \`color:var(--d-text)\` |
|
|
1042
1167
|
| \`_fgmuted\` | \`color:var(--d-text-muted)\` |
|
|
1168
|
+
| \`_fgwhite\`, \`_fgblack\`, \`_fginherit\` | absolute/inherited text colors |
|
|
1043
1169
|
| \`_fgsuccess\`, \`_fgerror\`, \`_fgwarning\`, \`_fginfo\` | status text |
|
|
1170
|
+
| \`_bcprimary\` | \`border-color:var(--d-primary)\` |
|
|
1171
|
+
| \`_bcaccent\` | \`border-color:var(--d-accent)\` |
|
|
1044
1172
|
| \`_bcborder\` | \`border-color:var(--d-border)\` |
|
|
1173
|
+
| \`_bcmuted\` | \`border-color:var(--d-muted)\` |
|
|
1174
|
+
| \`_bctransparent\` | \`border-color:transparent\` |
|
|
1045
1175
|
|
|
1046
1176
|
#### Overflow & Whitespace
|
|
1047
1177
|
| Atom | CSS |
|
|
@@ -1138,16 +1268,54 @@ If the essence defines hotkeys or command_palette, implement as keyboard event l
|
|
|
1138
1268
|
Check \`decantr.essence.json\` \u2192 \`meta.platform.routing\` for the routing strategy:
|
|
1139
1269
|
- \`"hash"\` \u2192 use \`HashRouter\` (e.g., for static hosting, GitHub Pages)
|
|
1140
1270
|
- \`"history"\` \u2192 use \`BrowserRouter\` (e.g., for server-rendered apps)
|
|
1271
|
+
- \`"pathname"\` \u2192 use pathname-based routing (e.g., Next.js App Router or React apps using \`BrowserRouter\`)
|
|
1141
1272
|
|
|
1142
1273
|
Routes are defined in \`decantr.essence.json\` \u2192 \`blueprint.routes\` and listed in \`.decantr/context/scaffold.md\`.
|
|
1143
1274
|
|
|
1275
|
+
### SEO Expectations by Platform
|
|
1276
|
+
|
|
1277
|
+
- For hash-routed SPA scaffolds, focus SEO work on the root document: document title, description, Open Graph/Twitter meta, and any root-level JSON-LD that the contract calls for.
|
|
1278
|
+
- Do **not** invent SSR-only per-route metadata systems for a clearly hash-routed scaffold.
|
|
1279
|
+
- For history/SSR-style projects, per-route metadata can be richer, but it still needs to follow the declared route contract instead of introducing off-contract marketing pages.
|
|
1280
|
+
|
|
1144
1281
|
### Layout Rules
|
|
1145
1282
|
|
|
1146
1283
|
1. **Never nest d-surface inside d-surface.** Inner sections use plain containers with padding atoms.
|
|
1147
1284
|
2. **Shell regions are frames, not surfaces.** Sidebar and header use var(--d-surface) or var(--d-bg) directly. Apply d-surface only to content cards within the body region.
|
|
1148
1285
|
3. **One scroll container per region.** Body has overflow-y-auto. Sidebar nav has its own overflow-y-auto. Never nest additional scrollable wrappers.
|
|
1149
1286
|
4. **d-section spacing is self-contained.** Each d-section owns its padding. The d-section + d-section rule adds a separator. Do NOT add extra margin between adjacent sections.
|
|
1150
|
-
5. **Responsive nav rules.** Hamburger menus appear ONLY below the shell collapse breakpoint. Full nav shows above it
|
|
1287
|
+
5. **Responsive nav rules.** Hamburger menus appear ONLY below the shell collapse breakpoint. Full nav shows above it.
|
|
1288
|
+
|
|
1289
|
+
### Accessibility Defaults
|
|
1290
|
+
|
|
1291
|
+
- If \`dna.accessibility.skip_nav = true\`, add a visible-on-focus skip link such as \`<a href="#main-content" className="skip-link">Skip to content</a>\`.
|
|
1292
|
+
- Pair that skip link with a real main landmark target such as \`<main id="main-content">\`.
|
|
1293
|
+
- Keep keyboard focus visible with \`:focus-visible\` treatments on custom interactive surfaces, not just browser defaults.
|
|
1294
|
+
- Implement shell-level accessibility and routing behaviors as reusable structure or shared helpers, not one-off inline patches. Compact header sizing, responsive sidebar collapse, and skip-nav targets should be consistent across the shell, not re-solved page by page.
|
|
1295
|
+
|
|
1296
|
+
### Motion Philosophy
|
|
1297
|
+
|
|
1298
|
+
Every interaction should feel responsive and polished. Apply motion by default, not as an afterthought:
|
|
1299
|
+
|
|
1300
|
+
- **Page transitions:** Apply entrance-fade (or the personality entrance animation) to the main content area on route change
|
|
1301
|
+
- **Stagger children:** Lists, grids, and card groups should stagger-animate on mount (50-100ms delay per item)
|
|
1302
|
+
- **Data visualization:** Charts, gauges, progress bars, and counters should animate to their values on mount \u2014 never render static
|
|
1303
|
+
- **Micro-interactions:** All interactive elements (buttons, toggles, cards, nav items) need hover/press transitions. Use the motion tokens (--d-duration-hover, --d-easing) for consistency.
|
|
1304
|
+
- **Scroll reveals:** Sections below the fold should fade-in on scroll intersection (IntersectionObserver, once)
|
|
1305
|
+
- **Reduced motion:** Wrap all animations in \`prefers-reduced-motion\` media query \u2014 skip animation, keep state changes instant
|
|
1306
|
+
|
|
1307
|
+
Never leave this to implication when \`dna.motion.reduce_motion = true\`. The scaffold should include a reviewed reduced-motion path in project CSS, even when the app initially runs on mock data.
|
|
1308
|
+
|
|
1309
|
+
### Interactivity Philosophy
|
|
1310
|
+
|
|
1311
|
+
Build for wow factor. When a pattern describes a canvas, graph, map, or spatial visualization, implement it as a **fully interactive surface**, not a static illustration:
|
|
1312
|
+
|
|
1313
|
+
- **Drag and drop:** Nodes, cards, and items on spatial canvases should be draggable. Use pointer events with proper grab/grabbing cursors.
|
|
1314
|
+
- **Pan and zoom:** Canvases and large visualizations should support pan (click-drag on background) and zoom (scroll wheel or pinch). Show zoom level indicator.
|
|
1315
|
+
- **Connections:** When nodes exist in a graph/topology view, they should have visible connection lines. Implement click-to-select + click-target for connecting nodes.
|
|
1316
|
+
- **Live state:** Data-driven visualizations should update in real-time with simulated data. Status changes should animate (color transitions, pulse effects).
|
|
1317
|
+
- **Direct manipulation:** Prefer drag-to-reorder over dropdown menus. Prefer inline editing over modal forms. Prefer resize handles over fixed layouts.
|
|
1318
|
+
- **Hover reveals:** Show contextual information (tooltips, expanded cards, action menus) on hover \u2014 don't require clicks to discover functionality.`;
|
|
1151
1319
|
function generateDecantrMdV31(params) {
|
|
1152
1320
|
const template = loadTemplate("DECANTR.md.template");
|
|
1153
1321
|
const body = renderTemplate(template, {
|
|
@@ -1191,6 +1359,29 @@ function generateDecantrMdV31(params) {
|
|
|
1191
1359
|
}
|
|
1192
1360
|
briefLines.push("");
|
|
1193
1361
|
}
|
|
1362
|
+
briefLines.push("## Development Workflow");
|
|
1363
|
+
briefLines.push("");
|
|
1364
|
+
briefLines.push("The essence file (`decantr.essence.json`) is the source of truth for your project's structure. Context files in `.decantr/context/` are derived from it. When you need to add, remove, or modify pages, sections, or features:");
|
|
1365
|
+
briefLines.push("");
|
|
1366
|
+
briefLines.push("**1. Update the essence** (use CLI commands for consistency):");
|
|
1367
|
+
briefLines.push("- `decantr add page {section}/{page} --route /{path}`");
|
|
1368
|
+
briefLines.push("- `decantr add section {archetype}`");
|
|
1369
|
+
briefLines.push("- `decantr add feature {name}` (or `--section {id}` for scoped)");
|
|
1370
|
+
briefLines.push("- `decantr remove page {section}/{page}`");
|
|
1371
|
+
briefLines.push("- `decantr remove section {id}`");
|
|
1372
|
+
briefLines.push("- `decantr remove feature {name}`");
|
|
1373
|
+
briefLines.push("- `decantr theme switch {name}`");
|
|
1374
|
+
briefLines.push("");
|
|
1375
|
+
briefLines.push("**2. Regenerate context:** `decantr refresh`");
|
|
1376
|
+
briefLines.push("");
|
|
1377
|
+
briefLines.push("**3. Read the updated context files**, then build.");
|
|
1378
|
+
briefLines.push("");
|
|
1379
|
+
briefLines.push("**Rules:**");
|
|
1380
|
+
briefLines.push("- Never create page components for routes that don't exist in the essence");
|
|
1381
|
+
briefLines.push("- Never delete pages without removing them from the essence");
|
|
1382
|
+
briefLines.push("- Always refresh after mutations \u2014 stale context files lead to drift");
|
|
1383
|
+
briefLines.push("- If you edit the essence directly, run `decantr refresh` before building");
|
|
1384
|
+
briefLines.push("");
|
|
1194
1385
|
briefLines.push("---");
|
|
1195
1386
|
briefLines.push("");
|
|
1196
1387
|
return briefLines.join("\n") + body;
|
|
@@ -1261,9 +1452,8 @@ function generateTaskContextV3(templateName, essence) {
|
|
|
1261
1452
|
const contentGap = essence.dna.spacing?.content_gap || "_gap4";
|
|
1262
1453
|
const vars = {
|
|
1263
1454
|
TARGET: essence.meta.target || "react",
|
|
1264
|
-
|
|
1455
|
+
THEME_ID: essence.dna.theme.id || "",
|
|
1265
1456
|
THEME_MODE: essence.dna.theme.mode,
|
|
1266
|
-
THEME_RECIPE: essence.dna.theme.id || essence.dna.theme.style || "",
|
|
1267
1457
|
DEFAULT_SHELL: defaultShell,
|
|
1268
1458
|
GUARD_MODE: essence.meta.guard.mode,
|
|
1269
1459
|
LAYOUT: layout,
|
|
@@ -1273,6 +1463,186 @@ function generateTaskContextV3(templateName, essence) {
|
|
|
1273
1463
|
};
|
|
1274
1464
|
return renderTemplate(template, vars);
|
|
1275
1465
|
}
|
|
1466
|
+
function renderPackReferenceList(title, entries, fallback, summaryPath = ".decantr/context/pack-manifest.json") {
|
|
1467
|
+
if (entries.length === 0) {
|
|
1468
|
+
return `### ${title}
|
|
1469
|
+
|
|
1470
|
+
- ${fallback}`;
|
|
1471
|
+
}
|
|
1472
|
+
if (entries.length <= 6) {
|
|
1473
|
+
return `### ${title}
|
|
1474
|
+
|
|
1475
|
+
${entries.map((entry) => `- ${entry}`).join("\n")}`;
|
|
1476
|
+
}
|
|
1477
|
+
return `### ${title}
|
|
1478
|
+
|
|
1479
|
+
- ${entries.length} compiled references available. Use \`${summaryPath}\` to resolve the exact files for this scope.`;
|
|
1480
|
+
}
|
|
1481
|
+
function generateScaffoldTaskContext(essence, scaffoldPack, manifest) {
|
|
1482
|
+
if (!scaffoldPack) {
|
|
1483
|
+
return generateTaskContextV3("task-scaffold.md.template", essence);
|
|
1484
|
+
}
|
|
1485
|
+
const themeShape = scaffoldPack.data.theme.shape || "default";
|
|
1486
|
+
const features = scaffoldPack.data.features.length > 0 ? scaffoldPack.data.features.join(", ") : "none";
|
|
1487
|
+
const routePlan = scaffoldPack.data.routes.length > 0 ? scaffoldPack.data.routes.map((route) => {
|
|
1488
|
+
const patternSummary = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
|
|
1489
|
+
return `- \`${route.path}\` -> \`${route.pageId}\` [${patternSummary}]`;
|
|
1490
|
+
}).join("\n") : "- No routes declared";
|
|
1491
|
+
const successChecks = scaffoldPack.successChecks.map((check) => `- [${check.severity}] ${check.label}`).join("\n");
|
|
1492
|
+
const tokenStrategy = scaffoldPack.tokenBudget.strategy.map((item) => `- ${item}`).join("\n");
|
|
1493
|
+
const sectionRefs = manifest?.sections.map(
|
|
1494
|
+
(section) => `Section \`${section.id}\` -> \`.decantr/context/${section.markdown}\``
|
|
1495
|
+
) ?? [];
|
|
1496
|
+
const pageRefs = manifest?.pages.map(
|
|
1497
|
+
(page) => `Page \`${page.id}\` -> \`.decantr/context/${page.markdown}\``
|
|
1498
|
+
) ?? [];
|
|
1499
|
+
return `# Task Context: Scaffolding
|
|
1500
|
+
|
|
1501
|
+
**Enforcement Tier: Creative** \u2014 Guard rules are advisory during initial scaffolding.
|
|
1502
|
+
|
|
1503
|
+
## Primary Compiled Contract
|
|
1504
|
+
|
|
1505
|
+
- Start with \`.decantr/context/scaffold-pack.md\` for the compact route, shell, and theme contract.
|
|
1506
|
+
- Use \`.decantr/context/scaffold.md\` only as secondary detail when the compiled pack is not enough.
|
|
1507
|
+
- Read the route-local page packs before building each page so layout and wiring stay aligned with the compiled plan.
|
|
1508
|
+
|
|
1509
|
+
## Generate This Application
|
|
1510
|
+
|
|
1511
|
+
- Target: \`${scaffoldPack.target.adapter}\` (${scaffoldPack.target.framework || "unknown framework"})
|
|
1512
|
+
- Shell: \`${scaffoldPack.data.shell}\`
|
|
1513
|
+
- Theme: \`${scaffoldPack.data.theme.id}\` (${scaffoldPack.data.theme.mode}, ${themeShape})
|
|
1514
|
+
- Routing: \`${scaffoldPack.data.routing}\`
|
|
1515
|
+
- Features: ${features}
|
|
1516
|
+
|
|
1517
|
+
## Route Plan
|
|
1518
|
+
|
|
1519
|
+
${routePlan}
|
|
1520
|
+
|
|
1521
|
+
${renderPackReferenceList("Section Packs", sectionRefs, "No section packs were generated for this scaffold.")}
|
|
1522
|
+
|
|
1523
|
+
${renderPackReferenceList("Page Packs", pageRefs, "No page packs were generated for this scaffold.")}
|
|
1524
|
+
|
|
1525
|
+
## Success Checks
|
|
1526
|
+
|
|
1527
|
+
${successChecks}
|
|
1528
|
+
|
|
1529
|
+
## Token Budget
|
|
1530
|
+
|
|
1531
|
+
- Target: ${scaffoldPack.tokenBudget.target} tokens
|
|
1532
|
+
- Max: ${scaffoldPack.tokenBudget.max} tokens
|
|
1533
|
+
${tokenStrategy}
|
|
1534
|
+
|
|
1535
|
+
Post-scaffold enforcement mode: **${essence.meta.guard.mode.toUpperCase()}**.
|
|
1536
|
+
|
|
1537
|
+
---
|
|
1538
|
+
|
|
1539
|
+
*Task context generated from Decantr execution packs*`;
|
|
1540
|
+
}
|
|
1541
|
+
function generateAddPageTaskContext(essence, scaffoldPack, manifest) {
|
|
1542
|
+
if (!scaffoldPack) {
|
|
1543
|
+
return generateTaskContextV3("task-add-page.md.template", essence);
|
|
1544
|
+
}
|
|
1545
|
+
const routePlan = scaffoldPack.data.routes.length > 0 ? scaffoldPack.data.routes.map((route) => {
|
|
1546
|
+
const patternSummary = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
|
|
1547
|
+
return `- \`${route.path}\` -> \`${route.pageId}\` [${patternSummary}]`;
|
|
1548
|
+
}).join("\n") : "- No routes declared";
|
|
1549
|
+
const sectionRefs = manifest?.sections.map(
|
|
1550
|
+
(section) => `Section \`${section.id}\` -> \`.decantr/context/${section.markdown}\``
|
|
1551
|
+
) ?? [];
|
|
1552
|
+
const pageRefs = manifest?.pages.map(
|
|
1553
|
+
(page) => `Page \`${page.id}\` -> \`.decantr/context/${page.markdown}\``
|
|
1554
|
+
) ?? [];
|
|
1555
|
+
return `# Task Context: Adding Pages
|
|
1556
|
+
|
|
1557
|
+
**Enforcement Tier: Guided**
|
|
1558
|
+
|
|
1559
|
+
## Primary Compiled Contract
|
|
1560
|
+
|
|
1561
|
+
- Start with \`.decantr/context/mutation-add-page-pack.md\` for the add-page workflow contract.
|
|
1562
|
+
- Use \`.decantr/context/scaffold-pack.md\` for the current route, shell, and theme contract.
|
|
1563
|
+
- Use \`.decantr/context/pack-manifest.json\` to choose the target section before you add a route.
|
|
1564
|
+
- After updating the essence, run \`npx @decantr/cli refresh\` so the new section/page packs exist before code generation.
|
|
1565
|
+
|
|
1566
|
+
## Current Scaffold Contract
|
|
1567
|
+
|
|
1568
|
+
- Target: \`${scaffoldPack.target.adapter}\` (${scaffoldPack.target.framework || "unknown framework"})
|
|
1569
|
+
- Shell: \`${scaffoldPack.data.shell}\`
|
|
1570
|
+
- Theme: \`${scaffoldPack.data.theme.id}\` (${scaffoldPack.data.theme.mode})
|
|
1571
|
+
- Existing routes: ${scaffoldPack.data.routes.length}
|
|
1572
|
+
|
|
1573
|
+
## Existing Routes
|
|
1574
|
+
|
|
1575
|
+
${routePlan}
|
|
1576
|
+
|
|
1577
|
+
${renderPackReferenceList("Section Packs", sectionRefs, "No section packs were generated for this scaffold.")}
|
|
1578
|
+
|
|
1579
|
+
${renderPackReferenceList("Page Packs", pageRefs, "No page packs were generated for this scaffold.")}
|
|
1580
|
+
|
|
1581
|
+
## Required Workflow
|
|
1582
|
+
|
|
1583
|
+
1. Add the new page to the essence before generating any code.
|
|
1584
|
+
2. Keep the new page inside a declared section and shell contract.
|
|
1585
|
+
3. Refresh derived files so Decantr recompiles the section and page packs.
|
|
1586
|
+
4. Read the relevant section pack and the new page pack before implementation.
|
|
1587
|
+
|
|
1588
|
+
## Guided Checks
|
|
1589
|
+
|
|
1590
|
+
- [error] Theme identity remains \`${scaffoldPack.data.theme.id}\` until the essence changes.
|
|
1591
|
+
- [error] The new page exists in the essence before code generation begins.
|
|
1592
|
+
- [error] New layouts only use registry-backed patterns.
|
|
1593
|
+
- [warn] New routes should fit the current shell and section topology instead of creating off-contract filler pages.
|
|
1594
|
+
|
|
1595
|
+
---
|
|
1596
|
+
|
|
1597
|
+
*Task context generated from Decantr execution packs*`;
|
|
1598
|
+
}
|
|
1599
|
+
function generateModifyTaskContext(essence, scaffoldPack, manifest) {
|
|
1600
|
+
if (!scaffoldPack) {
|
|
1601
|
+
return generateTaskContextV3("task-modify.md.template", essence);
|
|
1602
|
+
}
|
|
1603
|
+
const routePlan = scaffoldPack.data.routes.length > 0 ? scaffoldPack.data.routes.map((route) => {
|
|
1604
|
+
const patternSummary = route.patternIds.length > 0 ? route.patternIds.join(", ") : "none";
|
|
1605
|
+
return `- \`${route.path}\` -> \`${route.pageId}\` [${patternSummary}]`;
|
|
1606
|
+
}).join("\n") : "- No routes declared";
|
|
1607
|
+
const pageRefs = manifest?.pages.map(
|
|
1608
|
+
(page) => `Page \`${page.id}\` -> \`.decantr/context/${page.markdown}\``
|
|
1609
|
+
) ?? [];
|
|
1610
|
+
const successChecks = scaffoldPack.successChecks.map((check) => `- [${check.severity}] ${check.label}`).join("\n");
|
|
1611
|
+
return `# Task Context: Modifying Code
|
|
1612
|
+
|
|
1613
|
+
**Enforcement Tier: Strict**
|
|
1614
|
+
|
|
1615
|
+
## Primary Compiled Contract
|
|
1616
|
+
|
|
1617
|
+
- Start with \`.decantr/context/mutation-modify-pack.md\` for the strict modification workflow contract.
|
|
1618
|
+
- Start with \`decantr_get_page_context\` or the matching \`.decantr/context/page-*-pack.md\` file for the route you are editing.
|
|
1619
|
+
- Use \`decantr_get_section_context\` when you need the richer section contract behind that route.
|
|
1620
|
+
- If a change would alter route identity, shell identity, theme identity, or pattern contract, update the essence first and then refresh the packs.
|
|
1621
|
+
|
|
1622
|
+
## Current Route Topology
|
|
1623
|
+
|
|
1624
|
+
${routePlan}
|
|
1625
|
+
|
|
1626
|
+
${renderPackReferenceList("Page Packs", pageRefs, "No page packs were generated for this scaffold.")}
|
|
1627
|
+
|
|
1628
|
+
## Strict Workflow
|
|
1629
|
+
|
|
1630
|
+
1. Identify the target page and read its compiled page pack first.
|
|
1631
|
+
2. Compare the planned edit against the compiled route, shell, and pattern contract.
|
|
1632
|
+
3. If the edit changes that contract, stop and update the essence before writing code.
|
|
1633
|
+
4. Run \`npx @decantr/cli validate\` and \`npx @decantr/cli check\` after the modification.
|
|
1634
|
+
|
|
1635
|
+
## Strict Checks
|
|
1636
|
+
|
|
1637
|
+
${successChecks}
|
|
1638
|
+
- [error] The page you modify must already exist in the compiled topology.
|
|
1639
|
+
- [error] Pattern order and shell usage should stay aligned with the page pack unless the essence changes first.
|
|
1640
|
+
- [warn] Use section context only as supporting detail; the page pack is the primary contract for route-local work.
|
|
1641
|
+
|
|
1642
|
+
---
|
|
1643
|
+
|
|
1644
|
+
*Task context generated from Decantr execution packs*`;
|
|
1645
|
+
}
|
|
1276
1646
|
function generateEssenceSummaryV3(essence) {
|
|
1277
1647
|
const template = loadTemplate("essence-summary.md.template");
|
|
1278
1648
|
const blueprint = essence.blueprint;
|
|
@@ -1300,9 +1670,8 @@ ${rows.join("\n")}`;
|
|
|
1300
1670
|
BLUEPRINT: "",
|
|
1301
1671
|
PERSONALITY: (essence.dna.personality || []).join(", "),
|
|
1302
1672
|
TARGET: essence.meta.target ?? "",
|
|
1303
|
-
|
|
1673
|
+
THEME_ID: essence.dna.theme.id ?? "",
|
|
1304
1674
|
THEME_MODE: essence.dna.theme.mode,
|
|
1305
|
-
THEME_RECIPE: essence.dna.theme.id ?? essence.dna.theme.style ?? "",
|
|
1306
1675
|
SHAPE: essence.dna.theme.shape ?? "",
|
|
1307
1676
|
PAGES_TABLE: pagesTable,
|
|
1308
1677
|
FEATURES_LIST: featuresList,
|
|
@@ -1354,9 +1723,6 @@ async function scaffoldProject(projectRoot, options, detected, registry, archety
|
|
|
1354
1723
|
}
|
|
1355
1724
|
writeFileSync(projectJsonPath, JSON.stringify(projectJsonObj, null, 2));
|
|
1356
1725
|
const contextFiles = [];
|
|
1357
|
-
const scaffoldPath = join(contextDir, "task-scaffold.md");
|
|
1358
|
-
writeFileSync(scaffoldPath, generateTaskContextV3("task-scaffold.md.template", essenceV3));
|
|
1359
|
-
contextFiles.push(scaffoldPath);
|
|
1360
1726
|
if (composedSections) {
|
|
1361
1727
|
essenceV3.version = "3.1.0";
|
|
1362
1728
|
essenceV3.blueprint = {
|
|
@@ -1393,7 +1759,7 @@ async function scaffoldProject(projectRoot, options, detected, registry, archety
|
|
|
1393
1759
|
function scaffoldMinimal(projectRoot) {
|
|
1394
1760
|
const decantrDir = join(projectRoot, ".decantr");
|
|
1395
1761
|
const customDir = join(decantrDir, "custom");
|
|
1396
|
-
const contentTypes =
|
|
1762
|
+
const contentTypes = API_CONTENT_TYPES;
|
|
1397
1763
|
for (const type of contentTypes) {
|
|
1398
1764
|
mkdirSync(join(customDir, type), { recursive: true });
|
|
1399
1765
|
}
|
|
@@ -1451,10 +1817,7 @@ function scaffoldMinimal(projectRoot) {
|
|
|
1451
1817
|
meta: {
|
|
1452
1818
|
archetype: "custom",
|
|
1453
1819
|
target: "react",
|
|
1454
|
-
platform:
|
|
1455
|
-
type: "spa",
|
|
1456
|
-
routing: "hash"
|
|
1457
|
-
},
|
|
1820
|
+
platform: getPlatformMeta("react"),
|
|
1458
1821
|
guard: {
|
|
1459
1822
|
mode: "guided",
|
|
1460
1823
|
dna_enforcement: "error",
|
|
@@ -1578,6 +1941,76 @@ When available, use these tools:
|
|
|
1578
1941
|
gitignoreUpdated
|
|
1579
1942
|
};
|
|
1580
1943
|
}
|
|
1944
|
+
function writeExecutionPackArtifacts(basePathWithoutExtension, pack) {
|
|
1945
|
+
const markdownPath = `${basePathWithoutExtension}.md`;
|
|
1946
|
+
const jsonPath = `${basePathWithoutExtension}.json`;
|
|
1947
|
+
writeFileSync(markdownPath, pack.renderedMarkdown);
|
|
1948
|
+
writeFileSync(jsonPath, JSON.stringify(pack, null, 2) + "\n");
|
|
1949
|
+
return markdownPath;
|
|
1950
|
+
}
|
|
1951
|
+
function writeExecutionPackBundleArtifacts(contextDir, bundle) {
|
|
1952
|
+
mkdirSync(contextDir, { recursive: true });
|
|
1953
|
+
const outputPaths = [];
|
|
1954
|
+
const scaffoldPackPath = writeExecutionPackArtifacts(join(contextDir, "scaffold-pack"), bundle.scaffold);
|
|
1955
|
+
outputPaths.push(scaffoldPackPath);
|
|
1956
|
+
const reviewPackPath = writeExecutionPackArtifacts(join(contextDir, "review-pack"), bundle.review);
|
|
1957
|
+
outputPaths.push(reviewPackPath);
|
|
1958
|
+
for (const sectionPack of bundle.sections) {
|
|
1959
|
+
const sectionPackPath = writeExecutionPackArtifacts(
|
|
1960
|
+
join(contextDir, `section-${sectionPack.data.sectionId}-pack`),
|
|
1961
|
+
sectionPack
|
|
1962
|
+
);
|
|
1963
|
+
outputPaths.push(sectionPackPath);
|
|
1964
|
+
}
|
|
1965
|
+
for (const pagePack of bundle.pages) {
|
|
1966
|
+
const pagePackPath = writeExecutionPackArtifacts(
|
|
1967
|
+
join(contextDir, `page-${pagePack.data.pageId}-pack`),
|
|
1968
|
+
pagePack
|
|
1969
|
+
);
|
|
1970
|
+
outputPaths.push(pagePackPath);
|
|
1971
|
+
}
|
|
1972
|
+
for (const mutationPack of bundle.mutations) {
|
|
1973
|
+
const mutationPackPath = writeExecutionPackArtifacts(
|
|
1974
|
+
join(contextDir, `mutation-${mutationPack.data.mutationType}-pack`),
|
|
1975
|
+
mutationPack
|
|
1976
|
+
);
|
|
1977
|
+
outputPaths.push(mutationPackPath);
|
|
1978
|
+
}
|
|
1979
|
+
const manifestPath = join(contextDir, "pack-manifest.json");
|
|
1980
|
+
writeFileSync(manifestPath, JSON.stringify(bundle.manifest, null, 2) + "\n");
|
|
1981
|
+
outputPaths.push(manifestPath);
|
|
1982
|
+
return {
|
|
1983
|
+
paths: outputPaths,
|
|
1984
|
+
scaffoldPackPath,
|
|
1985
|
+
reviewPackPath,
|
|
1986
|
+
manifestPath
|
|
1987
|
+
};
|
|
1988
|
+
}
|
|
1989
|
+
async function generatePackContexts(projectRoot, contextDir, essence) {
|
|
1990
|
+
const emptyResult = {
|
|
1991
|
+
paths: [],
|
|
1992
|
+
scaffoldPack: null,
|
|
1993
|
+
manifest: null
|
|
1994
|
+
};
|
|
1995
|
+
const cacheRoot = join(projectRoot, ".decantr", "cache", "@official");
|
|
1996
|
+
if (!existsSync(cacheRoot)) return emptyResult;
|
|
1997
|
+
const customRoot = join(projectRoot, ".decantr", "custom");
|
|
1998
|
+
const overridePaths = existsSync(customRoot) ? [customRoot] : void 0;
|
|
1999
|
+
try {
|
|
2000
|
+
const bundle = await compileExecutionPackBundle(essence, {
|
|
2001
|
+
contentRoot: cacheRoot,
|
|
2002
|
+
overridePaths
|
|
2003
|
+
});
|
|
2004
|
+
const writtenArtifacts = writeExecutionPackBundleArtifacts(contextDir, bundle);
|
|
2005
|
+
return {
|
|
2006
|
+
paths: writtenArtifacts.paths,
|
|
2007
|
+
scaffoldPack: bundle.scaffold,
|
|
2008
|
+
manifest: bundle.manifest
|
|
2009
|
+
};
|
|
2010
|
+
} catch {
|
|
2011
|
+
return emptyResult;
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
1581
2014
|
async function resolvePatternSpec(name, registry, prefetched, includeExtendedFields = true) {
|
|
1582
2015
|
if (prefetched && (prefetched.layout_hints || prefetched.visual_brief || !includeExtendedFields)) {
|
|
1583
2016
|
if (!prefetched.components || prefetched.components.length === 0) {
|
|
@@ -1593,32 +2026,7 @@ async function resolvePatternSpec(name, registry, prefetched, includeExtendedFie
|
|
|
1593
2026
|
try {
|
|
1594
2027
|
const patResult = await registry.fetchPattern(name);
|
|
1595
2028
|
if (patResult?.data) {
|
|
1596
|
-
|
|
1597
|
-
const defaultPreset = inner.default_preset || "standard";
|
|
1598
|
-
const preset = inner.presets?.[defaultPreset];
|
|
1599
|
-
let slots = preset?.layout?.slots || {};
|
|
1600
|
-
if (Object.keys(slots).length === 0) {
|
|
1601
|
-
const synthetic = generateSyntheticSlots(name, inner.description || "");
|
|
1602
|
-
if (Object.keys(synthetic).length > 0) slots = synthetic;
|
|
1603
|
-
}
|
|
1604
|
-
const spec = {
|
|
1605
|
-
description: inner.description || prefetched?.description || "",
|
|
1606
|
-
components: inner.components || prefetched?.components || [],
|
|
1607
|
-
slots,
|
|
1608
|
-
layout_hints: inner.layout_hints,
|
|
1609
|
-
...includeExtendedFields ? {
|
|
1610
|
-
visual_brief: inner.visual_brief,
|
|
1611
|
-
composition: inner.composition,
|
|
1612
|
-
motion: inner.motion,
|
|
1613
|
-
responsive: inner.responsive,
|
|
1614
|
-
accessibility: inner.accessibility
|
|
1615
|
-
} : {}
|
|
1616
|
-
};
|
|
1617
|
-
if (!spec.components || spec.components.length === 0) {
|
|
1618
|
-
const syntheticComps2 = generateSyntheticComponents(name, spec.description);
|
|
1619
|
-
if (syntheticComps2.length > 0) spec.components = syntheticComps2;
|
|
1620
|
-
}
|
|
1621
|
-
return spec;
|
|
2029
|
+
return mapRegistryPatternToPatternSpecSummary(patResult.data, prefetched, includeExtendedFields);
|
|
1622
2030
|
}
|
|
1623
2031
|
} catch {
|
|
1624
2032
|
}
|
|
@@ -1660,17 +2068,16 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
1660
2068
|
try {
|
|
1661
2069
|
const bpResult = await registry.fetchBlueprint(storedBlueprintId);
|
|
1662
2070
|
if (bpResult?.data) {
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
projectJsonData.voice = bpData.voice;
|
|
2071
|
+
if (bpResult.data.voice) {
|
|
2072
|
+
storedVoice = bpResult.data.voice;
|
|
2073
|
+
projectJsonData.voice = bpResult.data.voice;
|
|
1667
2074
|
writeFileSync(projectJsonFilePath, JSON.stringify(projectJsonData, null, 2));
|
|
1668
2075
|
}
|
|
1669
2076
|
}
|
|
1670
2077
|
} catch {
|
|
1671
2078
|
}
|
|
1672
2079
|
}
|
|
1673
|
-
const themeName = essence.dna.theme.id ||
|
|
2080
|
+
const themeName = essence.dna.theme.id || "default";
|
|
1674
2081
|
const mode = essence.dna.theme.mode;
|
|
1675
2082
|
const guardMode = essence.meta.guard.mode;
|
|
1676
2083
|
const guardConfig = {
|
|
@@ -1683,29 +2090,13 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
1683
2090
|
if (!themeData) try {
|
|
1684
2091
|
const themeResult = await registry.fetchTheme(themeName);
|
|
1685
2092
|
if (themeResult?.data) {
|
|
1686
|
-
|
|
1687
|
-
themeData = {
|
|
1688
|
-
seed: t.seed,
|
|
1689
|
-
palette: t.palette,
|
|
1690
|
-
cvd_support: t.cvd_support,
|
|
1691
|
-
tokens: t.tokens,
|
|
1692
|
-
typography: t.typography,
|
|
1693
|
-
motion: t.motion,
|
|
1694
|
-
decorators: t.decorators,
|
|
1695
|
-
treatments: t.treatments,
|
|
1696
|
-
spatial: t.spatial,
|
|
1697
|
-
radius: t.radius,
|
|
1698
|
-
shell: t.shell,
|
|
1699
|
-
effects: t.effects,
|
|
1700
|
-
compositions: t.compositions,
|
|
1701
|
-
pattern_preferences: t.pattern_preferences
|
|
1702
|
-
};
|
|
2093
|
+
themeData = mapRegistryThemeToThemeData(themeResult.data);
|
|
1703
2094
|
}
|
|
1704
2095
|
} catch {
|
|
1705
2096
|
}
|
|
1706
2097
|
if (!themeData?.seed?.primary) {
|
|
1707
2098
|
try {
|
|
1708
|
-
const apiUrl = registry.
|
|
2099
|
+
const apiUrl = registry.getApiUrl();
|
|
1709
2100
|
const resp = await fetch(`${apiUrl}/themes/@official/${themeName}`);
|
|
1710
2101
|
if (resp.ok) {
|
|
1711
2102
|
const apiData = await resp.json();
|
|
@@ -1787,7 +2178,7 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
1787
2178
|
writeFileSync(decantrMdPath, generateDecantrMdV31({
|
|
1788
2179
|
guardMode,
|
|
1789
2180
|
cssApproach: CSS_APPROACH_CONTENT,
|
|
1790
|
-
blueprintId: storedBlueprintId || essence.meta
|
|
2181
|
+
blueprintId: storedBlueprintId || getLegacyBlueprintId(essence.meta) || void 0,
|
|
1791
2182
|
themeName,
|
|
1792
2183
|
themeMode: mode,
|
|
1793
2184
|
themeShape: essence.dna.theme.shape || void 0,
|
|
@@ -1804,15 +2195,25 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
1804
2195
|
writeFileSync(summaryPath, generateEssenceSummaryV3(essence));
|
|
1805
2196
|
contextFiles.push(summaryPath);
|
|
1806
2197
|
}
|
|
2198
|
+
const packContexts = await generatePackContexts(projectRoot, contextDir, essence);
|
|
2199
|
+
const scaffoldTaskPath = join(contextDir, "task-scaffold.md");
|
|
2200
|
+
writeFileSync(
|
|
2201
|
+
scaffoldTaskPath,
|
|
2202
|
+
generateScaffoldTaskContext(essence, packContexts.scaffoldPack, packContexts.manifest)
|
|
2203
|
+
);
|
|
2204
|
+
contextFiles.push(scaffoldTaskPath);
|
|
1807
2205
|
if (!options?.isInitialScaffold) {
|
|
1808
|
-
const scaffoldTaskPath = join(contextDir, "task-scaffold.md");
|
|
1809
|
-
writeFileSync(scaffoldTaskPath, generateTaskContextV3("task-scaffold.md.template", essence));
|
|
1810
|
-
contextFiles.push(scaffoldTaskPath);
|
|
1811
2206
|
const addPagePath = join(contextDir, "task-add-page.md");
|
|
1812
|
-
writeFileSync(
|
|
2207
|
+
writeFileSync(
|
|
2208
|
+
addPagePath,
|
|
2209
|
+
generateAddPageTaskContext(essence, packContexts.scaffoldPack, packContexts.manifest)
|
|
2210
|
+
);
|
|
1813
2211
|
contextFiles.push(addPagePath);
|
|
1814
2212
|
const modifyPath = join(contextDir, "task-modify.md");
|
|
1815
|
-
writeFileSync(
|
|
2213
|
+
writeFileSync(
|
|
2214
|
+
modifyPath,
|
|
2215
|
+
generateModifyTaskContext(essence, packContexts.scaffoldPack, packContexts.manifest)
|
|
2216
|
+
);
|
|
1816
2217
|
contextFiles.push(modifyPath);
|
|
1817
2218
|
}
|
|
1818
2219
|
const blueprint = essence.blueprint;
|
|
@@ -1878,16 +2279,7 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
1878
2279
|
try {
|
|
1879
2280
|
const shellResult = await registry.fetchShell(shellId);
|
|
1880
2281
|
if (shellResult?.data) {
|
|
1881
|
-
|
|
1882
|
-
shellInfoCache[shellId] = {
|
|
1883
|
-
description: inner.description || "",
|
|
1884
|
-
regions: inner.config?.regions || [],
|
|
1885
|
-
layout: inner.layout || void 0,
|
|
1886
|
-
guidance: inner.guidance || void 0,
|
|
1887
|
-
atoms: inner.atoms || void 0,
|
|
1888
|
-
config: inner.config || void 0,
|
|
1889
|
-
internal_layout: inner.internal_layout || void 0
|
|
1890
|
-
};
|
|
2282
|
+
shellInfoCache[shellId] = mapRegistryShellToShellInfo(shellResult.data);
|
|
1891
2283
|
}
|
|
1892
2284
|
} catch {
|
|
1893
2285
|
}
|
|
@@ -1916,6 +2308,12 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
1916
2308
|
}
|
|
1917
2309
|
}
|
|
1918
2310
|
}
|
|
2311
|
+
const sectionSpatialHints = themeData?.spatial ? {
|
|
2312
|
+
section_padding: themeData.spatial.section_padding ?? void 0,
|
|
2313
|
+
density_bias: typeof themeData.spatial.density_bias === "number" ? themeData.spatial.density_bias : void 0,
|
|
2314
|
+
content_gap_shift: themeData.spatial.content_gap_shift,
|
|
2315
|
+
label_content_gap: themeData.spatial.label_content_gap ?? void 0
|
|
2316
|
+
} : void 0;
|
|
1919
2317
|
const contextContent = generateSectionContext({
|
|
1920
2318
|
section,
|
|
1921
2319
|
themeTokens: themeTokensCss,
|
|
@@ -1934,7 +2332,8 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
1934
2332
|
shellInfo: shellInfoCache[section.shell],
|
|
1935
2333
|
themeData,
|
|
1936
2334
|
themeMode: mode,
|
|
1937
|
-
voiceTone: storedVoice?.tone ? storedVoice.tone.split(".")[0] + "." : void 0
|
|
2335
|
+
voiceTone: storedVoice?.tone ? storedVoice.tone.split(".")[0] + "." : void 0,
|
|
2336
|
+
spatialHints: sectionSpatialHints
|
|
1938
2337
|
});
|
|
1939
2338
|
const sectionContextPath = join(contextDir, `section-${section.id}.md`);
|
|
1940
2339
|
writeFileSync(sectionContextPath, contextContent);
|
|
@@ -1943,7 +2342,7 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
1943
2342
|
const routes = blueprint.routes || {};
|
|
1944
2343
|
const scaffoldContent = generateScaffoldContext({
|
|
1945
2344
|
appName: essence.meta.archetype || "Application",
|
|
1946
|
-
blueprintId: storedBlueprintId || essence.meta
|
|
2345
|
+
blueprintId: storedBlueprintId || getLegacyBlueprintId(essence.meta) || "",
|
|
1947
2346
|
themeName,
|
|
1948
2347
|
personality,
|
|
1949
2348
|
topologyMarkdown,
|
|
@@ -1994,19 +2393,16 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
1994
2393
|
try {
|
|
1995
2394
|
const shellResult = await registry.fetchShell(shell);
|
|
1996
2395
|
if (shellResult?.data) {
|
|
1997
|
-
|
|
1998
|
-
v30ShellInfo = {
|
|
1999
|
-
description: inner.description || "",
|
|
2000
|
-
regions: inner.config?.regions || [],
|
|
2001
|
-
layout: inner.layout || void 0,
|
|
2002
|
-
guidance: inner.guidance || void 0,
|
|
2003
|
-
atoms: inner.atoms || void 0,
|
|
2004
|
-
config: inner.config || void 0,
|
|
2005
|
-
internal_layout: inner.internal_layout || void 0
|
|
2006
|
-
};
|
|
2396
|
+
v30ShellInfo = mapRegistryShellToShellInfo(shellResult.data);
|
|
2007
2397
|
}
|
|
2008
2398
|
} catch {
|
|
2009
2399
|
}
|
|
2400
|
+
const v30SpatialHints = themeData?.spatial ? {
|
|
2401
|
+
section_padding: themeData.spatial.section_padding ?? void 0,
|
|
2402
|
+
density_bias: typeof themeData.spatial.density_bias === "number" ? themeData.spatial.density_bias : void 0,
|
|
2403
|
+
content_gap_shift: themeData.spatial.content_gap_shift,
|
|
2404
|
+
label_content_gap: themeData.spatial.label_content_gap ?? void 0
|
|
2405
|
+
} : void 0;
|
|
2010
2406
|
const contextContent = generateSectionContext({
|
|
2011
2407
|
section: syntheticSection,
|
|
2012
2408
|
themeTokens: themeTokensCss,
|
|
@@ -2025,12 +2421,16 @@ async function refreshDerivedFiles(projectRoot, essence, registry, prefetchedThe
|
|
|
2025
2421
|
shellInfo: v30ShellInfo,
|
|
2026
2422
|
themeData,
|
|
2027
2423
|
themeMode: mode,
|
|
2028
|
-
voiceTone: storedVoice?.tone ? storedVoice.tone.split(".")[0] + "." : void 0
|
|
2424
|
+
voiceTone: storedVoice?.tone ? storedVoice.tone.split(".")[0] + "." : void 0,
|
|
2425
|
+
spatialHints: v30SpatialHints
|
|
2029
2426
|
});
|
|
2030
2427
|
const sectionContextPath = join(contextDir, `section-${syntheticSection.id}.md`);
|
|
2031
2428
|
writeFileSync(sectionContextPath, contextContent);
|
|
2032
2429
|
contextFiles.push(sectionContextPath);
|
|
2033
2430
|
}
|
|
2431
|
+
if (packContexts.paths.length > 0) {
|
|
2432
|
+
contextFiles.push(...packContexts.paths);
|
|
2433
|
+
}
|
|
2034
2434
|
return {
|
|
2035
2435
|
decantrMdPath,
|
|
2036
2436
|
contextFiles,
|
|
@@ -2124,6 +2524,50 @@ function generateSyntheticComponents(patternId, description) {
|
|
|
2124
2524
|
if (patternId.includes("empty") || patternId.includes("new")) syntheticComponents.push("Icon", "Text", "Button");
|
|
2125
2525
|
return [...new Set(syntheticComponents)];
|
|
2126
2526
|
}
|
|
2527
|
+
function mapRegistryPatternToPatternSpecSummary(pattern, prefetched, includeExtendedFields = true) {
|
|
2528
|
+
const defaultPreset = pattern.default_preset || "standard";
|
|
2529
|
+
const preset = pattern.presets?.[defaultPreset];
|
|
2530
|
+
let slots = preset?.layout?.slots || pattern.default_layout?.slots || prefetched?.slots || {};
|
|
2531
|
+
if (Object.keys(slots).length === 0) {
|
|
2532
|
+
const synthetic = generateSyntheticSlots(pattern.id, pattern.description || prefetched?.description || "");
|
|
2533
|
+
if (Object.keys(synthetic).length > 0) slots = synthetic;
|
|
2534
|
+
}
|
|
2535
|
+
const spec = {
|
|
2536
|
+
description: pattern.description || prefetched?.description || "",
|
|
2537
|
+
components: pattern.components || prefetched?.components || [],
|
|
2538
|
+
slots,
|
|
2539
|
+
layout_hints: pattern.layout_hints,
|
|
2540
|
+
...includeExtendedFields ? {
|
|
2541
|
+
visual_brief: pattern.visual_brief,
|
|
2542
|
+
composition: pattern.composition,
|
|
2543
|
+
motion: pattern.motion,
|
|
2544
|
+
responsive: pattern.responsive,
|
|
2545
|
+
accessibility: pattern.accessibility ? {
|
|
2546
|
+
role: pattern.accessibility.role,
|
|
2547
|
+
keyboard: pattern.accessibility.keyboard,
|
|
2548
|
+
announcements: pattern.accessibility.announcements,
|
|
2549
|
+
focus_management: pattern.accessibility.focus_management
|
|
2550
|
+
} : void 0
|
|
2551
|
+
} : {}
|
|
2552
|
+
};
|
|
2553
|
+
if (!spec.components || spec.components.length === 0) {
|
|
2554
|
+
const syntheticComps = generateSyntheticComponents(pattern.id, spec.description);
|
|
2555
|
+
if (syntheticComps.length > 0) spec.components = syntheticComps;
|
|
2556
|
+
}
|
|
2557
|
+
return spec;
|
|
2558
|
+
}
|
|
2559
|
+
function mapRegistryShellToShellInfo(shell) {
|
|
2560
|
+
const config = isRecord(shell.config) ? shell.config : void 0;
|
|
2561
|
+
return {
|
|
2562
|
+
description: shell.description || "",
|
|
2563
|
+
regions: getStringArray(config?.regions),
|
|
2564
|
+
layout: shell.layout || void 0,
|
|
2565
|
+
guidance: shell.guidance,
|
|
2566
|
+
atoms: shell.atoms,
|
|
2567
|
+
config,
|
|
2568
|
+
internal_layout: isRecord(shell.internal_layout) ? shell.internal_layout : void 0
|
|
2569
|
+
};
|
|
2570
|
+
}
|
|
2127
2571
|
function generateShellImplementation(shellId, shellInfo) {
|
|
2128
2572
|
const lines = [];
|
|
2129
2573
|
lines.push(`## Shell Implementation (${shellId})`);
|
|
@@ -2132,9 +2576,9 @@ function generateShellImplementation(shellId, shellInfo) {
|
|
|
2132
2576
|
for (const [region, props] of Object.entries(shellInfo.internal_layout)) {
|
|
2133
2577
|
lines.push(`### ${region}`);
|
|
2134
2578
|
lines.push("");
|
|
2135
|
-
if (
|
|
2579
|
+
if (isRecord(props)) {
|
|
2136
2580
|
for (const [key, value] of Object.entries(props)) {
|
|
2137
|
-
if (
|
|
2581
|
+
if (isRecord(value)) {
|
|
2138
2582
|
lines.push(`- **${key}:**`);
|
|
2139
2583
|
for (const [subKey, subValue] of Object.entries(value)) {
|
|
2140
2584
|
lines.push(` - ${subKey}: ${subValue}`);
|
|
@@ -2236,10 +2680,10 @@ function generateQuickStart(input) {
|
|
|
2236
2680
|
lines.push("");
|
|
2237
2681
|
return lines;
|
|
2238
2682
|
}
|
|
2239
|
-
function generateSpacingGuide(density) {
|
|
2683
|
+
function generateSpacingGuide(density, spatialHints) {
|
|
2240
2684
|
const lines = [];
|
|
2241
2685
|
const level = density === "compact" || density === "spacious" ? density : "comfortable";
|
|
2242
|
-
const tokens = computeSpatialTokens(level);
|
|
2686
|
+
const tokens = computeSpatialTokens(level, spatialHints);
|
|
2243
2687
|
lines.push("## Spacing Guide");
|
|
2244
2688
|
lines.push("");
|
|
2245
2689
|
lines.push("| Context | Token | Value | Usage |");
|
|
@@ -2251,11 +2695,15 @@ function generateSpacingGuide(density) {
|
|
|
2251
2695
|
lines.push(`| Interactive H | \`--d-interactive-px\` | \`${tokens["--d-interactive-px"]}\` | Horizontal padding on buttons |`);
|
|
2252
2696
|
lines.push(`| Control | \`--d-control-py\` | \`${tokens["--d-control-py"]}\` | Vertical padding on inputs |`);
|
|
2253
2697
|
lines.push(`| Data row | \`--d-data-py\` | \`${tokens["--d-data-py"]}\` | Vertical padding on table rows |`);
|
|
2698
|
+
lines.push(`| Label gap | \`--d-label-mb\` | \`${tokens["--d-label-mb"]}\` | Gap below d-label section headers |`);
|
|
2699
|
+
lines.push(`| Label indent | \`--d-label-px\` | \`${tokens["--d-label-px"]}\` | Anchor indent for d-label[data-anchor] |`);
|
|
2700
|
+
lines.push(`| Section gap | \`--d-section-gap\` | \`${tokens["--d-section-gap"]}\` | Gap between adjacent d-sections |`);
|
|
2701
|
+
lines.push(`| Annotation gap | \`--d-annotation-mt\` | \`${tokens["--d-annotation-mt"]}\` | Top margin on d-annotation |`);
|
|
2254
2702
|
lines.push("");
|
|
2255
2703
|
return lines;
|
|
2256
2704
|
}
|
|
2257
2705
|
function generateSectionContext(input) {
|
|
2258
|
-
const { section, decorators, guardConfig, personality, themeName, zoneContext, patternSpecs, themeHints, constraints, shellInfo } = input;
|
|
2706
|
+
const { section, decorators, guardConfig, personality, themeName, zoneContext, patternSpecs, themeHints, constraints, shellInfo, spatialHints } = input;
|
|
2259
2707
|
const lines = [];
|
|
2260
2708
|
lines.push(`# Section: ${section.id}`);
|
|
2261
2709
|
lines.push("");
|
|
@@ -2275,16 +2723,36 @@ function generateSectionContext(input) {
|
|
|
2275
2723
|
lines.push(...generateShellImplementation(section.shell, shellInfo));
|
|
2276
2724
|
}
|
|
2277
2725
|
if (shellInfo?.guidance && Object.keys(shellInfo.guidance).length > 0) {
|
|
2726
|
+
const labelTreatment = shellInfo.guidance.section_label_treatment;
|
|
2727
|
+
const sectionDensity = shellInfo.guidance.section_density;
|
|
2728
|
+
if (labelTreatment) {
|
|
2729
|
+
lines.push("## Section Label Treatment");
|
|
2730
|
+
lines.push("");
|
|
2731
|
+
lines.push(`Apply \`${labelTreatment}\` to section headers in this shell.`);
|
|
2732
|
+
lines.push("- Uppercase monospace label typography (d-label base treatment)");
|
|
2733
|
+
if (labelTreatment.includes("[data-anchor]")) {
|
|
2734
|
+
lines.push("- Left accent border anchor (data-anchor variant)");
|
|
2735
|
+
}
|
|
2736
|
+
lines.push("- Density-responsive bottom gap via `--d-label-mb` x `--d-density-scale`");
|
|
2737
|
+
if (sectionDensity) {
|
|
2738
|
+
const scaleMap = { compact: "0.65", comfortable: "1", spacious: "1.4" };
|
|
2739
|
+
lines.push("");
|
|
2740
|
+
lines.push(`Section density: ${sectionDensity} (--d-density-scale: ${scaleMap[sectionDensity] || "1"})`);
|
|
2741
|
+
}
|
|
2742
|
+
lines.push("");
|
|
2743
|
+
}
|
|
2744
|
+
const structuredKeys = /* @__PURE__ */ new Set(["section_label_treatment", "section_density"]);
|
|
2278
2745
|
lines.push(`## Shell Notes (${section.shell})`);
|
|
2279
2746
|
lines.push("");
|
|
2280
2747
|
for (const [key, value] of Object.entries(shellInfo.guidance)) {
|
|
2748
|
+
if (structuredKeys.has(key)) continue;
|
|
2281
2749
|
const label = key.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
2282
2750
|
lines.push(`- **${label}:** ${value}`);
|
|
2283
2751
|
}
|
|
2284
2752
|
lines.push("");
|
|
2285
2753
|
}
|
|
2286
2754
|
const density = section.dna_overrides?.density || "comfortable";
|
|
2287
|
-
lines.push(...generateSpacingGuide(density));
|
|
2755
|
+
lines.push(...generateSpacingGuide(density, spatialHints));
|
|
2288
2756
|
lines.push("---");
|
|
2289
2757
|
lines.push("");
|
|
2290
2758
|
lines.push(`**Guard:** ${guardConfig.mode} mode | DNA violations = ${guardConfig.dna_enforcement} | Blueprint violations = ${guardConfig.blueprint_enforcement}`);
|
|
@@ -2370,7 +2838,7 @@ function generateSectionContext(input) {
|
|
|
2370
2838
|
}
|
|
2371
2839
|
if (themeHints) {
|
|
2372
2840
|
if (themeHints.preferred && themeHints.preferred.length > 0) {
|
|
2373
|
-
const sectionPatterns = new Set(section.pages.flatMap((p) => p.layout.
|
|
2841
|
+
const sectionPatterns = new Set(section.pages.flatMap((p) => p.layout.flatMap(extractPatternNames)));
|
|
2374
2842
|
const relevant = themeHints.preferred.filter((p) => sectionPatterns.has(p));
|
|
2375
2843
|
if (relevant.length > 0) {
|
|
2376
2844
|
lines.push(`**Preferred:** ${relevant.join(", ")}`);
|
|
@@ -2445,6 +2913,9 @@ function generateSectionContext(input) {
|
|
|
2445
2913
|
if (uniquePatterns.size > 0) {
|
|
2446
2914
|
lines.push("## Pattern Reference");
|
|
2447
2915
|
lines.push("");
|
|
2916
|
+
lines.push("Scaffold-tier rule: implement the core visual structure, states, and required slots first.");
|
|
2917
|
+
lines.push("Treat advanced capabilities such as drag/drop, force-layout, minimaps, or simulated live streaming as optional unless the slot guidance or section contract makes them explicitly required.");
|
|
2918
|
+
lines.push("");
|
|
2448
2919
|
for (const [patternName, spec] of uniquePatterns) {
|
|
2449
2920
|
lines.push(`### ${patternName}`);
|
|
2450
2921
|
lines.push("");
|
|
@@ -2557,6 +3028,15 @@ function generateScaffoldContext(input) {
|
|
|
2557
3028
|
if (input.voice.metrics_format) lines.push(`**Metrics format:** ${input.voice.metrics_format}`);
|
|
2558
3029
|
lines.push("");
|
|
2559
3030
|
}
|
|
3031
|
+
lines.push("## Development Mode");
|
|
3032
|
+
lines.push("");
|
|
3033
|
+
lines.push("For local development and showcases, wire all zone transitions with mock data:");
|
|
3034
|
+
lines.push("");
|
|
3035
|
+
lines.push("- **Auth bypass:** Auth pages should accept any input and redirect to the primary section's default route");
|
|
3036
|
+
lines.push("- **Route guards:** Check a simple localStorage flag (e.g., `decantr_authenticated`). Login sets it \u2192 redirect to app zone entry. Logout clears it \u2192 redirect to public/gateway zone.");
|
|
3037
|
+
lines.push("- **Mock data on every page:** All pages should render with simulated data on first load \u2014 never show empty states during development");
|
|
3038
|
+
lines.push("- **Zone transitions:** CTA links on marketing pages should route to the gateway (login/register). Successful auth should route to the primary section default page.");
|
|
3039
|
+
lines.push("");
|
|
2560
3040
|
lines.push(topologyMarkdown);
|
|
2561
3041
|
lines.push("");
|
|
2562
3042
|
lines.push("## Sections Overview");
|
|
@@ -2644,9 +3124,9 @@ function generateScaffoldContext(input) {
|
|
|
2644
3124
|
// src/registry.ts
|
|
2645
3125
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, readdirSync } from "fs";
|
|
2646
3126
|
import { join as join2 } from "path";
|
|
2647
|
-
import { RegistryAPIClient } from "@decantr/registry";
|
|
3127
|
+
import { RegistryAPIClient, API_CONTENT_TYPES as API_CONTENT_TYPES2 } from "@decantr/registry";
|
|
2648
3128
|
var DEFAULT_API_URL = "https://api.decantr.ai/v1";
|
|
2649
|
-
var ALL_CONTENT_TYPES =
|
|
3129
|
+
var ALL_CONTENT_TYPES = API_CONTENT_TYPES2;
|
|
2650
3130
|
function loadFromCache(cacheDir, contentType, id, namespace) {
|
|
2651
3131
|
const nsDir = namespace ? join2(cacheDir, namespace) : cacheDir;
|
|
2652
3132
|
const cachePath = id ? join2(nsDir, contentType, `${id}.json`) : join2(nsDir, contentType, "index.json");
|
|
@@ -2673,13 +3153,16 @@ var RegistryClient = class {
|
|
|
2673
3153
|
constructor(options = {}) {
|
|
2674
3154
|
this.projectRoot = options.projectRoot || process.cwd();
|
|
2675
3155
|
this.cacheDir = options.cacheDir || join2(this.projectRoot, ".decantr", "cache");
|
|
2676
|
-
this.apiUrl = options.apiUrl || DEFAULT_API_URL;
|
|
3156
|
+
this.apiUrl = options.apiUrl || process.env.DECANTR_API_URL || DEFAULT_API_URL;
|
|
2677
3157
|
this.offline = options.offline || false;
|
|
2678
3158
|
this.apiClient = new RegistryAPIClient({
|
|
2679
3159
|
baseUrl: this.apiUrl,
|
|
2680
|
-
apiKey: options.apiKey
|
|
3160
|
+
apiKey: options.apiKey || process.env.DECANTR_API_KEY || void 0
|
|
2681
3161
|
});
|
|
2682
3162
|
}
|
|
3163
|
+
getApiUrl() {
|
|
3164
|
+
return this.apiUrl;
|
|
3165
|
+
}
|
|
2683
3166
|
/**
|
|
2684
3167
|
* Load content from .decantr/custom/{contentType}/{id}.json
|
|
2685
3168
|
* Works for ALL content types, not just themes.
|
|
@@ -2719,12 +3202,17 @@ var RegistryClient = class {
|
|
|
2719
3202
|
* Unified fetch for a content list.
|
|
2720
3203
|
* Resolution: API -> Cache. Custom items are merged into the list.
|
|
2721
3204
|
*/
|
|
2722
|
-
async fetchContentList(contentType, namespace) {
|
|
3205
|
+
async fetchContentList(contentType, namespace, sort, recommended, intelligenceSource) {
|
|
2723
3206
|
let apiItems = [];
|
|
2724
3207
|
let source = { type: "cache" };
|
|
2725
3208
|
if (!this.offline) {
|
|
2726
3209
|
try {
|
|
2727
|
-
const apiResult = await this.apiClient.listContent(contentType, {
|
|
3210
|
+
const apiResult = await this.apiClient.listContent(contentType, {
|
|
3211
|
+
namespace,
|
|
3212
|
+
sort,
|
|
3213
|
+
recommended,
|
|
3214
|
+
intelligenceSource
|
|
3215
|
+
});
|
|
2728
3216
|
apiItems = apiResult.items;
|
|
2729
3217
|
source = { type: "api", url: this.apiUrl };
|
|
2730
3218
|
saveToCache(this.cacheDir, contentType, null, apiResult, namespace || "@official");
|
|
@@ -2848,14 +3336,19 @@ async function syncRegistry(cacheDir, apiUrl = DEFAULT_API_URL) {
|
|
|
2848
3336
|
}
|
|
2849
3337
|
|
|
2850
3338
|
export {
|
|
3339
|
+
collectPatternIdsFromItems,
|
|
3340
|
+
mapRegistryArchetypeToArchetypeData,
|
|
2851
3341
|
composeArchetypes,
|
|
2852
3342
|
composeSections,
|
|
2853
3343
|
deriveZones,
|
|
2854
3344
|
deriveTransitions,
|
|
2855
3345
|
generateTopologySection,
|
|
3346
|
+
mapRegistryThemeToThemeData,
|
|
2856
3347
|
scaffoldProject,
|
|
2857
3348
|
scaffoldMinimal,
|
|
3349
|
+
writeExecutionPackBundleArtifacts,
|
|
2858
3350
|
refreshDerivedFiles,
|
|
3351
|
+
mapRegistryPatternToPatternSpecSummary,
|
|
2859
3352
|
RegistryClient,
|
|
2860
3353
|
syncRegistry
|
|
2861
3354
|
};
|